Features
Masking and Blocking Sensitive Data
Keep personal data out of AnyReplay recordings: form fields masked by default, the fields never recorded, maskAllInputs, maskAllTyping, ar-mask and ar-block.
Everything described here happens in the visitor's browser or on their device, while the recording is made. A masked value is replaced before it is sent, so it never reaches AnyReplay, never appears in a replay and is never translated.
Three levels
| Level | What a reviewer sees | Use it for |
|---|---|---|
| Mask | The element and its layout, with text replaced by asterisks | Inputs, names, emails, amounts — where the shape matters and the value does not |
| Block | An empty placeholder of the same size | Chat transcripts, documents, embedded content — where even the layout is sensitive |
| Record | The element as the visitor saw it | Navigation, product copy, error messages |
Never recorded, whatever you configure
These are masked in the browser in every mode. There is no option that turns them off, so a checkout page cannot end up with a card number in a replay because someone forgot a setting.
| Field | How it is recognised |
|---|---|
| Passwords | type="password", autocomplete="current-password" or "new-password", a name, id, placeholder or label containing password, passphrase or pwd, and rrweb's data-rr-is-password marker (so a "show password" toggle that flips the type to text stays masked) |
| One-time codes | autocomplete="one-time-code", or a field named otp |
| Card numbers, CVV, expiry and cardholder name | autocomplete="cc-number", cc-csc, cc-exp, cc-exp-month, cc-exp-year, cc-name, cc-type; or a name, id, placeholder, aria-label or data-testid that reads like one — cardNumber, card-number, cvv, cvc, csc, security code, credit card |
| Bank details | A field named or labelled iban, routing number or sort code |
| Anything that looks like a card number | A value of 13–19 digits that passes the Luhn check, wherever it was typed — the last line of defence for a checkout whose fields are called field-2 |
| A card number while it is being typed | Digits only (spaces and dashes allowed), starting 2–6, grouped like a card and more than six digits long. A number is typed one key at a time, so the field is masked from the seventh digit on: at most the first six — the issuer, not the card — are ever recorded. Telephone numbers starting 0 or + or written in short groups, dates, postcodes and quantities stay recorded |
| A card number inside longer text | A card number written into a note, a message or any other field among other words — "my card is 4242 4242 …" — has its digits replaced with * as soon as a seventh is typed, here and in a contenteditable editor. The field stays recorded: the words around it, and other numbers such as a telephone number, a date or an order number, are kept |
<script> contents | Never recorded |
Masking form fields: maskAllInputs
maskAllInputs is on by default: the value of every input, text area and select is replaced with asterisks of the same length — what was in the markup when the page loaded, and every character typed after. A reviewer still sees the field being filled in, how long it took and where the person hesitated; they do not see the characters.
To see what visitors type, opt out with maskAllInputs: false. Passwords, one-time codes, payment fields and card numbers stay masked; everything else in a form is recorded.
anyreplay('init', {
projectKey: 'YOUR_PROJECT_KEY',
maskAllInputs: false, // record what visitors type into form fields
});Opt out when watching a form being filled in is the point and your forms carry nothing you would not want a teammate reading over a visitor's shoulder. If some fields are sensitive — health details, salaries, contract terms — mark those with ar-mask and they stay masked. Only an explicit false opts out — passing the string "false", which is what a tag manager variable often hands over, is ignored and logs a warning in the console, and the fields stay masked.
Rich-text editors: maskAllTyping
Input masking covers form fields. It does not cover a contenteditable element, because the text a person types there is page text rather than an input value — which is what a rich-text editor, a comment box or a chat composer is built from. maskAllTyping: true masks the text of every editable region as well, in the first snapshot and in every change after it, and masks form fields too whatever maskAllInputs says. It is the stricter of the two switches, and a superset of the other.
anyreplay('init', {
projectKey: 'YOUR_PROJECT_KEY',
maskAllTyping: true,
});Turn it on when people type personal or confidential prose into your product: a support composer, a document or note editor, a message box, a form built on a rich-text field. It is a separate switch because it masks everything inside an editable element, including the text your own code put there before anyone typed — a loaded document, a quoted message — so a replay of that editor shows the layout and asterisks.
Masking text on the page: ar-mask
Most personal data on a page was not typed during the session: an account page shows an email, an invoice shows an address. Add the ar-mask class and the text of that element and everything inside it is masked. The class works on a form field too: put it on one input, or on a <fieldset> around several, and their values are masked while the rest of the page is recorded normally. It is the right tool when only part of a form is sensitive.
<dl>
<dt>Email</dt>
<dd class="ar-mask">[email protected]</dd>
<dt>Plan</dt>
<dd>Team</dd>
</dl>
<label>Diagnosis <input name="diagnosis" class="ar-mask"></label>Removing an element: ar-block
Add ar-block and the element's contents are not recorded at all; replay shows a placeholder of the same size. Use it for anything where masked text would still reveal too much, and for images whose content is personal — on the web a recording keeps each image's URL, so a blocked element is the way to keep a picture out of the replay.
<section class="ar-block">
<!-- support chat transcript -->
</section>Using your own class names
anyreplay('init', {
projectKey: 'YOUR_PROJECT_KEY',
maskClass: 'pii', // instead of ar-mask
blockClass: 'no-record', // instead of ar-block
});Useful when your design system already marks sensitive elements. The options replace the defaults, so ar-mask and ar-block no longer apply once you set them.
React Native
The two SDKs behave the same way. What a person types into a TextInput is masked by default, maskAllInputs: false records it, maskAllTyping: true means what it means in the browser, and the same floor applies as far as a phone can tell.
| Masked whatever you configure | How it is recognised |
|---|---|
| A secure field | secureTextEntry |
| Passwords, new passwords and one-time codes | textContentType: "password", "newPassword", "oneTimeCode"; autoComplete: "password", "password-new", "sms-otp"; keyboardType: "visible-password" |
| Card numbers, security codes, expiry and cardholder name | textContentType: "creditCardNumber" and the rest of the creditCard… family; autoComplete: "cc-number", "cc-csc", "cc-exp…", "cc-name", "cc-type" |
| A field named like one of the above | A placeholder, accessibilityLabel, testID or nativeID that reads like cvv, card number, password, iban, sort code |
| Anything that looks like a card number | A value of 13–19 digits that passes the Luhn check |
| A card number while it is being typed | The same rule as the browser: digits only, starting 2–6, grouped like a card and more than six digits long. The screen is read twice a second, so a read can land halfway through a number; at most its first six digits are ever recorded |
| A card number inside longer text | The same rule as the browser, from 0.2.2: a card number written into any TextInput among other words — a note, a message, a multi-line field — has its digits replaced with * as soon as a seventh is typed. The field and the rest of its text stay recorded |
testID containing ar-mask | What you marked. Change the marker with maskTestID. |
maskAllTyping exists here too and keeps the same meaning — mask everything a person types — but covers less, because a screen has no contenteditable: every place a person can type is already a TextInput. What it adds on mobile is that it cannot be weakened, so maskAllInputs: false set elsewhere in your configuration no longer records field values.
A masked element keeps its box and loses its words, replaced on the device, and masking applies to everything inside a masked element. Image URLs are recorded unless you set maskImages: true; bundled images are uploaded once per project so they can be drawn, and photos from the device's caches or photo library are never read.
<Text testID="ar-mask-balance">{formatMoney(balance)}</Text>Check it
Record a test session
Fill in a form — including a password field and, if you have one, a card field — and visit the pages you marked.
Replay it
Masked inputs show asterisks,
ar-masktext shows asterisks,ar-blockelements are empty boxes. WithmaskAllInputs: false, ordinary fields show what you typed.Look at the network request
In the browser's network panel, open a request to
/v1/ingest/eventsand search the payload for what you typed. The password and the card number are never there; withmaskAllInputs: falsean ordinary field is, which is the check worth doing before you opt out.
Questions that come up in practice are answered in masking questions.