Privacy & Compliance 2026-08-10

A PII Masking Checklist for Session Replay

The first time I audited my own session replay setup, I found a user’s full name and email rendered in the account header of every single recording, a password reset token sitting in a captured URL, and a support widget cheerfully displaying the user’s message history. I had configured input masking and considered the job done. It was maybe a third done.

That audit is why this checklist exists. If you want to properly mask PII in session replay, you have to work through every channel the recorder captures — and inputs, the only channel most people configure, are just the first of six.

One framing note before the list: mask by default and unmask deliberately. Every replay tool I’ve used lets you invert the logic — record everything, block specific fields — and that mode fails unsafe. A new form field added eight months from now by a teammate who’s never read your masking config will leak. Default-mask fails safe: the new field shows up as asterisks until someone consciously decides otherwise.

How to mask PII in session replay, field by field

1. Input fields

The table stakes, but with edges people miss:

2. Text nodes — the channel everyone forgets

Inputs are what users type. Text nodes are what your app renders: the account page showing name and address, the invoice table, the admin panel listing customer emails, the ARIA live region that announces “Verification code sent to +1 555…”.

You have two workable strategies:

Most tools in the rrweb lineage support a masking class convention. In LogReplay, for instance, the recorder config takes the strict route in a few lines:

LogReplay.init('YOUR_PROJECT_ID', {
  privacySetting: 'strict',        // mask all text and inputs by default
  maskTextSelector: '*',
  unmaskTextSelector: '[data-lr-unmask]',  // opt chrome back in explicitly
});

Then sprinkle data-lr-unmask on your nav and static UI labels. Ten minutes of work, and new features are private by default.

3. URLs and query parameters

Recorders capture the page URL on every navigation, and URLs leak more than people think: password-reset and magic-link tokens, email addresses in query params (?email=… from your own marketing links), OAuth code and state values mid-flow, and path segments like /patients/jane-doe.

Two layers of defense, and you want both:

4. Network request and response bodies

If your replay tool captures network activity alongside the DOM — the engineering-focused ones do, and it’s half their value — then every API payload is a second copy of your data flowing into replay storage. Your masked login form is nice; the POST /api/login body with the plaintext password sitting in the network tab is a hole you could drive a truck through.

5. Custom data attributes and hidden DOM state

The DOM your users see is a subset of the DOM the recorder captures. Things I’ve found in my own markup during audits: data-user-email attributes left over from an analytics experiment, JSON blobs of user objects in <script type="application/json"> tags for hydration, and entire hidden admin panels rendered with display:none instead of not rendered at all.

display:none is invisible to humans and fully present in mutation records. If it’s in the DOM, it’s in the replay. Grep your codebase for data- attributes carrying user values, and prefer not-rendering over hiding for anything sensitive.

6. Third-party widgets

Support chat (Intercom, Zendesk), embedded checkout, analytics survey popups, social embeds. Two cases with opposite problems:

You don’t control when these vendors change their DOM structure, so block by the stable container ID, not by internal classes.

The verification procedure

Config is a hypothesis. The replay is the test. Run this before you trust anything, and re-run it quarterly and after big frontend changes:

  1. On production (or a prod-identical staging build — with masking, the differences are exactly where the bugs live), open a fresh browser profile with recording active.
  2. Walk every sensitive flow as a real user: sign up, log in, view and edit your profile, enter a test credit card, open the support widget, trigger a password reset and click the email link, visit anything admin-ish.
  3. Watch your own replay with fresh eyes, DOM-inspecting as you go. Check the URL bar at every step, the network tab for every auth and payment call, and pause on every screen that renders account data.
  4. Log every leak, fix, re-record. Repeat until a full pass is clean.
  5. Write down the date and what you checked. When a customer’s security questionnaire asks how you protect PII in analytics tooling — and under GDPR you should expect the question — “we run a recorded masking audit quarterly, here’s the log” is a real answer. It also feeds directly into the lawful-basis story you need for running session recording under GDPR at all.

Thirty minutes, once a quarter. Cheap insurance against the genuinely bad day where the leak you find is in a recording of someone else.

See the bug the way your user did

LogReplay captures session replays, console output, network requests, and errors in one timeline — so you stop guessing what happened before the ticket arrived.

Try LogReplay free