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.

BehaviorWhat appears in replay
MaskAsterisks replace text; element structure is intact
BlockBlank placeholder box; no content captured, position preserved
UnmaskReal text, always — bypasses mask_all_text for that element

What's masked by default

SurfaceDefault behaviorConfigurable?
<input> / <textarea> / <select> valuesAlways maskedNot currently
Page text (paragraphs, headings, links, buttons, labels)Captured as-is unless you mask itYes — mask_all_text and selector / class options below
<script> textNot recorded verbatimNo
<style> / <noscript> textCaptured as-isNo — avoid sensitive values there
<canvas> contentNot capturedNo (canvas capture is not currently supported)
Element attributes (aria-label, title, alt, data-*, placeholder)Captured as-isNo — keep sensitive values out of attributes or use block_selector
URLs, query strings, document.titleCaptured as-isNo — 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 with block_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 *.

OriginalMasked
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>
ClassBehavior
op-session-replay-blockElement 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-ignoreElement 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-maskElement'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 when mask_all_text: true is 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',
  },
});
OptionEquivalent classNotes
block_selectorop-session-replay-blockAny standard CSS selector — comma-separated lists are supported
ignore_selectorop-session-replay-ignoreUnmasks 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_selectorop-session-replay-maskHas 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 exposuremask_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 mostDefault masking + op-session-replay-mask / mask_text_selector on the affected pages
Run a heavily third-party-script-driven pageDefault masking + block_selector for every embed you don't control
Need to verify what's capturedRun a session, open it in the dashboard, and review before going live with new pages

Next steps

How is this guide?

On this page