Personal data handling
The Contentsquare tag masks Personal Data before it leaves the browser and offers configurable commands, HTML attributes, and encrypted-collection modes to control what is sent and stored.
At a glance
Section titled At a glancePersonal Data handling on the Contentsquare tag relies on three layers:
- Automatic Personal Data Redaction — the tag detects common patterns (emails, JWTs, OAuth tokens, credit card numbers) and redacts them before any data leaves the browser. See Automatic Personal Data Redaction.
- Masking features you configure — commands and HTML attributes to mask or strip Personal Data from URLs, API error URLs, DOM content, and clicked element text. See Masking features.
- Controlled Exposure — collect and encrypt selected values client-side so Contentsquare cannot see them, and decrypt only with your private key when reviewing replays. See Controlled Exposure.
Some data is sent as-is and is not automatically masked — most notably identity strings (identify), user properties, event names, event/pageview properties, custom error attributes, and the page title. You are responsible for ensuring these do not contain Personal Data. See Data sent as-is.
Automatic Personal Data Redaction
Section titled Automatic Personal Data RedactionThe Contentsquare tag automatically detects and replaces certain types of personal data before any information leaves the browser. This happens without any configuration.
The types of personal data that are detected depend on the data channel.
What is detected
Section titled What is detected| Personal data type | Replacement token | How it is detected |
|---|---|---|
| Email addresses | CS_ANONYMIZED_EMAIL | Pattern matching of email addresses, including URL-encoded forms (%40, %2540) |
| JWT tokens | CS_ANONYMIZED_JWT | Structural match on the standard JWT format (eyXxx.eyXxx.Xxx — three base64-encoded segments) |
| OAuth token values | CS_ANONYMIZED_TOKEN | Query parameters named id_token, access_token, refresh_token, or auth_token, regardless of the token format |
| Credit card numbers | CS_ANONYMIZED_PII | 16-digit numbers starting with 4 or 5 (Visa/Mastercard ranges), validated with a Luhn checksum. See Credit card numbers |
| Long digit sequences | • for every digit | Text containing a sequence of 9 digits or more, such as phone numbers or social security numbers. See Long digit sequences |
Credit card numbers
Section titled Credit card numbersThe digits can be written as a single block or grouped by four with spaces or dashes, as long as the same separator is used throughout (4111 1111 1111 1111, 4111-1111-1111-1111). Numbers that are already partially masked, such as 4***-****-****-1234, are also replaced by CS_ANONYMIZED_PII.
Long digit sequences
Section titled Long digit sequencesIn Session Replay, when a text or an attribute value contains a sequence of 9 digits or more, all the digits of that text or value are masked, even with No masking. They are shown as •, or as a on pages where text is masked. The Data masking settings call them sensitive numbers. This covers phone numbers, social security numbers, and similar identifiers.
- When numbers are masked on a page (with Full masking, Partial masking, or a page rule), every digit is masked, whatever the length of the sequence.
- Text inside elements marked with
data-cs-captureis not subject to this rule. Email addresses and credit card numbers are still masked in it.
Where each type is masked
Section titled Where each type is maskedNot every detection applies everywhere. The table below shows exactly which personal data types are masked in each data channel.
| Data channel | Emails | JWT tokens | OAuth tokens | Credit cards | Long digit sequences |
|---|---|---|---|---|---|
| URLs (page URL, referrer) | ✅ | ✅ | ✅ | — | — |
| HTML / DOM content (Session Replay) | ✅ | — | — | ✅ | ✅ |
| JavaScript error messages | ✅ | — | — | — | — |
| JavaScript error file names (inline scripts only, see below) | ✅ | ✅ | ✅ | — | — |
| API error URLs | ✅ | ✅ | ✅ | — | — |
| Custom error messages, including embedded URLs | ✅ | — | — | — | — |
| Custom Variables (name and value) | ✅ | — | — | — | — |
| Dynamic Variables (name and string value) | ✅ | — | — | — | — |
| Ecommerce transactions (transaction id) | ✅ | — | — | — | — |
Ecommerce transaction items (item id, name, sku, and category) | ✅ | — | — | — | — |
| Network request URLs in Session Replay (performance timings) | ✅ | ✅ | ✅ | — | — |
JavaScript error file names: the masking listed above, and your setPath and setQuery overrides, apply only when the file name is the URL of the current page, which is the case for errors raised by inline scripts. The URLs of other script files are collected as-is.
Custom error messages: by default, only email addresses are masked in custom error messages, including in the URLs they contain. Masking of JWT and OAuth tokens in these URLs is applied only when enabled for your project.
Network request URLs in Session Replay: the query string is removed, and the patterns registered with networkRequest:maskUrls are applied.
Also, the following data-specific protections are always applied:
- JavaScript errors: form field values (patterns like
value="...") are redacted in error messages. - Custom errors: see Custom error messages.
- API errors: the query string is stripped entirely from the collected URL, in addition to the masking listed above. A
#fragmentis kept when the URL has no query string. See Removing Personal Data in API errors for the details collected with API errors. - Clicked element text: the entire text is dropped when it contains an email address, a credit card number, or a sequence of 9 digits or more. See Removing Personal Data in Clicked Element.
- External events (Voice of Customer event tags): see External events.
Custom error messages
Section titled Custom error messagesThe message passed to trackError is truncated to 300 characters, then the following patterns are replaced:
- Email addresses are replaced by
CS_ANONYMIZED_EMAIL. - Phone numbers are replaced by
CS_ANONYMIZED_PHONE. - Name values are replaced by
CS_ANONYMIZED_NAMEwhen they are in quotes after a name key. Name keys start withfirst,last,user,profile, orprimary, followed byname. For example,firstName="John",lastName: 'Doe',userName:"jsnow", and{key:"firstName", value:"John"}are redacted.
Unquoted values, such as firstName=John, are not redacted.
External events
Section titled External eventsEvent tags sent to Voice of Customer are dropped entirely, and not collected, when they:
- Contain an email address, an IP address, or a sequence of 9 digits or more.
- Contain characters other than letters
a-zandA-Z, digits, spaces, and_,-,.,:,/. - Are empty or longer than 250 characters.
Data sent as-is (not automatically masked)
Section titled Data sent as-is (not automatically masked)The following data is transmitted without any Automatic Personal Data Redaction. If these channels may contain personal data, you are responsible for ensuring it is removed before sending it to Contentsquare.
| Data channel | Details |
|---|---|
identify identity string | The identity string is sent as-is. Use the { hash: true } option to hash it client-side before sending. |
User properties (addUserProperties) | Keys and values are not masked. They are truncated to 255 characters |
Visit properties (customProperties:visit:add) | Keys and values are not masked. Keys longer than 512 characters are dropped. String values are truncated to 1,024 characters |
Pageview properties (customProperties:page:add) | Keys and values are not masked. Keys longer than 512 characters are dropped. String values are truncated to 1,024 characters |
Page events (trackPageEvent) | The event name string is sent verbatim |
Custom events (trackEvent) | The event name string is not masked. It is truncated to 1,024 characters |
Custom error attributes (trackError) | Only the error message is filtered. Attribute keys and values are not masked: they are truncated to 30 characters, and all attributes are dropped when there are more than 5 |
Ecommerce item data (ec:transaction:items:add) | Item attributes other than id, name, sku, and category, such as the merchant, are sent as-is |
| Page title | On projects that collect Product Analytics data, the page title is not masked. It is truncated to 255 characters. See Hiding the page title |
| Dynamic Variable integer values | Integer values are not masked |
| Non-email Personal Data in Custom/Dynamic Variable strings | Credit card numbers, phone numbers, JWT tokens, and other sensitive data (other than emails) are not detected in Custom or Dynamic Variable values |
Custom and Dynamic Variable names are truncated to 512 characters, and their values to 255 characters.
Masking features
Section titled Masking featuresBeyond Automatic Personal Data Redaction, Contentsquare provides several commands and HTML attributes to mask or strip Personal Data from specific channels. This section lists every available feature and explains how and where to use them.
Command cheatsheet
Section titled Command cheatsheetThe following commands and attributes are available to control Personal Data collection. Each is detailed in the corresponding section below or in the Command Reference.
| Scope | Command / attribute | Purpose |
|---|---|---|
| URL path | setPath | Rewrite the URL path before it is collected |
| URL query string | setQuery | Rewrite the URL query string before it is collected |
| Referrer | referrer:maskUrl | Mask Personal Data in the referrer path |
| Referrer | referrer:removeQueryString | Drop the referrer query string |
| Referrer | referrer:keepQueryString | Revert referrer:removeQueryString |
| Page title | hidePageTitle | Hide the page title in the collected pageviews |
| Page title | resetHidePageTitle | Revert hidePageTitle |
| Page title | data-cs-hide-title attribute on <title> | Hide the page title in the collected pageviews |
| API error URLs | networkRequest:maskUrls | Mask Personal Data in collected API error URLs |
| API error URLs | networkRequest:maskUrls:reset | Clear the patterns registered with networkRequest:maskUrls |
| API error URLs | api-errors:maskUrl (deprecated) | Mask Personal Data in collected API error URLs (full URL patterns) |
| Session Replay (DOM) | setPIISelectors | Mask DOM elements matching CSS selectors |
| Session Replay (DOM) | data-cs-mask attribute | Mask the content of a specific HTML element |
| Session Replay (DOM, masked pages) | setCapturedElementsSelector | Expose specific elements on a masked page via CSS selectors |
| Session Replay (DOM, masked pages) | data-cs-capture attribute | Expose a specific element on a masked page |
| Session Replay (page-level) | excludeURLforReplay | Disable Session Replay collection for matching pages |
| Session Replay (visitor consent) | replay:consent:unanonymized:granted | Lift text masking for a visitor who consented |
| Session Replay (visitor consent) | replay:consent:unanonymized:withdrawn | Restore text masking for a visitor who withdrew consent |
| Controlled Exposure | setEncryptionSelectors | Collect the content of matching elements in encrypted form |
| Controlled Exposure | data-cs-encrypt attribute | Collect the content of a specific element in encrypted form |
| Identity | identify with { hash: true } | Hash the identity string client-side before sending it |
| Identity | resetIdentity | Clear the identity string (for example, on logout) |
In identity and user attributes
Section titled In identity and user attributesThe tag exposes several commands to attach identity or attributes to a user. None of these values are automatically masked — what you pass is what is sent and stored.
identify: associates an identity string with the current user. The value is sent as-is unless you use the{ hash: true }option, which hashes it client-side. Prefer non-directly-identifying values (UUIDs, account numbers) over emails or phone numbers. See Track Users → Best practices for the trade-offs of client-side hashing.resetIdentity: clears the identity associated with the current user. Use it on logout so a subsequent user on the same device does not inherit the previous identity.addUserProperties,customProperties:visit:add,customProperties:page:add: attach custom properties to the user, visit, or pageview. Keys and values are sent without any Personal Data redaction — do not pass Personal Data in either. Only length limits apply:addUserPropertiestruncates keys and values to 255 characters.customProperties:visit:addandcustomProperties:page:adddrop keys longer than 512 characters and truncate string values to 1,024 characters.
trackEventtruncates the event name to 1,024 characters, andtrackPageEventsends it verbatim. Neither applies Personal Data redaction.
For the full API, see Track Users.
Personal Data handling in the HTML (in Session Replay)
Section titled Personal Data handling in the HTML (in Session Replay)Sessions will be collected for Session Replay if:
- The
Session Replayfeature has been enabled for your account - The threshold set by the
Session Replay Collection Rateis not exceeded
For the HTML content to be sent, all these conditions must be true:
- The user hasn't opted out
- The session is being tracked
- The browser is compatible
- The session has been flagged as "Collected for Session Replay", using the
_cs_scookie
Session Replay data collection options
Section titled Session Replay data collection optionsIf Session Replay is turned on and data collection is active for the session, the HTML content of the page is sent after every pageview (both natural and artificial) and every DOM change (only the updated elements are sent).
The Data masking page of your project settings controls how much of the page content is collected. Admins set a default approach for all pages:
- Full masking: hide all text, all numbers, and all media.
- Partial masking: hide all text and all numbers, show all media.
- No masking: show all text, non-sensitive numbers, and all media.
Page rules override the default approach on specific pages, and element rules mask or unmask specific elements.
With No masking
Section titled With No maskingThe following protections apply whatever the default approach: Contentsquare never collects the actual text typed into any <input> or <textarea> field.
- The value typed in every
<input>element is replaced with bullets before the HTML is sent, whatever its type. For<input>elements with typenumber, every character other than whitespace is replaced by0 - The value of
<select>elements is replaced the same way. The checked state of checkboxes and radio buttons, and the option selected in a<select>, remain visible in the replay - The
valueattribute is stripped from every<input>element, except for the typesbuttonandsubmit, where it holds the label displayed on the button - The content of
<textarea>elements is replaced with bullets before the HTML is sent - The content inside any element with a
contenteditableattribute is replaced with bullets before the HTML is sent, whatever the attribute value (includingcontenteditable="false") - The content of
<script>elements is emptied. The element and its attributes are kept - HTML comments are never collected
- Email addresses, credit card numbers, and long digit sequences are masked in text and in attribute values, except in the attributes kept as-is listed in With Full masking or Partial masking. In the
hrefof<a>elements and thesrcof<iframe>elements, only email addresses are masked - The content of every element marked with the data attribute
data-cs-mask, or matching one of the default masked selectors, is removed (see the Tagging DOM elements section) - Except for the items listed previously, all the HTML content is sent, including
<head>
With Full masking or Partial masking
Section titled With Full masking or Partial maskingAll the protections listed in With No masking also apply. On pages where text is masked, the following also applies:
- Every character of the text content included in the elements sent is replaced by a lowercase "a". Characters from Chinese and Japanese scripts are replaced by the full-width "a". Other characters, including Korean, are replaced by "a". Whitespace is preserved, so the replay keeps the shape of the text
- Full masking and Partial masking also mask numbers: digits are shown as "a". When a page rule unmasks numbers but not text, digits are preserved, unless the text contains a sequence of 9 digits or more
- With Full masking, images, videos, and audio are replaced by grey blocks
- The label of
<input>elements with typebuttonorsubmit(theirvalue) is replaced by "a", like other text - The content of every element marked with the data attribute
data-cs-captureis collected and shown unless it falls under the excluded elements listed above (<input>,<textarea>) - The following attributes are kept as-is:
id,class,style,srcset,sizes,rel,type,width,height,media,align,dir,bgcolor,color,border,colspan,rowspan,cols,rows,size,start, andslothrefandsrc. Email addresses are masked in thehrefof<a>elements and thesrcof<iframe>elements. On other elements, such as<img>, they are collected as-isnameon radio buttons- All the attributes of SVG elements
- Attributes that are allowlisted for your project
- All other attributes, such as
alt,title,placeholder,aria-*, ordata-*, are kept but their value is emptied
Page rules
Section titled Page rulesPage rules override your default approach on specific pages, so that you don't have to apply the same masking across the whole site. Admins set them in the Page rules section of the Data masking page:
- With Full masking or Partial masking as default approach, page rules unmask content on the matching pages.
- With No masking as default approach, page rules mask content on the matching pages.
For the matching pages, check what to mask or unmask: Text, Numbers, and Media. Masked numbers are hidden whatever their length, and masked media (images, videos, and audio) are replaced by grey blocks.
Each rule matches pages on their URL: URL starts with, URL ends with, URL contains, URL matches exactly, their negative forms, or Regex. Under More options, you can ignore the query string, URI fragments, or case sensitivity. Use Check URL to verify which URLs your rules cover.
Visitor consent and masking
Section titled Visitor consent and maskingOn projects configured for it, a visitor's consent can lift text masking for that visitor's replays. Send the visitor's decision with the replay:consent:unanonymized:granted and replay:consent:unanonymized:withdrawn commands. These commands only work when the default approach is Full masking or Partial masking and the project is configured for visitor consent on masked replays. With No masking, they have no effect, even on pages masked by page rules.
Once consent is granted, text masking is lifted for that visitor on all pages. Numbers and media stay masked.
This consent is different from the consent that some projects require before Session Replay collection starts, described in Following Session Replay requests.
Block data collection for specific pages (Session Replay)
Section titled Block data collection for specific pages (Session Replay)Use the excludeURLforReplay command to block data collection on pages based on their URL. Use cases include:
- Personal data has been exposed
- Exclude whole parts of your website from being collected for Session Replay
- Some page replays for Session Replay cause performance issues (when the page contains many images or videos, for instance) — you can block these pages until the problem is fixed
window._uxa = window._uxa || [];window._uxa.push(["excludeURLforReplay", "URL_REGEX"]);Once pushed, the regular expression stays active until the page is reloaded. The tag tests it at every natural and artificial pageview, so a single call covers all the routes of a single-page application. Push the command again on every full page load, before the tag is loaded.
- The regular expression is tested against the collected URL, that is the URL after your
setPathandsetQueryoverrides and after Automatic Personal Data Redaction. - The pattern does not need to match the full URL, so
www\.example\.com/checkoutmatches without the protocol. If you anchor the pattern with^, include the protocol, for example^https://www\.example\.com/checkout. Matching is case-sensitive. - Each call replaces the regular expression of the previous call. To exclude several sections, combine them in one expression, for example
(checkout|account). - Pushing the command after Session Replay collection has started does not stop the collection of the current pageview. It applies from the next pageview.
Using this command does not block pageviews for the given pages — it only blocks data collection for Session Replay.
Remove content/Personal Data from the collected HTML
Section titled Remove content/Personal Data from the collected HTMLThere are two methods to remove content from the collected HTML in your code:
- Using the Contentsquare Personal Data Selector
- Tagging DOM Elements
Admins can also create element rules without code, in the Element rules section of the Data masking page. A Mask rule hides the elements matching CSS selectors and can mask specific attributes (Mask attributes). A rule applies to All pages or Some pages, matched on their URL. Sense can scan your pages for personal data and suggest element rules.
Personal Data Selector
Section titled Personal Data SelectorEach DOM element to be masked is identified by its CSS selector, and those selectors are pushed to the API via a JavaScript object. The content of the identified element is removed from the replay.
Push the following object containing CSS selectors for text and attributes to the API, before firing the main tag:
window._uxa = window._uxa || [];window._uxa.push(['setPIISelectors', PIIObject]);The structure of the PII Object is as follows:
-
PIISelectors: Comma-separated list of CSS selectors which allow the matching DOM elements to be masked by the tag. -
Attributes: Using this key, you can remove the defined attributes of the DOM elements from the elements matching the CSS selectors.attrNameaccepts a single attribute name or an array of attribute names, as shown in the following example.
Keep in mind that using both PII Selectors and Attributes is optional, meaning you can use either full element selectors, specific attribute selectors or both in the same object:
{ PIISelectors: [".css-selector, selector"], // DOM elements to be masked Attributes: [ { selector: "select#month option, select#year option", // CSS selector(s) attrName: 'id' // Attribute name you want to mask }, { selector: ".link-page-7", // CSS selector(s) attrName: ['href','name'] // Array attribute names you want to mask } ]};Each setPIISelectors call adds its selectors to the ones already registered. Invalid CSS selectors are ignored without any error message: check your selectors in the browser console with document.querySelectorAll() before deploying them. See When masking rules take effect for timing.
Tagging DOM elements
Section titled Tagging DOM elementsTo exclude content from being collected, use the data-cs-mask attribute on selected elements:
Content before<p data-cs-mask>emailaddress@sentitivedata.com</p>Content afterTheir content is removed in the browser before it is sent to Contentsquare:
Content before<p data-cs-mask>emailaddress@sentitivedata.com</p>Content afterFor a masked element:
- Its text content and all its child elements are removed.
- The element itself is kept with its size, so the replay shows an empty placeholder in its place and the layout is preserved.
- Its
value,checked,src,data, andaltattributes are removed.
You can also add the data-cs-mask attribute with JavaScript. See When masking rules take effect.
Events on removed HTML — In Session Replay, clicks, hovers, keyboard, and scroll events that target elements inside a masked element are not collected. Analytics events used for heatmaps and Zoning Analysis, such as clicks, are still collected for these elements.
Default masked selectors
Section titled Default masked selectorsElements matching the following selectors are always masked, in the same way as elements marked with data-cs-mask. This keeps the masking set up for other tools in place:
[data-hj-suppress]and.data-hj-suppress[data-hj-masked][data-heap-redact-text][data-heap-redact-attributes][data-heap-ignore]and[heap-ignore].ctHidden
Display content in a fully masked HTML page
Section titled Display content in a fully masked HTML pageThere are two methods to display content in a masked HTML page in your code:
- Using the Contentsquare Personal Data Selector
- Tagging DOM Elements
Admins can also create an Unmask element rule on the Data masking page, to show the elements matching CSS selectors on masked pages.
Personal Data Selector
Section titled Personal Data SelectorEach DOM element to be displayed is identified by its CSS selector, and those selectors are pushed to the API via a JavaScript object. The content of the identified element is displayed in Session Replay.
The string which contains comma-separated CSS selectors for text and for attributes must be pushed to the Contentsquare API, before firing the main tag, as follows:
window._uxa = window._uxa || [];window._uxa.push(["setCapturedElementsSelector", "#capture-me, .show-me"]);Each setCapturedElementsSelector call replaces the selectors of the previous call. To expose several elements, pass all their selectors in a single comma-separated string.
Tagging DOM elements
Section titled Tagging DOM elementsTo display an HTML element in a masked page, add the data-cs-capture attribute to the HTML elements:
Sensitive Content before<p data-cs-capture>some piece of text that is not sensitive</p>Sensitive Content afterContent that is part of the tagged element is captured as is:
aaaaaaaaa aaaaaaa aaaaaa<p data-cs-capture>some piece of text that is not sensitive</p>aaaaaaaaa aaaaaaa aaaaadata-cs-capture and setCapturedElementsSelector apply to the matching element and all its descendants. Only the text is exposed: the values of attributes that masking empties stay empty.
The data-cs-capture attribute can also be added with JavaScript. See When masking rules take effect.
When masking rules take effect
Section titled When masking rules take effectThe tag reads masking attributes and selectors when a pageview starts. This applies to the data-cs-mask, data-cs-capture, and data-cs-encrypt attributes, and to the setPIISelectors, setCapturedElementsSelector, and setEncryptionSelectors commands:
- Add the attributes, or push the commands, before the pageview is sent.
- Elements inserted in the page later are handled when they are added.
- Adding an attribute to an element that already exists after the pageview has been sent only takes effect at the next natural or artificial pageview.
Session Replay Testing
Section titled Session Replay TestingPrerequisites
Section titled Prerequisites- Have Google Chrome installed
- Download the Contentsquare Tracking Setup Assistant ↗ Chrome Extension
- Mask elements from the replay using CSS selectors or the
data-cs-maskattribute —<input>elements are not captured by default, except dropdowns which still need masking - Set the default approach to Full masking
- If you have already provided us with a list of your internal/office IPs for us to add to our exclusion list, inform your Contentsquare Success Manager to temporarily pause that exclusion list. Otherwise, sessions run from such an IP will not be collected by Contentsquare
Testing
Section titled Testing-
Start a journey on your website, featuring the elements to be masked.
-
In the Contentsquare Tracking Setup Assistant extension, select
Turn off automaskingto prevent text masking (aaa) from affecting your local session.
-
Select
Collected for Session Replayto enable Session Replay data collection for your session.
-
Select
Trigger testing pageEventto create theSR Testingpage event which identifies your current session.
-
Make sure that the elements to be masked are visible as you progress along your testing journey. If required, fill out any test personal information. You will be able to check this information has been masked in the resulting replay.
-
Once you are done testing, close your session by either closing the session's tab or deleting your cookies.

-
Watch the replay and look for all the areas where Personal Data could appear:
- ❌ If Personal Data is visible, update your masking and test again.
- ✅ If there were no Personal Data, you are covered in terms of Privacy compliance.
What's next?
Section titled What's next?If you are in the initial implementation phase of your project, inform your Success Manager about the results so they can double-check and then switch the default approach to No masking for you to use full Session Replay.
If you are not in implementation and your tests are successful, you can update the default approach, page rules, and element rules yourself if you are an Admin, on the Data masking page of your project settings.
For instructions, see the Help Center article ↗.
Following Session Replay requests
Section titled Following Session Replay requestsHTML content is sent via requests to one of the endpoints listed below, based on where data is stored for your project:
https://k.aeu1.contentsquare.net/{apiVersion}/recordinghttps://k.aus1.contentsquare.net/{apiVersion}/recordinghttps://k.eu1.az.contentsquare.net/{apiVersion}/recordinghttps://k.us1.az.contentsquare.net/{apiVersion}/recordinghttps://k.ba.contentsquare.net/{apiVersion}/recordinghttps://k.bf.contentsquare.net/{apiVersion}/recordinghttps://k.aa.contentsquare.net/{apiVersion}/recordinghttps://k.af.contentsquare.net/{apiVersion}/recording
The request is sent when the second value of the _cs_s cookie (Session Replay data collection state) is one of the following, or when Event Triggered Replay (ETR) is enabled on the project:
5: collected, per the project's global sampling rate6: collected, per a URL sampling rule8: collected, per a targeting rule
When your project requires the visitor's consent for Session Replay, no request is sent until the visitor grants it.
The request contains all the DOM.
Blocking Personal Data in URLs
Section titled Blocking Personal Data in URLsPersonal data appears in more places than just the HTML content.
For a given page, you also need to mask Personal Data found in:
- The path of the current page
- The query string parameters of the current page
- The referrer of the next visited page
Unmasked data could be exposed in Session Replay and in the Error Analysis module.
Masking Personal Data in the URL path
Section titled Masking Personal Data in the URL pathTo remove Personal Data in the URL of the current page, rewrite the URL of the pageview before the tag collects it, using the setPath command.
The setPath command takes as a parameter the exact new path to collect. The tag collects this value as it is: it does not replace anything in it. Your site code is responsible for computing the masked path.
window._uxa = window._uxa || [];window._uxa.push([ 'setPath', <NEW_PATH_TO_COLLECT>, { lifespan: 'onNextPageviewOnly' }]);Call this command on every page where the URL contains Personal Data, before the main Tracking tag is loaded. Pass only a path, starting with /, never a full URL.
The lifespan: 'onNextPageviewOnly' option limits the override to the next pageview. In a single-page application, call setPath again before every route change. Otherwise, the following pageviews are collected with the real URL, including its Personal Data. See How long setPath and setQuery apply.
Example
Section titled ExampleYour site code replaces the user name in the current path with a placeholder, then passes the result to setPath:
window._uxa = window._uxa || [];var maskedPath = window.location.pathname.replace(/\/users\/[^/]+/, "/users/CS_ANONYMIZED_USER_ID");window._uxa.push(["setPath", maskedPath, { lifespan: "onNextPageviewOnly" }]);| URL before masking | Value passed to setPath | URL after masking |
|---|---|---|
https://www.contentsquare.com/users/jon.snow | /users/CS_ANONYMIZED_USER_ID | https://www.contentsquare.com/users/CS_ANONYMIZED_USER_ID |
https://www.contentsquare.com/cartpage/users/jon.snow | /cartpage/users/CS_ANONYMIZED_USER_ID | https://www.contentsquare.com/cartpage/users/CS_ANONYMIZED_USER_ID |
Masking Personal Data in URL query string parameters
Section titled Masking Personal Data in URL query string parametersWhen Personal Data can be found in the query parameters of a URL, use the setQuery command to replace the query string within the URL with a new masked one.
window._uxa = window._uxa || [];window._uxa.push([ 'setQuery', <ANONIMYZED_QUERY_STRING>]);The setQuery command takes as a parameter the exact new query string to display within the URL. This command instructs the tag to replace the part of the URL after the ? with masked values. The leading ? is optional. To remove the query string entirely, pass an empty string: setQuery("").
Call this command on all pages where the URL contains Personal Data, and before the main tag is loaded. Only the query string is to be configured, not the whole URL.
Example
Section titled Examplewindow._uxa = window._uxa || [];window._uxa.push(["setQuery", "?user_id=CS_ANONYMIZED_USER_NAME&address=CS_ANONYMIZED_ADDRESS"]);| URL before masking | URL after masking |
|---|---|
https://www.mysite.com/us/makeup/valentines-day/?user_id=jon.snow&address=castle.black | https://www.mysite.com/us/makeup/valentines-day/?user_id=CS_ANONYMIZED_USER_NAME&address=CS_ANONYMIZED_ADDRESS |
https://www.mysite.com/us/makeup/valentines-day/?campaign=valentinesday_feb_2021&user_id=jon.snow&address=castle.black | https://www.mysite.com/us/makeup/valentines-day/?user_id=CS_ANONYMIZED_USER_NAME&address=CS_ANONYMIZED_ADDRESS |
Customize the masked term according to the type of the parameter to replace, so that the result is explicit:
CS_ANONYMIZED_USER_IDCS_ANONYMIZED_EMAILCS_ANONYMIZED_ADDRESS
...and more.
How long setPath and setQuery apply
Section titled How long setPath and setQuery apply- By default, the overridden path or query string stays in effect for every following pageview of the same page load, including artificial pageviews in single-page applications. Push the command again with the new value on each route change.
- To apply the override to the next pageview only, use the
lifespanoption. SeesetPathandsetQueryin the Command Reference. - When you override the path or the query string, the
#fragmentpart of the URL is not collected. - Automatic Personal Data Redaction still applies to the resulting URL.
Masking Personal Data in the referrer
Section titled Masking Personal Data in the referrerEven when you have masked Personal Data in URLs and query parameters on a page, they propagate to the next visited page through the referrer ↗. To handle this use case, use the referrer: commands below.
In the URL
Section titled In the URLConsider this journey:
- Users navigate from
https://www.example.com/users/123456/products/ABCDEFtohttps://www.example.com/. - A pageview is sent on
https://www.example.com/. - The referrer value in both the browser and in the pageview request is
https://www.example.com/users/123456/products/ABCDEF.
To mask Personal data in the URL within the referrer, use the referrer:maskUrl command:
var referrers = [ "https://www.example.com/users/:USER_ID/products/:PRODUCT_ID", "https://www.example.com/account/cancelOrder/:ORDER_ID", "https://www.example.com/order/:ORDER_ID/merge/:ORDER_ID",];window._uxa = window._uxa || [];for (var i = 0; i < referrers.length; i++) { window._uxa.push(["referrer:maskUrl", referrers[i]]);}The referrer value in the pageview request for https://www.example.com/ will be:
https://www.example.com/users/CS_ANONYMIZED_USER_ID/products/CS_ANONYMIZED_PRODUCT_IDhttps://www.example.com/account/cancelOrder/CS_ANONYMIZED_ORDER_IDhttps://www.example.com/order/CS_ANONYMIZED_ORDER_ID/merge/CS_ANONYMIZED_ORDER_ID
How referrer:maskUrl patterns match:
- Patterns must include the protocol and the host, for example
https://www.example.com/users/:user_id. - A pattern matches the beginning of the referrer URL. Path segments after the end of the pattern are kept.
- The query string is ignored for matching, and kept in the collected referrer. Use
referrer:removeQueryStringto drop it. - Literal segments are case-sensitive.
- Variable names are upper-cased to build the replacement:
:user_idbecomesCS_ANONYMIZED_USER_ID, and:userIdbecomesCS_ANONYMIZED_USERID.
Within query parameters
Section titled Within query parametersConsider this journey:
- Users navigate from
https://www.example.com/confirmation?firstname=jon&lastname=snowtohttps://www.example.com/. - A pageview is sent on
https://www.example.com/. - The referrer value in both the browser and in the pageview request is
https://www.example.com/confirmation?firstname=jon&lastname=snow.
To mask Personal data in the query string part of the referrer, use the referrer:removeQueryString command:
window._uxa = window._uxa || [];window._uxa.push(["referrer:removeQueryString"]);The referrer value in the pageview request for https://www.example.com/ will be:
https://www.example.com/?
Hiding the page title
Section titled Hiding the page titleOn projects that collect Product Analytics data, the page title is collected without any Personal Data redaction. If your page titles can contain Personal Data, such as an order number or a customer name, hide them with one of these methods:
- Add the
data-cs-hide-titleattribute to the<title>element. Thedata-heap-redact-textattribute has the same effect. - Push the
hidePageTitlecommand, for the next pageview only or for all the following ones. UseresetHidePageTitleto collect the title again.
<title data-cs-hide-title>Order #12345 for Jane Smith - Retail Store</title>The page title is then replaced by ****.
Removing Personal Data in API errors
Section titled Removing Personal Data in API errorsUse the networkRequest:maskUrls command to rewrite the URL before the tag collects the API error URL. The same patterns also apply to the network request URLs collected in Session Replay for performance timings.
The command takes as parameter one masking pattern (a string) or several (an array of strings) to apply to URLs containing Personal Data. A pattern matches any run of consecutive path segments in the URL: it can include the host, but it does not have to.
window._uxa = window._uxa || [];window._uxa.push([ 'networkRequest:maskUrls', <PATTERN>]);Patterns include the URL fragments to mask as variables prefixed by a colon.
Such variables are replaced by CS_ANONYMIZED_{FRAGMENT}:
:user_idbecomesCS_ANONYMIZED_USER_ID:addressbecomesCS_ANONYMIZED_ADDRESS:emailbecomesCS_ANONYMIZED_EMAIL
Call this command on all pages containing Personal Data which call the Contentsquare API, and before the main tag is loaded.
- Each call adds its patterns to the ones already registered. To clear all the registered patterns, use
networkRequest:maskUrls:reset. - Start each pattern with a literal segment, such as
order/:order_id. A pattern that starts with a variable can also match the host name. - The deprecated
api-errors:maskUrlcommand takes full URL patterns. When one of its patterns matches a URL, thenetworkRequest:maskUrlspatterns are not applied to that URL.
Example
Section titled Examplewindow._uxa.push([ "networkRequest:maskUrls", ["order/:order_id/merge/:order_id", "order/:order_id/item", "cancelOrder/:order_id"]]);| URL before masking | URL after masking |
|---|---|
https://api.net/order/12345/merge/67890 | https://api.net/order/CS_ANONYMIZED_ORDER_ID/merge/CS_ANONYMIZED_ORDER_ID |
https://api.net/order/12345/item | https://api.net/order/CS_ANONYMIZED_ORDER_ID/item |
https://www.api.nl/nl/ajax/nfs/account/cancelOrder/12345 | https://www.api.nl/nl/ajax/nfs/account/cancelOrder/CS_ANONYMIZED_ORDER_ID |
API error details
Section titled API error detailsWhen the collection of API error details is configured for your project, the request and response bodies, query parameters, and the headers and body elements configured for encryption are collected encrypted with your Controlled Exposure public key. If encryption is not available, these values are not collected.
Custom headers and request and response body elements that are not configured for encryption are sent in clear text, with email addresses masked. This includes the unencrypted response body elements used in the Errors Dashboard, Alerts, and Analysis Context (see Advanced troubleshooting). Standard headers are sent as-is. Do not configure body elements that can contain personal data as unencrypted.
Removing Personal Data in Clicked Element
Section titled Removing Personal Data in Clicked ElementWhen clicked element collection is turned on and data collection is active for the session, the tag collects the text payload of the elements being clicked as part of other analytics payloads (see /events request).
Clicked element collection is off by default. It is enabled per project, either for buttons and links only, or for any clicked element (see Target text). The text is truncated to 100 characters, and the whole text is dropped when:
- It contains an email address, a credit card number, or a sequence of 9 digits or more. When numbers are masked on the page, any digit drops the text.
- Any part of it is masked, encrypted, or not captured.
With No masking
Section titled With No maskingThe clicked element will be collected except if it is masked by data-cs-mask.
With Full masking or Partial masking
Section titled With Full masking or Partial maskingThe clicked element text will not be collected, except if the element whose text is collected (the clicked element, or its nearest button or link) is exposed with data-cs-capture or setCapturedElementsSelector, whether directly or through one of its ancestors.
You can also mask specific elements on the webpage. This masking takes priority over the default approach and page rules. For more information, see personal data selector section.
Controlled Exposure
Section titled Controlled ExposureThe Contentsquare Tracking Tag does not collect any text typed into input or textarea fields.
If you need to display the personal data of a user, such as shipping/delivery addresses, names, phone numbers, email addresses, or text inputs in a form field, use the collect and encrypt mode.
With the collect and encrypt mode, you can collect, store, and display text in input, textarea fields, or any other element within a page that is usually masked by the tag, before it is sent to Contentsquare's servers.
Collect and encrypt personal data from the collected HTML
Section titled Collect and encrypt personal data from the collected HTMLThere are two methods to collect and encrypt personal data from the collected HTML:
- Using the Contentsquare Encryption Selector
- Tagging DOM Elements
Encryption Selector
Section titled Encryption SelectorEach DOM element to be collected and encrypted is identified via its CSS selector. These selectors are pushed to the API via a JavaScript object. The content of the identified element is then encrypted and collected for Session Replays.
The following object that contains CSS selectors for text should be pushed to the API as follows, before firing the main tag:
window._uxa = window._uxa || [];window._uxa.push(["setEncryptionSelectors", "#cart, .total-items"]);Tagging DOM elements
Section titled Tagging DOM elementsThe data-cs-encrypt attribute can be added on an input field, textarea field, or any other element.
Instead of being masked, the text in the tagged element is encrypted, stored, and made available for exposing under specific conditions.
Encryption applies to all the text inside the tagged element, including the text of nested elements, of shadow DOM content, and of nodes added later. It also applies to the values of <input> and <textarea> elements:
- The values of
<input>elements with typepassword,number,checkbox, orradioare never encrypted: they stay masked. - Attributes are never encrypted: they are collected as they would be without
data-cs-encrypt.
Add the data-cs-encrypt attribute on the most granular elements that must be collected, so that you only expose the data you need.
<h2 class="welcoming"> Welcome back <span class="user_name" data-cs-encrypt>John Doe</span></h2>You can also add the data-cs-encrypt attribute with JavaScript. See When masking rules take effect.
Data Encryption
Section titled Data EncryptionTo use Controlled Exposure, obtain a combination of encryption keys:
- Public key: used by the Tracking Tag to encrypt the data
- Private key: used by the app for decryption
How to generate the key pair
Section titled How to generate the key pairOption 1: With a Google chrome script
Section titled Option 1: With a Google chrome script-
Copy the following script:
function arrayBufferToString(buffer) {const byteArray = new Uint8Array(buffer);let byteString = "";for (let i = 0; i < byteArray.byteLength; i += 1) {byteString += String.fromCodePoint(byteArray[i]);}return byteString;}crypto.subtle.generateKey({name: "RSA-OAEP",hash: "SHA-256",modulusLength: 4096,publicExponent: new Uint8Array([1, 0, 1]),},true,["encrypt", "decrypt"],).then((keysObject) => {crypto.subtle.exportKey("pkcs8", keysObject.privateKey).then((result) => {const privateKey = btoa(arrayBufferToString(result));console.log(`Private key: `, privateKey);});crypto.subtle.exportKey("spki", keysObject.publicKey).then((result) => {const publicKey = btoa(arrayBufferToString(result));console.log(`Public key: `, publicKey);});}); -
Paste the script in the Chrome console.
-
Press enter on the keyboard.
Option 2: With OpenSSL or OpenSSH
Section titled Option 2: With OpenSSL or OpenSSHOn linux/Mac, you can use OpenSSL:
openssl genpkey -out mykey.pem -algorithm RSA -pkeyopt rsa_keygen_bits:4096openssl rsa -in mykey.pem -pubout > mykey.pubOn Windows you will need to install WSL ↗.
How to add a public key
Section titled How to add a public key- Go to the Contentsquare Console > Choose the account > Choose the project > "Encryption management" tab.
- Select "Store public key".
- Paste your preferred public key.
- Select "Store key".
Unmask on-screen text in Contentsquare
Section titled Unmask on-screen text in Contentsquare- Select the key icon and select Unmask on-screen text
- Enter in the private key and select decrypt (all replays will be exposed)
User identifier
Section titled User identifierWith the User Identifier feature, you can search for replays belonging to specific users using a unique identifier. You can search and access all sessions associated with a specific user, for example john@gmail.com, within the selected date range. Use this feature in the case of user feedback or complaints.
The user identifier can be any value which uniquely identifies a user such as a username, an account number, email address, phone number, CRM ID, or loyalty system ID. In the browser, the tag trims the value, converts it to lowercase, and hashes it in a one-way format before it is sent to Contentsquare.
When filtering Session Replays with a user identifier in Contentsquare, the same hashing algorithm is used and both hashes are compared to match the user identifier with its associated replays.
User identifier implementation
Section titled User identifier implementation-
Choosing the right User Identifier: Opt for a unique identifier like the email address, username, or account number that is available on the front-end.
-
Collecting User Identifiers: To collect the user identifier, you need to send a page event using the
trackPageEventcommand, with the prefix@user-identifier@:window._uxa = window._uxa || [];window._uxa.push(["trackPageEvent", "@user-identifier@user_email"]);Or if you pull the identifier from the data layer:
window._uxa = window._uxa || [];window._uxa.push(["trackPageEvent", "@user-identifier@" + dataLayer.Name]);The user identifier value is limited to 100 characters. A longer value is rejected and not sent.

