Privacy and Masking
Configure privacy controls for Session Replay - mask text, block elements, and use selector overrides for granular control.
Privacy and Masking
Session Replay ships with safe defaults: every form input is masked, <script> contents are not recorded verbatim, and <canvas> content is not captured. <style> and <noscript> are captured as-is — avoid putting sensitive values there. From there, you have three layers of control.
| Behavior | What appears in replay |
|---|---|
| Mask | Asterisks replace text; element structure is intact |
| Block | Blank placeholder box; no content captured, position preserved |
| Unmask | Real text, always — bypasses mask_all_text for that element |
What's masked by default
| Surface | Default behavior | Configurable? |
|---|---|---|
<input> / <textarea> / <select> values | Always masked | Not currently |
| Page text (paragraphs, headings, links, buttons, labels) | Captured as-is unless you mask it | Yes — mask_all_text and selector / class options below |
<script> text | Not recorded verbatim | No |
<style> / <noscript> text | Captured as-is | No — avoid sensitive values there |
<canvas> content | Not captured | No (canvas capture is not currently supported) |
Element attributes (aria-label, title, alt, data-*, placeholder) | Captured as-is | No — keep sensitive values out of attributes or use block_selector |
URLs, query strings, document.title | Captured as-is | No — keep sensitive values out of URLs/titles or scope replay to safe pages |
Important: Element attributes and URLs are not masked. If you put PHI/PII in
aria-label,data-*,title, or in a query string (?email=...), it will be recorded. The usual fixes are: move sensitive values out of those surfaces, or block the affected element entirely withblock_selector.
Layer 1 — Global text masking
Set mask_all_text: true to mask every visible text node on the page.
ours('init', '{cdp_token}', {
session_replay: {
token: 'replay_token',
mask_all_text: true,
},
});When enabled, replay still records the page's structure (layout, scroll position, mouse trails, click locations) but all text is masked. If mask_all_text is off, use op-session-replay-mask or mask_text_selector to target only the sensitive regions.
What the masked text looks like
Text masking preserves whitespace and punctuation and replaces letters and numbers with *.
| Original | Masked |
|---|---|
Hello World | ***** ***** |
Order #1234! | ***** #****! |
you@example.com | ***@*******.*** |
日本語テキスト | ******* |
Whitespace and punctuation are preserved so the page layout stays stable in the replay.
Layer 2 — CSS classes
Add these classes to specific elements throughout your application. They work with or without mask_all_text.
<!-- Replace element with a placeholder box -->
<div class="op-session-replay-block">Sensitive content</div>
<!-- Bypass masking — text captured unredacted -->
<div class="op-session-replay-ignore">Non-essential content</div>
<!-- Mask text content -->
<div class="op-session-replay-mask">Sensitive text</div>| Class | Behavior |
|---|---|
op-session-replay-block | Element is replaced with a placeholder box — no children, text, or inner HTML is captured, but the element's position and dimensions are preserved in replay |
op-session-replay-ignore | Element is captured with full structure and text — masking is bypassed. Text appears unredacted even when mask_all_text: true is active. This does not unmask form control values; <input>, <textarea>, and <select> values remain masked |
op-session-replay-mask | Element's text is masked; structure is captured |
When to use which
- Block PII/PHI containers, payment forms, password fields you can't otherwise reach, and any third-party iframe whose content you don't control
- Unmask (
op-session-replay-ignore) elements that must always show their real content regardless of global masking — for example, non-sensitive UI labels or display names you need legible in the replay even whenmask_all_text: trueis on - Mask elements that may contain personal data but whose structure matters for analysis (e.g., search results, customer names in an admin UI)
You can also enable mask_all_text: true to mask everything on the page at once, then use op-session-replay-ignore to carve out only the elements that should stay legible — often simpler than adding op-session-replay-mask to every sensitive element individually.
Layer 3 — Selector overrides
When you can't add classes (third-party widgets, headless CMS output, code you don't own), pass CSS selectors directly.
ours('init', '{cdp_token}', {
session_replay: {
token: 'replay_token',
block_selector: '.cc-card, [data-private]',
ignore_selector: '.cc-noise',
mask_text_selector: '.cc-pii',
},
});| Option | Equivalent class | Notes |
|---|---|---|
block_selector | op-session-replay-block | Any standard CSS selector — comma-separated lists are supported |
ignore_selector | op-session-replay-ignore | Unmasks matching elements — text appears unredacted even with mask_all_text: true. This does not unmask form control values; <input>, <textarea>, and <select> values remain masked |
mask_text_selector | op-session-replay-mask | Has no effect when mask_all_text: true (* already covers everything) |
You can also enable mask_all_text: true to mask everything globally and use ignore_selector to selectively reveal only the elements that need to stay visible — useful when you can't enumerate every sensitive selector upfront.
Picking a strategy
| If you... | Start with |
|---|---|
| Have HIPAA / PHI exposure | mask_all_text: true + block_selector for any container that holds PHI you must redact entirely (forms, image-based PHI, third-party widgets) |
| Have PII in some pages but not most | Default masking + op-session-replay-mask / mask_text_selector on the affected pages |
| Run a heavily third-party-script-driven page | Default masking + block_selector for every embed you don't control |
| Need to verify what's captured | Run a session, open it in the dashboard, and review before going live with new pages |
Next steps
- Configuration — full options reference
- FAQs
How is this guide?