Session Recording Privacy

Session Replay lets you watch how a user experienced your site. To protect users, Middleware provides client‑side masking and exclusion so sensitive data never leaves the browser. Use the controls below to tune privacy for your product and region.

What gets recorded (and what doesn’t)#

  • Recorded: DOM structure & attributes, user interactions (clicks, inputs), viewport/scroll, and visual changes needed to reconstruct the session.
  • Not recorded by default: Sensitive input values (e.g., password fields) are masked. You can further mask text or exclude DOM regions entirely.
  • Session boundaries: A session ends after 15 minutes of inactivity or 4 hours of continuous activity (whichever comes first). Recording is tied to the RUM session.

Treat masking/exclusion as defence in depth: combine selective blocking on sensitive regions with targeted text masking for the rest.

Quick Start (recommended defaults)#

Add a minimal privacy configuration at initialisation so you start from a safe baseline, then tighten over time.

1<script>
2  if (window.Middleware) {
3    Middleware.track({
4      // ...your existing config...
5      recording: "1",
6      recordingOptions: {
7        maskAllInputs: true,                  // mask all input values as ***
8        maskTextSelector: ".pii, [data-pii]", // mask text inside these elements
9        blockSelector: ".auth-widget, .payment-form" // don't record these regions
10      }
11    });
12  }
13</script>

Configuration Reference (browser)#

OptionTypeDefaultPurpose
maskAllInputsbooleanfalseMasks the values of input, textarea, and select fields in the recording. Structure remains for usability.
maskTextSelectorstring (CSS selector)—Masks innerText of matching elements (e.g., .pii, [data-pii], [data-secret]). Use for names, emails, IDs that appear as text.
blockSelectorstring (CSS selector)—Excludes matching DOM regions from recording (no pixels, no text, no attributes). Use on auth/payment widgets, tokens, secure modals.
ignoreSelectorstring (CSS selector)—Prevents interaction events from being recorded on matching nodes (structure may remain). Useful for low‑value/high‑volume elements.
blockClassstring (CSS class)—Convenience: any element with this class is treated like blockSelector. Use when you can’t easily inject selectors.

Selectors can be combined (comma‑separated). Favour specific rules (e.g., .checkout [data-pii]) over global wildcards to preserve useful context.

Selector Patterns#

ScenarioExample
Mask all user‑provided text in a profile form.profile-form [data-pii], .profile-form .pii
Exclude 3rd‑party auth & billing widgets.auth-widget, .payment-form, #card-hosted-iframe
Ignore noisy UI (ad placeholders, carousels).ad, .carousel, .video-autoplay
Mask by attribute flags from your app[data-secret], [data-sensitive], [data-pii]
xclude cookie banners & A/B overlays.consent-modal, .ab-test-overlay

Example: masking text with maskTextSelector#

maskTextSelector takes a CSS selector and replaces the rendered text of every matching element — and of its descendants — with asterisks in the recording. The element itself is still captured, so layout, structure and interactions are preserved and the replay looks like the real page.

1. Mark the elements that render PII. Use a data attribute, a class, or both:

1<div class="account-summary">
2  <h2>Account</h2>
3  <p>Name: <span data-pii>Ada Lovelace</span></p>
4  <p>Email: <span data-pii>ada@example.com</span></p>
5  <p>Phone: <span class="pii">+1 415 555 0142</span></p>
6  <p>Plan: <span>Enterprise</span></p>
7</div>

2. Point maskTextSelector at them at initialisation:

1<script>
2  if (window.Middleware) {
3    Middleware.track({
4      // ...your existing config...
5      recording: "1",
6      recordingOptions: {
7        maskTextSelector: "[data-pii], .pii"
8      }
9    });
10  }
11</script>

3. Check the result in the replay. Matched text is masked; everything else stays readable:

ElementLive pageIn the replay
<span data-pii>Ada Lovelace</span>Ada Lovelace*** ********
<span data-pii>ada@example.com</span>ada@example.com***************
<span class="pii">+1 415 555 0142</span>+1 415 555 0142** *** *** ****
<span>Enterprise</span>EnterpriseEnterprise

Each non‑whitespace character is replaced with *; whitespace is preserved, so word shapes and layout stay intact.

Because descendants are masked too, you can mask a whole region with a single selector on its container:

1// Masks every text node inside the invoice block, however deeply nested
2maskTextSelector: ".invoice-details"

A few things to keep in mind:

  • Scope selectors where you can. .checkout [data-pii] keeps the rest of the app readable, whereas a bare [data-pii] applies everywhere it appears.
  • Apply the class or attribute in your markup before the element renders — in dynamic UIs, adding it after the user types leaves the earlier frames unmasked.
  • maskTextSelector covers rendered text only. Use maskAllInputs: true for values the user types, and blockSelector when a region should not be captured at all.
  • If you'd rather not configure a selector at all, the SDK ships a built‑in masking class: add mw-mask to any element and its text is masked with no recordingOptions change (mw-block and mw-ignore work the same way for blockClass and ignoreClass).
  • For the full option matrix with defaults, including maskTextClass, maskSensitiveInputs, and maskTextFn, see Data Security.

If your site requires user consent for analytics or recording, gate initialisation, or set recording: "0" until consent is granted.

1<script>
2  function hasAnalyticsConsent() {
3    return document.cookie.includes("consent_analytics=true");
4  }
5  if (hasAnalyticsConsent() && window.Middleware) {
6    Middleware.track({
7      // ...config...
8      recording: "1",
9      recordingOptions: { maskAllInputs: true, maskTextSelector: ".pii, [data-pii]" }
10    });
11  }
12</script>

In stricter regimes, separate analytics vs replay consent. Keep RUM on, but start with recording: "0" until replay consent is granted.

Testing Your Privacy Rules (dev checklist)#

  1. Mark a few obvious PII spots (name, email, phone) with [data-pii].
  2. Reload and perform a short session that touches each spot.
  3. Review the replay: verify that text values are masked and blocked areas are absent.
  4. Iterate: narrow/expand selectors as needed, avoid over‑blocking critical UI.
  5. Automate: add a smoke test step in staging to exercise sensitive flows before releases.

Example Playbooks#

Login & account pages#

  • blockSelector: ".auth-widget, .magic-link, .otp-modal"
  • maskTextSelector: ".profile .pii, .profile [data-pii]"
  • maskAllInputs: true

Checkout & Payments#

  • blockSelector: ".payment-form, #card-hosted-iframe, .bank-redirect"
  • maskTextSelector: ".billing [data-pii], .shipping [data-pii]"
  • Keep order IDs visible if they’re non‑PII and useful for support.

Support & admin tools#

Performance & UX notes#

  • Cost: Masking is lightweight; blocking prevents capture entirely and can modestly reduce payload size.
  • Specificity > breadth: A few targeted selectors often beat a single global * rule that hides useful context.
  • Accessibility: Masking does not affect your live DOM; it only changes what’s captured.

Troubleshooting#

  • I still see sensitive text:
    • Check that the text node is actually inside a matching maskTextSelector element.
    • In dynamic UIs, ensure the attribute/class is applied before user input.
  • A widget still appears in replay:
    • Use blockSelector (or blockClass) on the widget container instead of only masking inputs.
  • Too much is hidden:
    • Narrow selectors (e.g., scope to .checkout [data-pii] rather than [data-pii] globally).
  • Consent logic seems ignored:
    • Verify you’re calling Middleware.track() only after your consent check passes, and that recording is set accordingly.

Need assistance or want to learn more about Middleware? Get in touch with us via our Contact Us or join our Slack channel.