TL;DR, Quick Answer
8 min readPII redaction in session replay happens in the browser, before the recorder serialises the DOM, and every library ships a different default. Plain rrweb masks nothing but password inputs. Sentry masks all text and blocks all media. Microsoft Clarity masks numbers and email addresses in its default Balanced mode. Masked text still leaks character length in rrweb, Sentry and FullStory, and no vendor can apply a new masking rule to recordings already on its servers.
What is PII redaction in session replay?
The point of PII redaction session replay work is that it happens in the visitor's browser, before the recorder serialises the DOM and sends anything over the network, which is why a rule you add today does nothing for a recording captured yesterday. A replay script does not film the screen. It walks the DOM, serialises every node and streams mutations, so redaction means deciding which nodes ship real values, which ship placeholder characters, and which get dropped and replayed as an empty box.
Three verbs cover the outcomes, under different names in every library. Blocking drops the element and replays a placeholder of matching dimensions. Masking keeps the element and replaces its text. Ignoring keeps the element visible but stops recording what the user typed. Choosing wrongly between masking and blocking is the most common mistake, because masked elements still carry interaction data and still carry length.
The compliance framing, including retention and consent, sits in the guide to session replay privacy masking. This page covers the mechanics underneath: exact option names, shipped defaults, and where they leak.
Which rrweb options control what gets recorded?
rrweb, the open source recorder behind a long list of commercial replay products, exposes these privacy options on rrweb.record() with these documented defaults.
| Option | Default | What it does |
|---|---|---|
blockClass | 'rr-block' | Element is not recorded and replays as a placeholder of the same dimensions |
blockSelector | null | Same as blockClass but matched with a CSS selector |
ignoreClass | 'rr-ignore' | Element renders normally but its input events are not recorded |
ignoreSelector | null | Same as ignoreClass but matched with a CSS selector |
maskTextClass | 'rr-mask' | All text in the element and its children is masked |
maskTextSelector | null | Same as maskTextClass but matched with a CSS selector |
maskAllInputs | false | Masks the content of every input as * |
maskInputOptions | { password: true } | Masks specific input types |
maskInputFn | none | Replaces the default input masking logic |
maskTextFn | none | Replaces the default text masking logic |
rrweb's guide states the shipped default in one line: input[type="password"] will be masked by default.
What the list does not contain is an unmask option. There is no unmaskTextClass and no unblockSelector in rrweb's recording options, so a mask on a container cannot be lifted from one child inside it. Vendors that offer unmasking, Sentry and FullStory among them, built that layer themselves.

What does rrweb mask by default?
Out of the box rrweb masks exactly one thing: the value of input[type="password"]. Text and every other input type are recorded verbatim, so email, name, address, card number and free text fields leave the browser with their real contents unless you configure otherwise.
Two details in rrweb's source turn that default into a trap. The first is that maskInputOptions replaces the default rather than merging with it. Pass maskInputOptions: { email: true } because you want emails covered and you have silently switched password masking off. The safe form spells out { password: true, email: true } every time.
The second is the shape of the MaskInputOptions type. It accepts color, date, datetime-local, email, month, number, range, search, tel, text, time, url, week, textarea, select and password, and it does not accept checkbox, radio or file. Setting maskAllInputs: true expands to every key in that list, so even the maximum input masking setting records which checkboxes and radio buttons a visitor selected. On a medical intake form, the checkbox is the sensitive data. FullStory handles that by excluding rather than masking: its Form Privacy feature "masks all form elements with the attributes input, textarea, select, and contenteditable, and exclude all form elements with the attributes radio and checkbox."
How do session replay vendors differ on masking defaults?
Vendors sit at opposite ends of the same toolkit, and the default decides how much a missed rule costs.
| Tool | Masked with no configuration | Opt-out mechanism |
|---|---|---|
| rrweb | input[type="password"] only | None; no unmask option exists |
| PostHog | All inputs (maskAllInputs defaults to true); text is not masked | ph-no-capture class |
| Sentry | All text (maskAllText: true), all inputs, all media (blockAllMedia: true) | .sentry-unmask, .sentry-unblock, neither applied by default |
| Microsoft Clarity | Numbers and email addresses in the default Balanced mode; inputs and drop-downs in every mode | data-clarity-unmask="true" |
| FullStory with Private by Default | Every element, captured masked | .fs-unmask |
| LogRocket | Nothing; inputSanitizer and textSanitizer both default to false | data-public |
Sentry states its position plainly: "by default, the Session Replay SDK will mask all text content with * and block all media elements." Clarity offers the opposite trade-off through three modes, where Strict masks "the entire content", Balanced masks "numbers and email addresses", and Relaxed masks nothing beyond inputs and drop-downs. Two Clarity behaviours are not configurable at all: "Content in the input boxes is masked in all modes and can't be customized" and "The drop-down menus are also masked in all modes." Clarity marks what it hid with ▫ for a digit, ▪ for a letter, and • for Strict mode.
Why does masked text still leak information?
Masked text leaks the length of what it replaced, because the most widely deployed recorders substitute one placeholder character per original character. rrweb's masking function is literally text = '*'.repeat(text.length). Sentry's default mask function is (s) => '*'.repeat(s.length). FullStory is explicit that "This placeholder text blob will retain the size, color, and character length of the original text."
FullStory's own documentation makes the risk concrete for a field like account balance: "if you were comparing the session replays of 2 different accounts, and one showed a placeholder string for account balance that was three inches long and the other had a placeholder string that was half an inch long, you now know more about these two accounts than you probably need to."
Length is not the only channel. Masked elements still record interactions, which is why FullStory tells healthcare customers to exclude instead: "Because masked elements collect interaction data, it would be possible for someone with good working knowledge of the product to understand which health issues a user was checking the boxes for."
Flowsery
Start Your 14-Day Free Trial
Real-time dashboard
Goal tracking
Cookie-free tracking
The fix for both is the same. Return a fixed-length string from a custom maskTextFn or maskInputFn instead of a per-character one, and upgrade anything inferential from mask to block. The gap between reducing identifiability and removing it is the difference between pseudonymization and anonymization.
What do masking rules miss?
Masking rules apply to DOM text nodes and input values, so anything carrying data outside those two places stays in the recording. Each vendor documents its own blind spot.
- Stylesheets. Clarity "doesn't mask content within style sheets or style tags. Hence, it is recommended not to host sensitive content within CSS."
- Inline SVG. Hotjar: "SVGs linked as the source for image elements can be suppressed, but inline SVGs cannot."
- Images on web. FullStory: "Images cannot be masked when capturing data on web sessions. If you wish to hide images from data capture, use
.fs-exclude." - URLs and network payloads. A masked page with an account number in the query string still ships it. PostHog exposes
maskCapturedNetworkRequestFn; FullStory allowlists request and response bodies. - Custom event properties. Redaction governs the replay stream, not the analytics events your own code sends.

Can masking be applied to recordings you already have?
No vendor can retroactively redact a session that has already reached its servers, and two say so in print. Clarity: "Changes to masking settings affect new recordings and could take up to one hour to be reflected. Masking changes can't be applied retroactively." Hotjar: "After a session is sent, there is no way to retrieve or suppress data, and updating suppression settings will not apply the updated settings retroactively."
The one hour figure is the part teams miss. Adding a rule after you spot card numbers in a replay leaves up to another hour of recordings capturing them, on top of everything already stored. The remedies are deleting the affected recordings and shortening retention, which makes retention length a redaction control rather than a storage setting. It is also why plaintiffs in session replay wiretapping lawsuits argue over what was captured rather than what was later hidden in the replay UI.
Should you mask everything and unmask, or the reverse?
Mask everything by default and unmask what you have verified, because the failure mode of a blocklist is capturing data you never intended and the failure mode of an allowlist is a boring replay. FullStory built Private by Default on that reasoning: "no text is captured or sent outside the user's browser unless it is explicitly allowlisted as safe to capture," so that "you won't accidentally capture unwanted data even if you fail to set specific data capture rules."
Get the precedence rule right before writing the first selector. FullStory resolves conflicts toward privacy: "if an element matches both a masking rule and an unmasking rule, it would be masked rather than unmasked, as the stricter rule will always apply." Clarity goes the other way for attributes, where data-clarity-unmask="true" unmasks a node and its children and "overrides anything set on the Clarity website." Two products, two opposite answers.
Keep the rules in code rather than a dashboard. FullStory calls code-first "a less brittle and more future-proof approach than handling these rules through the UI using CSS selectors", because a selector written in a settings screen breaks silently when a component library ships new class names.
Flowsery runs cookie-free and records sessions without sampling. If you are still deciding what replay captures at all, start with what session replay is, then set the masking policy before the first script tag goes live.
Frequently asked questions
Does session replay masking happen before or after data is sent?
Before, in every product documented here. Clarity "never captures anything that is masked or sent over the wire", and Hotjar describes suppression as removing PII "before sending a session from your website's Document Object Model (DOM) to Hotjar". Masking applied at playback time is a display filter, not redaction.
Does maskAllInputs: true cover every form field in rrweb?
No. It expands to every key in rrweb's MaskInputOptions type, which omits checkbox, radio and file, so checkbox and radio state keeps recording. Block those elements instead, the way FullStory's Form Privacy does.
Can I unmask one field inside a masked container?
It depends on the product, and rrweb itself cannot. rrweb's recording options include no unmask class or selector, so a mask on a parent covers every child. FullStory and Sentry add unmasking on top, with Sentry shipping .sentry-unmask and .sentry-unblock applied to nothing by default.
What is the difference between masking and blocking an element?
Masking keeps the element and replaces its text; blocking removes the element and replays a placeholder. FullStory draws the line by inference risk, recommending exclusion for regulated data, for identifiers like bank account numbers, and for any element where interaction alone reveals something personal.
Do masked recordings still count as personal data?
Treat them as personal data until you have proved otherwise. Masking that preserves character length, click coordinates and element structure reduces identifiability without removing it, which is pseudonymisation rather than anonymisation, and pseudonymised data stays in scope under GDPR.
How long after adding a masking rule does it take effect?
On Clarity the documented figure is up to one hour, and only for new recordings. Client-side rules in rrweb, Sentry, PostHog and LogRocket take effect on the next page load serving the updated script, so a cached bundle or long CDN TTL extends the window where old rules still apply.
Flowsery
Start Your 14-Day Free Trial
Real-time dashboard
Goal tracking
Cookie-free tracking
Which session replay tool masks the most by default?
Sentry sets the strictest default of the tools covered here: it masks all text (maskAllText: true), masks every input, and blocks all media (blockAllMedia: true). rrweb sits at the other end, masking only input[type="password"] until you configure more. Picking a vendor without checking these defaults means picking how much gets exposed before you write a single rule.
Does PostHog mask text by default?
PostHog does not mask text by default. It masks all inputs, since maskAllInputs defaults to true, but plain text nodes record verbatim unless you add rules. Drop individual elements from the recording with the ph-no-capture class, which replaces them with a block of the same size.
Can inline SVGs or stylesheets be masked in session replay?
Masking rules apply to DOM text nodes and input values, so content outside those two places is not covered. Clarity does not mask content inside style sheets or style tags, which is why it recommends against hosting sensitive content in CSS. Hotjar notes that SVGs used as an image source can be suppressed, but inline SVGs cannot.
Should masking rules live in code or in a dashboard settings screen?
Keep them in code. FullStory calls a code-first approach less brittle than handling rules through the UI with CSS selectors, because a selector defined in a settings screen breaks silently the moment a component library ships new class names. Version them alongside the app so a rule change ships and reverts like any other code.
Was This Article Helpful?
Let us know what you think!
See us more often in Google
One click marks Flowsery as a preferred source, so our articles sit higher in your Top Stories, AI Mode, and AI Overviews.
Before you go...
Flowsery
Revenue-first analytics for your website
Track every visitor, source, and conversion in real time. Simple, powerful, and cookie-free.
Real-time dashboard
Goal tracking
Cookie-free tracking
Related Glossary Terms


How Session Replay Works, and What It Cannot See
A session replay rebuilds a visit from DOM mutations and input events, not video. See what it captures, what masking hides, and how it differs from heatmaps.


How Much Does Session Replay Slow Down Website Speed?
The real answer to does session replay slow down website speed, with published rrweb and Sentry numbers for script size, main-thread CPU, and upload bandwidth.


Where Rage Clicks vs Dead Clicks Actually Differ
The rage clicks vs dead clicks split is about cause, not severity. Detection thresholds published by PostHog, Hotjar, FullStory, LogRocket and Clarity.


How CHIPS Partitioned Cookies Give Each Site Its Own Jar
Setting CHIPS partitioned cookies takes Secure, SameSite=None and one more attribute, and the browser keeps a separate copy per top-level site.


What a Session Replay Lawsuit Wiretapping Claim Has to Prove in Court
Every session replay lawsuit wiretapping claim runs on CIPA 631, Pennsylvania's WESCA, or the federal Wiretap Act. What plaintiffs allege, what courts held.


What Digital Experience Analytics Covers That Web Analytics Misses
Unlike event counting, digital experience analytics reconstructs the visit itself, using session replay, heatmaps, friction detection and journey analysis.
Related Articles


Two Numbers Hide Behind One Drop-off Rate
Every funnel produces two drop-off rate numbers, one per step and one end to end, and teams quote them interchangeably. A worked table separates them.


Who Owns Dwell Time, the Search Engine or Your Analytics
Search engines own dwell time and your analytics cannot see it. Where the line falls against time on page and session duration, and what Google documents.


The Setup Choices Behind Every Funnel Analysis
Three setup choices decide what funnel analysis reports: step sequencing, the conversion window, and whether the funnel counts users or sessions.

