Skip to content
Docs menu · Masking & blocking

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

LevelWhat a reviewer seesUse it for
MaskThe element and its layout, with text replaced by asterisksInputs, names, emails, amounts — where the shape matters and the value does not
BlockAn empty placeholder of the same sizeChat transcripts, documents, embedded content — where even the layout is sensitive
RecordThe element as the visitor saw itNavigation, 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.

FieldHow it is recognised
Passwordstype="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 codesautocomplete="one-time-code", or a field named otp
Card numbers, CVV, expiry and cardholder nameautocomplete="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 detailsA field named or labelled iban, routing number or sort code
Anything that looks like a card numberA 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 typedDigits 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 textA 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> contentsNever 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 configureHow it is recognised
A secure fieldsecureTextEntry
Passwords, new passwords and one-time codestextContentType: "password", "newPassword", "oneTimeCode"; autoComplete: "password", "password-new", "sms-otp"; keyboardType: "visible-password"
Card numbers, security codes, expiry and cardholder nametextContentType: "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 aboveA placeholder, accessibilityLabel, testID or nativeID that reads like cvv, card number, password, iban, sort code
Anything that looks like a card numberA value of 13–19 digits that passes the Luhn check
A card number while it is being typedThe 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 textThe 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-maskWhat 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

  1. 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.

  2. Replay it

    Masked inputs show asterisks, ar-mask text shows asterisks, ar-block elements are empty boxes. With maskAllInputs: false, ordinary fields show what you typed.

  3. Look at the network request

    In the browser's network panel, open a request to /v1/ingest/events and search the payload for what you typed. The password and the card number are never there; with maskAllInputs: false an ordinary field is, which is the check worth doing before you opt out.

Questions that come up in practice are answered in masking questions.