> For the complete documentation index, see [llms.txt](https://manual.bubble.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://manual.bubble.io/help-guides/data/the-database/protecting-data-with-privacy-rules.md).

# Protecting data with privacy rules

This section covers how to use privacy rules to protect private data

{% hint style="info" %}
This article takes a long-form look at what privacy rules are. For more concise, technical documentation, see our core reference entry.

**Reference:** [Privacy](/core-resources/data/privacy.md)
{% endhint %}

{% hint style="danger" %}
Privacy rules are an essential part of your app's security. Any database data that's private or sensitive needs to be protected with privacy rules to be considered secure.

Stay on top of your app's privacy rules so your users can safely entrust their data to your app.
{% endhint %}

Privacy rules are conditions you set up on each data type to protect your data from being viewed and edited by unauthorized users.

## What are privacy rules?

All the data you store in the database is hosted on a server. Privacy rules instruct that server to only send data to the browser, or write to the database, if certain conditions are met.

For example, you could allow your products to be viewable only by people who are logged in. Written out in plain terms, the rule would be:

> Only return the data if the current user is logged in

The logic here is that you *ask* the server for information using a data source like `Do a search for` and an operator like `Product's Name` to show a product's name. If the privacy rule above is active, Bubble only returns the data if the query came from a logged-in user.

This is essential to app security because it's stopped on the *server side*. The data stays encrypted in the database instead of being sent to the browser, where it could be viewed.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FcP4RL7KdI73EqLT3Puau%2Fprivacy-rules-illustration-b.jpeg?alt=media&amp;token=7a296176-f2df-4fd3-8fc4-5dc1d98f20f7" alt=""><figcaption></figcaption></figure>

As illustrated above, privacy rules make up a sort of firewall for your data. Every request to the database goes through a process of [authentication and authorization](#user-content-fn-1)[^1] before it's completed or rejected.

### Client-side data

Before exploring privacy rules further, it's important to understand that any data reaching your user's device is, by definition, no longer secure. As long as data has been downloaded to a user's device, even if it isn't displayed anywhere in your app, the person using that device can access it by looking at the data traffic being sent to and from the Bubble server.

This is a fairly technical subject. Suffice it to say that the only way data remains truly inaccessible is to make sure it isn't sent from the server in the first place, and privacy rules are how you do that.

It's also worth stressing that in most cases, you *want* data to reach the device. If it didn't, you wouldn't be able to work with data at all. The task is making sure only the intended information is sent, and no unauthorized data.

In an eCommerce store, you'd likely have data with different security requirements:

* All **products** should be viewable by anyone. If not, no one can buy anything.
* All **shopping carts** should be viewable only by the user who created them, to keep purchase history private.

Products are known as *public data*, and the shopping cart is *private data*.

<details>

<summary>What does server-side and client-side mean?</summary>

**Server-side** means something happens on Bubble's servers, before any data is sent out. The user's device never sees the work being done, only the result the server decides to send.

**Client-side** means something happens on the user's own device, in their browser or app, after data has already arrived there. Because the data is already on their machine at that point, anything done client-side can't protect it.

This is why privacy rules run server-side: it's the only place where withholding data actually keeps it out of reach.

**Article:** [Client-side and server-side](/help-guides/security/client-side-and-server-side.md)

</details>

## How privacy rules work

{% hint style="info" %}
**Bubble API:** Using the Bubble API introduces some additional settings in the privacy rule tab. You can read more about those in the articles below.

**Article:** [Data API privacy rules](/help-guides/integrations/api/the-bubble-api/the-data-api/data-api-privacy-rules.md)

**Article:** [Workflow API privacy rules](/help-guides/integrations/api/the-bubble-api/the-workflow-api/workflow-api-privacy-rules.md)
{% endhint %}

Bubble has a privacy rule editor that lets you control the privacy settings for all your data types in one central place. You'll find it by going to the *Data* tab and clicking *Privacy*.

Privacy rules protect your data types in the following ways:

* You can stop specific fields from being **viewed**
* You can stop specific fields from being used as **search constraints**
* You can stop the data type from being **found with** `Do a search for`
* You can stop users from viewing **uploaded files**
* You can stop users from making changes with **auto-binding**

{% hint style="danger" %}
**Caution:** Privacy rules don't update automatically on a page if the rules affecting a user change while that user is on the page.

For example, if a user has a page open where they can see a thing, then clicks a button that changes which privacy rules apply to them so they can no longer see it, that new outcome won't be reflected on the page until it's refreshed.
{% endhint %}

{% hint style="info" %}
Managing the security of **private files** requires configuring settings in both the privacy rules and the element responsible for uploading the file.

**Article section:** [Files](/help-guides/data/files.md) | [Uploading private files](/help-guides/data/files.md#uploading-private-files)
{% endhint %}

Privacy rules are built from two pieces of information:

* The attributes of the database thing
* The attributes of the current user

By combining these two, you can flexibly set up rules that determine **who** the user is and **what** they're trying to access. Here's another rule written out as a sentence:

> Only return the data if the thing's Creator is the current user

This involves both the **thing** in question and the **current user**. If the current user and the creator of the thing are the same, Bubble returns the data. If they aren't, the data stays securely on the server.

{% hint style="info" %}
**Client-side filtering:** Privacy rules are applied on the **server**. Once data reaches the client, those rules no longer apply, since the data is already on the user's device. Client-side operations like filtering, sorting, or counting work on data that's already been returned, and don't trigger additional privacy checks.

**Article:** [Client-side and server-side](/help-guides/security/client-side-and-server-side.md)
{% endhint %}

## The privacy rule editor

The *Privacy* section is split into two panels. On the left is a list of every data type in your app, each showing its current status, either *Privacy rules applied* or *Publicly visible*. If you have a lot of data types, you can filter the list with the search box above it.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FmTJ9klT4FKHoqAQW8Eig%2Fprivacy-rules-editor.png?alt=media&amp;token=4f47b6d9-9cc2-49b6-ad20-ab1e6eb2c68d" alt=""><figcaption><p>You'll find privacy rules in the <em>Data</em> tab and <em>Privacy</em> section.</p></figcaption></figure>

Select a data type to see its rules on the right. A data type can have several rules, and each one can be given a name that describes who it covers, such as "User's own data." Click **New rule** to add another. Each rule can be collapsed while you work, and deleted or commented on using the icons in its header. If a data type has many rules or fields, the *Search rules and fields* box helps you find the one you're after.

### Creating a new privacy rule

The genereal steps to create a new privacy rules are as follows. We'll cover more details and examples further down in the article:

{% stepper %}
{% step %}

#### Open the Privacy section

Go to the *Data* tab and click *Privacy*.
{% endstep %}

{% step %}

#### Select a data type

In the left panel, select the data type you want to protect. Its existing rules appear on the right.
{% endstep %}

{% step %}

#### Create a new rule

Click **New rule** and give it a name that describes who it covers, like "User's own data."
{% endstep %}

{% step %}

#### Set the condition

In the *When* field, build a dynamic expression that defines which users the rule applies to.
{% endstep %}

{% step %}

#### Set the record-level permissions

Choose whether users matching the rule can *find this in searches* and *view files attached to this*.
{% endstep %}

{% step %}

#### Set the field permissions

For each field, choose whether it can be viewed, used as a search constraint, and saved with auto-bind.
{% endstep %}
{% endstepper %}

### Privacy rule settings

Each privacy rule is made up of several settings, and the easiest way to understand them is to read them from left to right, like an English sentence.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FIINAW1iFBlTCDcROoz8J%2Fprivacy-rule-settings.png?alt=media&amp;token=59bebb9a-7e67-4a31-8b28-c85b606271b4" alt=""><figcaption></figcaption></figure>

The sentence above says:

1. *When the current user is logged in, they can...*
2. *...find this thing in searches,*
3. *view files attached to this thing,*
4. *view the following fields,*
5. *constrain searches by the following fields,*
6. *use auto-bind on the following fields.*

The settings and their labels are meant to be taken literally. *View all fields* means **view** all fields. It doesn't stop someone from making *changes* to the field in a workflow. Likewise, *Find this in searches* applies to `Do a search for` specifically, but the record can still be fetched in other ways.

We recommend playing around with the settings and viewing the results on a page, to learn how they affect what users can see and do.

#### Working with the field list

Each field on the data type gets its own row, with a checkbox for *View*, *Constraint*, and *Auto-bind*. Each row also shows the field's type, such as text, image, date, or a data type like *Post*, which helps when you're deciding how sensitive a field is.

A few things make the list easier to work with on data types that have many fields:

* Each column header has its own checkbox, so you can grant or remove a permission across every field at once rather than one at a time.
* The header also shows how many fields currently have that permission enabled, giving you a quick sense of how open the data type is.
* You can sort the list by field name, or search within it using the *Search rules and fields* box.

Some fields show a dash instead of a checkbox in the *Auto-bind* column. These are fields that can't be auto-bound at all, such as built-in fields like *Created Date* and *Modified Date*.

{% hint style="warning" icon="lock" %}
**A note on the slug field:** Slug fields are designed to be public and searchable, so the *Constraint* permission can't be disabled on them. Slugs should never contain sensitive information.
{% endhint %}

#### Multi-level data references and searches

There's an important limitation to be aware of when conditions reference related data [more than one level deep](#user-content-fn-2)[^2].

Privacy rules can evaluate fields that belong directly to the data type, but they can't traverse multiple layers of relationships to grant [search access](#user-content-fn-3)[^3]. If a rule uses a chained reference, such as accessing a field on a related thing and then another field beyond that, Bubble can't use that rule to grant search access.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FHUBLHc0OuFD7pSKRotG2%2Fcreator-is-admin.png?alt=media&amp;token=fb15570b-0cb7-489c-953d-149a587e7b10" alt=""><figcaption></figcaption></figure>

To avoid this limitation, structure your data so the fields required for privacy checks only need to reach one level in their expression.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FjYNqtuA6AdDBYonBRfh4%2Fuser-is-admin-workaround.png?alt=media&amp;token=5ae7afce-572b-4812-8e93-88aca2cb8493" alt=""><figcaption></figcaption></figure>

In the example above, instead of checking the cart's creator's X, we check the current user, reducing the levels from two to one.

### Privacy rules and workflows

Do privacy rules affect your app's workflows? The answer is *yes and no*.

* *View all fields* doesn't stop you from using *Make changes to a thing* to update that field in a workflow. It *can*, however, stop you from correctly checking a condition, if the current user is unable to view the field the condition is based on.
* *Find this in searches* doesn't stop you from making changes to a thing, but it can stop you from getting the search results you want in a workflow. If you use `Do a search for` in a workflow, the search only returns data the current user has access to.
* *Allow auto-binding* stops a user from making changes through auto-bound elements, but it doesn't stop them from making those same changes in a workflow.

There are two important lessons here:

* Think of a workflow as being *run by the current user*. The same restrictions apply to the workflow as anywhere else in your app.
* To stop workflows from performing tasks you don't want them to, use *conditional expressions* in the *Only when* field on the workflow or on a specific action.

#### Overriding privacy rules in a workflow

Sometimes you'll need to override privacy rules when a user takes a specific action. For example:

1. User 1 doesn't have permission to search for any other users.
2. When User 1 runs a specific action, you need to search all other users to make a change.

In other words, you need to override the privacy rule that stops User 1 from searching for other users. You do this with an API workflow, checking *Ignore privacy rules when running the workflow*.

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FYeTNL4yOrLCK6vpw0G0E%2Fapi-workflow-override.png?alt=media&amp;token=032e1fce-2e13-4500-8071-961fbc9cd745" alt=""><figcaption></figcaption></figure>

This lets you run workflows that perform an important task with full access to data, without compromising security by opening that access up to the current user. The operation runs purely server-side, so your users can't see it or even know it's running.

## Search privacy modes

Search privacy modes control which fields a user can *filter on* when they search.

### Why constraints need their own protection

When you set up a `Do a search for`, you can narrow the results using *constraints*. A constraint is a filter, such as `Salary > 100,000` or `Status is "approved"`.

A user can learn something about a field without ever viewing it. Imagine a *Salary* field protected by a privacy rule. The user can't see the number, but if they're allowed to search with the constraint `Salary > 100,000`, the records that come back tell them who earns more than that. The field's value never appears on screen, but it can be inferred from the records the search returns.

Search privacy modes are how you close that gap.

<details>

<summary>What does inference mean here?</summary>

Inference is working out a piece of information indirectly, from clues, rather than seeing it stated outright.

In this context, someone never sees the protected field itself. Instead, they run search after search with different constraint values and watch which records come back each time. Each search narrows things down a little further, and over enough attempts, the hidden values can be reconstructed piece by piece.

</details>

### The three modes

You set the search privacy mode in the *Settings* tab, in the *General* section. The setting applies to the application branch you're working in.

<table><thead><tr><th width="114.43359375">Mode</th><th>Explanation</th><th>Recommendation</th></tr></thead><tbody><tr><td><strong>Off</strong></td><td>Constraints aren't restricted. Any field on any data type can be used in a search constraint.</td><td>Not recommended for apps that perform searches on sensitive data.</td></tr><tr><td><strong>Automatic</strong></td><td>A user can search using constraints already defined on your app's pages and workflows. If a constraint isn't defined anywhere in your app's pages or workflows, Bubble blocks it unless you've granted permission for that field.</td><td>Balances security and convenience, and existing apps won't break. If your app searches sensitive data, we recommend strict mode.</td></tr><tr><td><strong>Strict</strong></td><td>Every constraint needs explicit permission. A field can only be used in a search constraint if its privacy rule allows it.</td><td>Recommended for apps performing searches on sensitive data. Gives you precise control.</td></tr></tbody></table>

### How automatic mode decides what to allow

Automatic mode protects your data without breaking the searches your app already relies on. To do that, it looks at where each search comes from.

When a search runs, Bubble checks whether the constraint and its operator match one defined on one of your app's pages or workflows. If it finds a match, it allows the constraint. If it doesn't, the constraint is only allowed when you've granted permission for that field.

Searches made through the Data API always need explicit permission. They run outside the context of your pages, so Bubble has no page-defined search to match them against.

### Which mode should I choose?

As long as your app handles any kind of private data, we recommend strict mode. No field can be used as a search constraint unless you've explicitly allowed it, which removes the search inference risk across every field at once.

Strict mode takes a little more setup. You'll need to allow constraints on the fields your searches depend on, and we recommend using the search tool with the *Uses constraint field* operator to find where specific fields are used as constraints. The payoff is predictable search permissions: a field is constrainable only when you say so, with no behind-the-scenes matching.

Automatic mode is a strong default while you get there. It protects undefined constraints, including Data API searches, without breaking the searches already built into your pages.

Off mode gives no constraint protection, so we only recommend it for apps that hold no private data.

### Allowing a field in constraints

This setting controls which fields a user can use as a search constraint.

* **Enabled:** the field can be used as a search constraint.
* **Disabled:** the field can't be used as a search constraint.

{% hint style="warning" %}
Allowing a constraint on a field users can't view can expose that field's values through search inference. The security dashboard flags this combination for sensitive fields, so you can confirm it's intentional.
{% endhint %}

<details>

<summary>Checklist: transitioning to strict mode</summary>

Use this checklist to find and resolve any searches that may break when strict mode is enabled.

* [ ] **Find all fields where&#x20;*****Constraint*****&#x20;is unchecked.** Go through each data type's privacy rules and note every field where *Constraint* is disabled.
* [ ] **Check each field with the app search tool.** Use the *Uses constraint field* filter to find any expressions in your app that use the field as a constraint.
* [ ] **If the search returns no results, leave the field as is.** No expressions depend on it, so the current setting is safe.
* [ ] **If the search returns results, choose one of the following:**
  * Enable *Constraint* on the relevant privacy rule, if you want to keep the field constrainable for users matching that rule.
  * Refactor the affected expression to use a different field as the constraint.
  * Refactor your data schema so a less sensitive field can be used in place of the original constraint.

</details>

## Privacy rule examples

### Example 1: Shopping cart

Say you have a custom data type called *Shopping Cart*. It should only be found in searches and viewable by the person who created it. But if no one else can see the order, how do you deliver the product?

Someone else needs to be able to see it, but that access must be restricted. You can set up a custom field on each user called *Admin* (yes/no), which grants access to see anyone's cart. On the cart itself, you can use the existing, automatically populated *Created by* field.

The user should look like this:

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FKmNcmb1GKLhdXsvM53Dd%2Fadmin-user.png?alt=media&amp;token=89317426-4e5f-4678-b350-10548c9c544e" alt="The Shopping Cart data type&#x27;s three privacy rules: one for admins, one for the cart&#x27;s creator, and an Everyone else rule that grants no access."><figcaption><p>We set up a simple yes/no field to separate admin users from regular users. Note that we have set the default value to <em>no</em> to make sure that new users are <em>not</em> an admin by default.</p></figcaption></figure>

This setup requires three privacy rules:

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FCF9LVuCok9426B5LCPp7%2Fshopping-cart-privacy-rules.png?alt=media&amp;token=2040227c-a3b8-4517-9b82-4560210a0834" alt=""><figcaption></figcaption></figure>

{% stepper %}
{% step %}

#### Grant access to admins

The first rule gives access to search for and view fields on the cart *if* the current user's *Admin* field is set to *yes*.
{% endstep %}

{% step %}

#### Grant access to admins Grant access to the cart's creator

The second rule gives access to search for and view fields on the cart *if* the current user is the one who created it.
{% endstep %}

{% step %}

#### Close off everyone else

The third is the automatically generated *Everyone else* rule, which states that everyone else should have no access.
{% endstep %}
{% endstepper %}

Pay especially close attention to the *bottom* rule. This is the one that determines what happens to everyone else. We've unchecked all its boxes here, making sure only admins and cart owners get access.

A good way to think about privacy rules is that they're additive. Every rule a user matches contributes its permissions, and those permissions stack up. The user ends up with the combined access of all matching rules, not the strictest one.

> With privacy rules, you don't *prohibit access*, you **grant it**.

Following that logic, our two top rules are *granting* access to the cart if you're the cart's creator, or if you're an admin, as specified by the *Admin* yes/no field.

### Example 2: Sharing tasks

This example looks at how properties on the data type itself can grant access under certain circumstances.

Imagine a task management app where access to tasks follows two simple rules:

1. The creator of a task has full access to their tasks.
2. The creator can invite other users into a specific task, granting them access.

You need some way to determine whether a user has been invited to a specific task. Keep in mind that you want to add that permission only to certain tasks, not all of them.

You can solve this by adding a custom field on the *Task* data type that holds a list of all users invited to it. The database structure can look like this:

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FZx1xngekYDd7RB7dINm7%2Ftask-invite-users.png?alt=media&amp;token=e9ac1313-2809-43ee-b495-e7bca6ad89bc" alt="The Task data type with a custom Invited users field of type List of Users, alongside the built-in Creator, Modified Date, Created Date, and Slug fields."><figcaption></figcaption></figure>

Whenever you want to give someone access to a task, you add them to the list using a workflow:

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FDzNRsE2YyAHFCc7o3iHH%2FCleanShot%202026-05-11%20at%2015.08.38.png?alt=media&amp;token=443b4c60-25ba-4879-9986-57ab10c60e57" alt="A workflow triggered by a button click, with a Make changes to Task action that adds the parent group&#x27;s user to the task&#x27;s Invited users list."><figcaption></figcaption></figure>

Since the *Invited users* field is set to be a **list**, Bubble lets you use the *add* function to add that single user to the list of invited users. If you wanted to add several users at once, you'd use *add list* instead.

Setting up the privacy rule again takes three rules:

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2FBu0VQQreeEU1uCRZjusN%2FCleanShot%202026-09-07%20at%2014.36.00.png?alt=media&amp;token=5878f50d-b038-4e10-822c-3629cf9a362e" alt="The three privacy rules on the Task data type, with the two access-granting rules above and the Everyone else rule, which grants nothing, at the bottom."><figcaption></figcaption></figure>

{% stepper %}
{% step %}

#### Grant access to the task's creator

The first rule states that if the current user is the [creator of the task](#user-content-fn-4)[^4], they should have access.
{% endstep %}

{% step %}

#### Grant access to invited users

The second states that if the current user is in the list of invited users, they should have access.
{% endstep %}

{% step %}

#### Close off everyone else

The third, the *Everyone else* rule, states that no one else should have access.
{% endstep %}
{% endstepper %}

In the privacy rule editor, it looks like this:

<figure><img src="https://34394582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5sbzwG7CljeZdkntrL%2Fuploads%2F45kana0VBQidG7lpsCM9%2Fshared-task-privacy-rule.png?alt=media&amp;token=5e0ca70f-8d49-4f77-a4ed-077d80813349" alt=""><figcaption></figcaption></figure>

Again, the two rules **grant** access under specific circumstances, while the bottom rule does **not** grant permission to everyone else.

## Ending notes

Privacy rules are the most important safeguard you have for protecting your users' data, so it's worth getting to know them well and making a habit of setting data up securely from day one. Privacy rules act on the server side, which keeps data from being stolen, tampered with, or accidentally leaked, since the information never leaves the server.

Bubble offers a robust set of security features for you to take advantage of. As the developer, it's up to you to use them correctly to keep your users' private data private.

## Other ways to learn

<details>

<summary>Video lessons</summary>

* [Setting up privacy rules](https://youtu.be/1-meIeBUXPY)

</details>

<details>

<summary>Books</summary>

[The Ultimate Guide to Bubble Security](https://www.amliesolutions.com/the-ultimate-guide-to-bubble-security/) by Petter Amlie

</details>

[^1]: **Authentication** is the process of determining *who* a user is.\
    \
    **Authorization** is the process of determining *what* that user has access to.

[^2]: In this context, a *level* refers to accessing a field on a related thing, and then accessing another field beyond that.

    For example, in the expression `This Post's Creator's Admin is yes`, `Creator` is the first level of reference, and `Admin` is one level deeper than `Creator`.

[^3]: The *Find this in searches* property, which applies to the `Do a search for` data source in your app.

[^4]: *Created by* is a built-in field that's automatically populated. It can't be changed.

    If you want a dynamic owner, for example to transfer ownership from one user to another, you may want to set up an additional field you can manipulate directly.
