> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mixpanel.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Session Replay Privacy Controls

**Last updated January 21st, 2026**

## Introduction to Session Replay

Mixpanel offers a privacy-first approach to Session Replay, including features such as data masking. Mixpanel's Session Replay privacy controls were designed to assist customers in protecting end user privacy.

Data privacy regulations are rapidly evolving and vary considerably across states and countries. A consistent requirement across many data privacy regulations for website operators is disclosing to end users that their personal information is being collected, often in a privacy notice. Before implementing Session Replay on your website, a best practice is to review your privacy notice with legal counsel to ensure it remains accurate and compliant with data privacy laws.

## How does Session Replay work?

Session Replay for Web captures the Document Object Model (DOM) structure and changes to it. Mixpanel then reconstructs the web page, applying recorded events at the time an end user completed them. Within Mixpanel’s platform, you can view a reconstruction of your end user’s screen as they navigate your website. However, Session Replay for Web is not a video recording of your end user’s screen and end user actions are not literally video-recorded.

## How does masking and blocking work? What are the high-level technical details?

Masking and blocking are slightly different.

Masked data is suppressed client-side, meaning it is not collected in its original form by Mixpanel’s SDK, and the data is not stored on Mixpanel servers. Masked elements have their text replaced with asterisks of the same length \[\*\*\*\*].

Blocked data is similarly suppressed client-side, meaning it is not collected in its original form by Mixpanel’s SDK, and the data is not stored on Mixpanel servers. However, blocked elements will be rendered with a placeholder element (e.g., an empty box of similar size).

Note: interactions (such as mouse-clicks) with blocked and masked elements are still captured by Session Replay for Web.

<div>
  <Frame>
    <img src="https://mintcdn.com/mixpanel-edb78807/getS8ds-Vy3HR5EI/images/01-masking-vs-blocking.svg?fit=max&auto=format&n=getS8ds-Vy3HR5EI&q=85&s=bc5673887a99f9e4eb4394b95c014590" alt="Masking vs Blocking" style={{width: '100%'}} width="750" height="980" data-path="images/01-masking-vs-blocking.svg" />
  </Frame>
</div>

## Configuring Privacy Controls

By default, Mixpanel masks and/or blocks the most common elements that contain content like input text, non-input text, images, and videos. However, Mixpanel also offers its customers a range of privacy controls to choose to unmask / unblock elements as needed, which are detailed further on this page.

| Element Type      | Default State                                                                                                                                                                                                                         | Customizable                                                                                                                                                                                       |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Inputs            | Mixpanel masks all user input text by default. When a user enters text into an input field, Mixpanel captures \[\*\*\*\*] in place of text.                                                                                           | Yes. You can selectively unmask specific inputs using `record_unmask_input_selector`. However, certain [sensitive input types](#always-masked-inputs) cannot be unmasked for security reasons.     |
| Text              | By default, Mixpanel masks all non-input text on your webpage. This masked content on your webpage is replaced with \[\*\*\*\*].                                                                                                      | Yes. You can selectively unmask text elements using `record_unmask_text_selector`, or set `record_mask_all_text: false` to unmask all text by default and then selectively mask specific elements. |
| Videos and Images | By default, Mixpanel blocks videos and images. These elements will be rendered with a placeholder element (i.e., an empty box of similar size). Note: interactions with blocked elements will still be captured (e.g., mouse-clicks). | Yes. Mixpanel empowers its customers to decide to record images and videos as-is.                                                                                                                  |

Other elements not listed in this table are captured by default, and can be blocked at your discretion. You can specify a CSS selector under the config option `record_block_selector` to block all elements which match the selector.

### Text Masking vs Input Masking

Text masking and input masking are two separate pipelines that are configured independently. Text masking handles visible text inside any HTML element (`<p>`, `<h1>`, `<span>`, `<label>`, `<button>`, etc.), while input masking handles values typed into `<input>`, `<textarea>`, and `<select>` elements.

This means you can configure them independently. For example, you could unmask form labels while keeping input values masked — the label "Full Name" would be visible but the typed value "John Smith" would show as `****`.

<div>
  <Frame>
    <img src="https://mintcdn.com/mixpanel-edb78807/getS8ds-Vy3HR5EI/images/02-text-vs-input-masking.svg?fit=max&auto=format&n=getS8ds-Vy3HR5EI&q=85&s=05dbd6cfda1f1ce560f68988ce17f994" alt="Text vs Input Masking" style={{width: '100%'}} width="750" height="680" data-path="images/02-text-vs-input-masking.svg" />
  </Frame>
</div>

## Text Masking Configuration

Mixpanel provides flexible options for controlling which text elements are masked in your replays.

### Configuration Options

| Option                        | Type                 | Default             | Description                                                                                                            |
| ----------------------------- | -------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `record_mask_all_text`        | `boolean`            | `true`              | When `true`, all text is masked by default. Use `record_unmask_text_selector` to selectively reveal specific elements. |
| `record_mask_text_selector`   | `string \| string[]` | `undefined`         | CSS selector(s) for elements to mask. Only applies when `record_mask_all_text` is `false`.                             |
| `record_unmask_text_selector` | `string \| string[]` | `undefined`         | CSS selector(s) for elements to unmask. Only applies when `record_mask_all_text` is `true`.                            |
| `record_mask_text_class`      | `string \| RegExp`   | Common mask classes | CSS class name or regex for elements to mask. Included for backward compatibility.                                     |

<Note>
  Selector options accept either a single CSS selector string or an array of selectors. Arrays are joined with commas internally, so `['.header', '.footer']` is equivalent to `'.header, .footer'`.
</Note>

### Mask All Text (Default Behavior)

By default, `record_mask_all_text` is `true`, meaning all text on the page is masked. To selectively unmask specific elements, use `record_unmask_text_selector`:

```javascript theme={"system"}
// All text masked by default, except navigation and footer
mixpanel.init('YOUR_PROJECT_TOKEN', {
    record_sessions_percent: 100,
    record_unmask_text_selector: ['.navigation', '.footer', '#public-content']
});
```

### Unmask All Text

To show all text by default and selectively mask sensitive areas, set `record_mask_all_text` to `false` and use `record_mask_text_selector`:

```javascript theme={"system"}
// All text visible by default, mask only sensitive areas
mixpanel.init('YOUR_PROJECT_TOKEN', {
    record_sessions_percent: 100,
    record_mask_all_text: false,
    record_mask_text_selector: ['.user-data', '.account-info', '#billing-section']
});
```

### Using CSS Classes

You can also mask elements using the `.mp-mask` CSS class directly in your HTML:

```html theme={"system"}
<div class="mp-mask">This text will be masked</div>
<p>This text follows regular masking rules</p>
```

## Input Masking Configuration

Input masking follows a similar pattern to text masking, with additional security protections for sensitive input types.

### Configuration Options

| Option                         | Type                 | Default     | Description                                                                                                              |
| ------------------------------ | -------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------ |
| `record_mask_all_inputs`       | `boolean`            | `true`      | When `true`, all inputs are masked by default. Use `record_unmask_input_selector` to selectively reveal specific inputs. |
| `record_mask_input_selector`   | `string \| string[]` | `undefined` | CSS selector(s) for inputs to mask. Only applies when `record_mask_all_inputs` is `false`.                               |
| `record_unmask_input_selector` | `string \| string[]` | `undefined` | CSS selector(s) for inputs to unmask. Only applies when `record_mask_all_inputs` is `true`.                              |

### Selectively Unmask Inputs

To unmask specific input fields while keeping others masked:

```javascript theme={"system"}
// All inputs masked by default, except search and quantity fields
mixpanel.init('YOUR_PROJECT_TOKEN', {
    record_sessions_percent: 100,
    record_unmask_input_selector: ['#search-box', '.quantity-input', '[data-public="true"]']
});
```

### Always Masked Inputs

<Warning>
  Certain input types are **always masked** regardless of your configuration settings. This is a security measure that cannot be overridden.
</Warning>

The following inputs will always be masked:

| Input Type                                     | Reason                                                                                                                                                                                |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type="password"`                              | Contains authentication credentials                                                                                                                                                   |
| `type="email"`                                 | Contains personally identifiable information                                                                                                                                          |
| `type="tel"`                                   | Contains phone numbers                                                                                                                                                                |
| `type="hidden"`                                | May contain sensitive tokens or data                                                                                                                                                  |
| Inputs with non-empty `autocomplete` attribute | The [autocomplete attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete) is primarily used for PII fields (name, address, credit card, etc.) |
| Inputs with `data-rr-is-password` attribute    | Used internally when password inputs are transformed (e.g., "Show Password" functionality)                                                                                            |

Additionally, Mixpanel applies heuristic detection to identify inputs that may contain sensitive data based on their `name` or `id` attributes. Inputs matching patterns associated with credit cards, passwords, social security numbers, and similar sensitive data will be masked.

## Blocking Elements

To block elements entirely (replacing them with placeholder boxes), use the blocking configuration:

```javascript theme={"system"}
mixpanel.init('YOUR_PROJECT_TOKEN', {
    record_sessions_percent: 100,
    record_block_selector: '.sensitive-data'
});
```

```html theme={"system"}
<img src="https://example.com/photo.jpg" class="mp-block"/>
```

## Configuring Privacy Options via GTM

If you're using the Mixpanel GTM template, privacy options like `record_mask_all_text` and `record_block_selector` should be set using a **Custom JavaScript variable** rather than the template's "Set options manually" interface.

The manual interface can pass empty strings or whitespace for selector fields, which are not valid CSS selectors and will cause a runtime error (`Failed to execute 'matches' on 'Element'`). Using a Custom JavaScript variable gives you full control over the config object passed to the SDK.

### Setup

In GTM, create a new **Variable → Custom JavaScript** and return your privacy configuration alongside your other init options:

```javascript theme={"system"}
function() {
  return {
    record_sessions_percent: 100,
    record_mask_all_text: false,       // show all text by default
    record_block_selector: undefined   // uses default: 'img, video, audio'
  };
}
```

Then set **Initialization Options** in your Mixpanel init tag to use this variable.

### Common GTM privacy configurations

**Show all text, block nothing extra (testing/low-sensitivity sites)**

```javascript theme={"system"}
function() {
  return {
    record_sessions_percent: 100,
    record_mask_all_text: false,
    record_block_selector: undefined
  };
}
```

**Mask everything except specific elements (default-private approach)**

```javascript theme={"system"}
function() {
  return {
    record_sessions_percent: 100,
    record_mask_all_text: true,
    record_unmask_text_selector: '.public-content, nav, footer',
    record_block_selector: 'img, video, audio, .sensitive-widget'
  };
}
```

**Show all text, selectively mask sensitive areas**

```javascript theme={"system"}
function() {
  return {
    record_sessions_percent: 100,
    record_mask_all_text: false,
    record_mask_text_selector: '.user-data, .account-info, #billing-section',
    record_block_selector: 'img, video, audio'
  };
}
```

> **Important:** Do not pass `""` or `[]` for selector options — both resolve to an empty string, which is an invalid CSS selector. Pass `undefined` to use the SDK default, or omit the key entirely.

For the full list of available privacy options, see the sections above.

## How Nesting and Inheritance Works

Both masking and unmasking selectors propagate through the DOM tree. When checking if an element should be masked, the SDK checks the **element itself and all of its ancestors**.

**Masking propagates to children:** If a parent element matches a masking selector, all descendants are also masked — even deeply nested ones.

**Unmasking overrides ancestor masking:** When `record_mask_all_text` is `true`, adding `record_unmask_text_selector` on a container will unmask that container and all its children, even though the rest of the page remains masked.

<Warning>
  **Blocking does NOT support unblocking children.** When an element is blocked, the entire subtree is removed during DOM serialization. The recording engine never traverses inside a blocked element, so children cannot opt out. There is no unblock selector or class.
</Warning>

<div>
  <Frame>
    <img src="https://mintcdn.com/mixpanel-edb78807/getS8ds-Vy3HR5EI/images/03-nesting-inheritance.svg?fit=max&auto=format&n=getS8ds-Vy3HR5EI&q=85&s=363bd2390a1c4ace907e1fb239aac547" alt="Nesting and Inheritance" style={{width: '100%'}} width="750" height="1340" data-path="images/03-nesting-inheritance.svg" />
  </Frame>
</div>

## Quick Reference: Which Config Do I Use?

<div>
  <Frame>
    <img src="https://mintcdn.com/mixpanel-edb78807/getS8ds-Vy3HR5EI/images/04-configuration-recipes.svg?fit=max&auto=format&n=getS8ds-Vy3HR5EI&q=85&s=24eab7a599b931d494f1f7989fb23ed4" alt="Configuration Recipes" style={{width: '100%'}} width="750" height="540" data-path="images/04-configuration-recipes.svg" />
  </Frame>
</div>

## Disabling Replay Collection

Once enabled, Session Replay runs on your website/app until either:

* The user leaves your website/app
* The user is inactive for more than 30 minutes
* You call manually stop a session recording using an SDK method.

Call `mixpanel.stop_session_recording()` before a user navigates to a restricted area of your site to disable replay collection while the user is in that area. To restart replay collection, call `mixpanel.start_session_recording()` to re-add the plugin.

## Additional Considerations

WebComponents that utilize HTML attributes may be ingested and stored by Session Replay for Web, regardless of whether they are displayed in an individual recording as text. Customers should utilize the block functionality outlined above to the extent specific areas of a webpage should not be ingested.

## User Opt-Out

Session Replay follows [standard SDK opt-out setting](/docs/privacy/end-user-data-management#opt-out-users). If a user is opted out of regular SDK tracking, they will also be opted out of Session Replay recording.

## Data Deletion

Deletion requests for Session Replay for Web use Mixpanel's standard end user management process for events documented [here](/docs/privacy/end-user-data-management).

## Data Retention

Mixpanel retains Session Replays for 30 days from the date the replay is ingested and becomes available for viewing within Mixpanel.
