Why session replay masking is the hard part of self-hosting
A regulated team that wants to watch real user journeys faces a hard constraint: the replays must show where people get stuck without capturing card numbers, health details, account balances or the contents of forms. Self-hosting OpenReplay or PostHog keeps those recordings inside your own infrastructure, which matters for vendor risk and data location. It does not, by itself, remove personal data from the recordings. If the browser sends a balance or a diagnosis to your server, your server now stores it, whoever operates the box. That is why masking at capture, before data leaves the browser, is the control that matters, and why it should be designed and tested as a product requirement rather than left to the default settings.
The short answer is that OpenReplay's README describes capture-time sanitization that masks or skips data before it reaches the server, including masking by CSS selector and input redaction, plus a private mode that masks all text and inputs by default. Private mode is the safer starting point for regulated screens, with selective unmasking only for elements a reviewer has approved. PostHog's session replay sits in the same product family and needs the same configuration discipline, but the masking settings you rely on are part of your own configuration, so confirm them against the current PostHog documentation for your version. Before you deploy either tool, check the license terms for any feature you plan to use: the PostHog README states that premium features in its ee/ directory on a self-hosted instance require a PostHog license, and OpenReplay's Enterprise Edition under ee/ is covered by a separate commercial license.
Which privacy requirements shape the masking design
This table maps each privacy requirement that affects session replay to the source it comes from and the design decision it forces. Read it top to bottom as a build checklist, then confirm each row against your own data classification and legal review. The main takeaway is that masking must be enforced in the browser and verified after every front-end release, because server-side cleanup cannot recover data that has already been captured.
| Requirement | Source of the requirement | What it means for the build |
|---|---|---|
| Sensitive values masked before they leave the browser | Internal data classification policy and the principle of minimizing personal data collected | Configure capture-time masking so values are redacted in the client SDK. OpenReplay documents sanitization that runs before data reaches the server, so confirm the equivalent behavior in your chosen platform and version. |
| Input fields redacted by default | Internal policy for form data such as account numbers, identifiers and health or financial entries | Require input redaction on all form fields, then allow specific non-sensitive fields to be unmasked by explicit allowlist rather than by default. |
| Private-by-default mode for sensitive flows | Internal risk assessment for login, onboarding, payment, benefits, or clinical pages | Enable a mode that masks all text and inputs on high-risk routes. Record which routes use it and review the list whenever pages are added. |
| Masking by CSS selector maintained as code | Engineering standard for change control and the reader's own privacy review | Keep selector rules in version control with owners. Selectors break when markup changes, so each rule needs a test that fails when a sensitive element is no longer covered. |
| Network request and response bodies filtered | Internal data minimization rule covering API payloads that carry personal or account data | Drop or redact sensitive fields in captured network bodies and headers, including authorization tokens and cookies. Confirm which network data the platform records by default. |
| Masking survives front-end releases | Internal release and change management controls, and the assessor's expectation of ongoing control operation | Add automated masking tests to the release pipeline that load sensitive pages, record a session and check that protected values never appear in captured output. |
| Access controls on replay viewing | Internal access policy and the principle of least privilege for recorded user activity | Restrict who can view recordings, log each view, and separate replay access from general analytics access. Confirm role and audit features in your edition, since some are sold as paid packages or enterprise options. |
| Self-hosted data residency for recordings | Internal data location policy and any contractual or sector limits on third-party processing | Keep replay storage inside your own infrastructure. Document where recordings, backups and logs are stored and who can reach them. |
How capture-time masking works in OpenReplay and PostHog
OpenReplay's README describes capture-time sanitization that masks or skips data before it reaches the server, so the goal of masking is to keep sensitive values out of the recording pipeline entirely, not only hide them in the player. The README names three controls: masking by CSS selector, input redaction, and a private mode that masks all text and inputs by default. Private mode is the strongest starting point for regulated applications because it changes the default from capture to suppression, and teams then open up specific, reviewed elements.
- Selector-based masking: Mark elements that display account numbers, balances or personal details with the masking attribute or selector your SDK version expects, and keep those selectors in version control.
- Input redaction: Confirm that password, card and identifier fields are redacted at capture, not only blurred in the player.
- Private mode: Enable the default-masked mode for pages that handle personal or financial data, then allowlist only elements with a documented reason.
- Self-hosted storage: Because sanitization runs before data reaches the server, the masked output is what your infrastructure stores, which narrows the access review.
Before relying on these controls, check the masking configuration against the SDK and server versions you run, using the OpenReplay deployment documentation as the reference.
PostHog's session replay offers masking options that the team must verify in the current documentation for its own installed version, because option names, defaults and behavior change between releases. Treat any setting described here as a checklist item, not a confirmed default. The aim is to confirm that text, inputs and selected elements are masked at capture in the browser, and that the configuration is enforced in the build that ships to users.
- Text and input masking: Check whether the SDK masks all text and inputs by default in your version, and whether you can override that per page or element.
- Element-level controls: Confirm the attribute or class names used to block or mask specific elements, and test them on your real markup.
- Configuration location: Confirm whether settings live in the project, in the SDK initialization code or in both, and who can change them.
- Self-hosted scope: Self-hosted deployments are officially unsupported by PostHog for product and infrastructure questions, so your own test evidence carries more weight.
Confirm each setting against the installed version of your PostHog instance and SDK before you sign off masking for production.
Element-level masking works on the DOM that the SDK can read. Some content sits outside that reach, and masking settings on either platform will not hide it. Teams often assume that a configured mask covers the whole screen, and that assumption is where leaks happen. Map these surfaces during design, decide whether each one should be avoided or excluded, and record the decision.
- Canvas rendering: Text drawn on a canvas element is not DOM text, so selector masks may not hide it. Keep sensitive values out of canvas-based views.
- PDFs and embedded documents: Documents shown in a browser viewer or embedded object are typically outside the page's DOM and need their own control, such as excluding the page.
- Third-party iframes: Content from a payment, identity or support widget runs in its own frame, so the parent page's masking may not reach it. Exclude those frames or confirm they are covered by their own controls.
- Network payloads: Masking the display does not filter request and response bodies. Network payload filtering must be confirmed per platform, including which fields are dropped before transmission.
For each surface on the sensitive pages, record whether it is masked, excluded or avoided, and retest it after every front-end release.
Which self-hosted platforms fit a masking-sensitive program
Two self-hosted options dominate this use case, and each brings a different licensing split and a different set of paid features. The cards below cover what each platform documents about capture-time masking, where its license boundary falls, and which decisions need to be settled before a masking design is signed off.
OpenReplay core and masking features
OpenReplay's README documents capture-time sanitization that masks or skips data before it reaches the server, including masking by CSS selector, input redaction and a private mode that masks all text and inputs by default. Most of the monorepo is AGPLv3, and some directories are MIT. Check that your deployment only uses the code under those licenses, and test the masking rules against your own pages, since a README describes features, not your configuration.
OpenReplay Enterprise and the ee directory
Everything under the ee/ directory falls under a separate commercial license, defined in ee/LICENSE. The self-hosted Enterprise edition adds SSO and SAML, SCIM provisioning, roles and permissions, an audit trail, a custom SLA and dedicated support, sold on custom pricing with multi-year contracts. Decide whether access controls over who can view recordings need those features, and ask the vendor which capabilities sit in ee/ before you design around them.
OpenReplay single-VM starting point
The README states that a single VM with 2 vCPUs, 8 GB of RAM and 50 GB of storage is enough to start self-hosting, with targets including Docker, Kubernetes with Helm and several major clouds. That is a starting point for a test environment, not a sizing for production replay volume. Confirm storage, retention and the staging location of raw recordings before a masking rule is promoted to production.
PostHog license split and posthog-foss
PostHog's repository is MIT except for the ee/ directory, which is covered by a separate license. Premium features from ee/ on a self-hosted instance require a PostHog license. Teams that need fully free software should check this split first, then consider the posthog-foss repository, which the README says is purged of proprietary code and features. Verify which masking and privacy controls you need are present in the code you are permitted to run.
PostHog hobby deployment and support limits
PostHog offers a free Docker Compose hobby deployment under the MIT license, installed with a deploy-hobby script, and recommends a server equivalent to 4 vCPU, 16 GB of RAM and more than 30 GB of storage. Its docs state that self-hosted deployments are officially unsupported and that PostHog does not provide customer support for product or infrastructure questions on self-hosted instances. Any masking test and patch cycle you rely on is your responsibility or your implementation partner's.
PostHog volume guidance and managed regions
The README says open-source deployments should scale to approximately 100k events per month and recommends migrating to PostHog Cloud above that. Because the hosted option has US (Virginia) and EU (Frankfurt) regions, a team with high replay volume or residency rules can compare self-hosting against a managed region. Decide where masked recordings must reside, then size the deployment against that decision.
How a masking rollout moves from inventory to recurring tests
This sequence turns masking from a configuration setting into evidence: every sensitive field is mapped to a control, proven in staging and in production replays, and re-tested each time the front end ships. Each phase produces a record that a security or privacy reviewer can check.
Inventory screens and fields carrying sensitive data
Walk every page, form, modal and API response in the user journey with product, front-end and privacy owners, and list each field that can hold personal, financial or health data. Include content that users type, content the page renders from the back end, and values that appear only after login or in error states. The output is a screen-by-field register, reviewed by the privacy lead, and it is done when the front-end team confirms no route in the recording scope is missing from the list.
Map each field class to a masking control
Group the register into field classes such as account identifiers, balances, contact details, free text and health information, then assign each class a control: masking by CSS selector, input redaction, skipping the element entirely, or private-by-default mode for whole screens. The policy document records the control, the owner and the reason for each class. It is complete when every row in the register points to exactly one control and the privacy lead has signed it.
Deploy a staging instance and seed synthetic data
Install the self-hosted platform in a non-production environment that matches the production topology as closely as the team can confirm, then load the front end with synthetic accounts that carry realistic-looking but fictional values for every field class. Configure the sanitization rules from the policy, including the selectors and input settings. The output is a staging deployment with the policy applied and a list of the synthetic values planted for testing.
Audit captured frames against rendered sensitive text
Run scripted journeys with the planted synthetic values, then compare what the browser rendered with what the replay stored. Check the recorded frames, console and network captures, and any event properties for the planted strings. Any match is a defect that goes back to the policy or the selector configuration. The audit is done when every planted value is absent from stored data across all tested screens, and the results are saved as a dated report.
Configure access, retention and network filtering
Set role-based access so that only named groups can open recordings, decide the retention period with the privacy lead, and filter network payloads so that request and response bodies from sensitive endpoints are not stored. Confirm these settings in the platform's own administration screens and record them in the configuration baseline. The step closes when a test user without the right role cannot open a recording and the baseline is committed to version control.
Go live with a signed sign-off record
Enable capture in production for the agreed scope and in a limited first wave where the team can watch for problems. Repeat a short version of the capture audit against production replays using test accounts, then gather sign-off from security, privacy and product owners. The sign-off record lists the policy version, the configuration hash, the audit results and the scope enabled, and it is the document an assessor or internal reviewer will ask to see first.
Gate every front-end release with masking tests
Add the synthetic-data journey and the frame comparison to the front-end release pipeline so that a build fails when a planted value reaches a stored replay or a new screen lacks a mapped control. Route new screens through the inventory step before they ship. The gate is working when a deliberately introduced unmasked field is caught in a pre-production run and the failure is traced to the release that introduced it.
Evidence a privacy reviewer will ask for and where it comes from
This table maps each piece of evidence a privacy reviewer or assessor typically requests for a masked session replay deployment to the system or process that produces it. Use it to plan what your team must export, version and retain before an review begins. DeployOpen prepares these artifacts, but auditors, privacy regulators and your own legal function make the determination of whether the controls are adequate.
| Evidence item | What it shows | Produced by | Where it usually falls short |
|---|---|---|---|
| Masking configuration export | Which selectors, inputs and text nodes are masked or skipped at capture, and whether private mode is on | Replay platform project settings, exported and stored under change control | Exported once at launch and never refreshed after later edits |
| Masked and excluded selector list per release | Which elements were covered by each front-end build that went to production | Release pipeline that snapshots the selector inventory alongside each build | Selectors renamed in a refactor silently stop matching |
| Masking regression test reports | That sensitive fields stay masked in replays after each front-end release | Automated browser tests run in the CI pipeline against a staging replay instance | Tests cover a few pages and miss payment, profile or form screens |
| Network payload filter rules | Which request and response bodies or query parameters are dropped before leaving the browser | Replay SDK configuration and its version history | Rules cover the main API but not third-party or newly added endpoints |
| Retention settings | How long recordings and derived events are kept, per project and per data class | Replay platform retention configuration and the storage lifecycle policy | Retention is set for the default project but not for separate environments |
| Viewer and admin access logs | Who opened which recordings, when, and which administrative changes were made | Platform audit trail where the edition provides it, plus identity provider logs | Audit logging is an enterprise feature in some editions, so confirm availability before design |
| Change history of masking rules | Who changed masking rules, what changed, and the approval recorded for it | Version control for the configuration, linked to tickets or change approvals | Changes made in a web console without a corresponding commit or ticket |
| Records of processing entry | Purpose, categories of data, recipients, retention and safeguards for session replay | Privacy office, drawn from the configuration, retention and access evidence above | Entry describes the tool but not the masking rules actually in force |
What teams ask before configuring replay masking
How much effort does replay masking take, and what drives it up?
The effort depends mostly on how many screens and components handle sensitive data, how many custom components sit outside standard form fields, and how often the front end ships. Each screen that shows balances, identifiers, health details or free text needs a masking decision, and each custom input widget may need its own selector rule. A capture audit on a sample of real user journeys shows how many elements need attention before anyone writes configuration. Teams with a small set of screens and a stable release cadence usually spend far less time than teams with dozens of flows and weekly deploys. Ask any partner to quote against a written screen inventory and a stated release rhythm, not a general estimate. Implementation effort is largely one-time, while re-checking masking after each release recurs.
What does our team need to supply before masking work starts?
Three inputs make the work faster and safer: a screen inventory, a data classification, and named owners who sign off on what gets masked. The screen inventory lists every route, modal and flow that the replay tool will record, including logged-in areas and embedded third-party frames. The data classification states which fields are personal, financial, health or credential data, and which can be captured for product analysis. Owners, usually a product lead, a privacy or compliance representative and a front-end lead, approve the rules so that nobody loosens masking later without review. Without sign-off, masking rules tend to drift as engineers add features. Confirm these artifacts with your privacy office before the engagement begins, since they define what counts as sensitive in your organization.
Can our in-house team configure replay masking without an outside partner?
Yes, if the team can run a capture audit and owns the front-end release process. Configuring capture-time masking by CSS selector, redacting inputs and enabling a private-by-default mode are engineering tasks that a competent front-end or platform team can learn from the vendor documentation. The harder part is proving the result: someone must replay real sessions, confirm that card numbers, balances and free text never appear, and repeat that check whenever the interface changes. If no one on the team has time for that cycle, or if the organization needs independent evidence for an assessment, an implementation partner can take on the audit and documentation. Compare the internal headcount cost with the cost of a fixed-scope engagement, using the scope and the release frequency as the basis.
What changes in masking after each front-end release?
Every release can add, rename or restructure elements, which can silently break a masking rule. A selector that matched a balance field may stop matching after a component refactor, and a new widget may render sensitive text without any rule at all. The practical response is a regression step in the release pipeline: replay a fixed set of test journeys against a staging environment, check that masked elements still show placeholders, and review any new screens before they reach production. Record which build each check covered so that the evidence trail is clear. Cadence matters here, so a team that deploys several times a week needs an automated check, while a team that deploys monthly may manage with a scripted manual review. Confirm with your engineering leads how releases are gated today.
Does self-hosting session replay satisfy our privacy duties?
No. Self-hosting keeps session data in your own infrastructure, and OpenReplay's documentation notes that first-party tracking from your domain is not blocked by ad blockers, but hosting location does not decide whether collection is lawful, proportionate or properly limited. Privacy duties still require you to decide what is captured, to mask sensitive data before it leaves the browser, to set retention limits, to control who can view recordings, and to honor access and erasure requests where they apply. Consent obligations may also apply in some jurisdictions, depending on the data and the purpose. Self-hosting reduces third-party transfer risk, which is one input among several. Review these duties with your privacy counsel, and treat hosting as only one control in the design.
How are retention periods and viewer access set for recordings?
Retention and access are configured at the platform level and should be decided before recording starts, not after data accumulates. Retention needs a written period for each category of recording, a deletion method, and confirmation that the platform actually removes expired replays. Access needs role-based permissions, so that only staff who need to diagnose a journey can open a recording, and an audit trail of who viewed what. Open-source editions may offer self-managed retention with community support, while enterprise editions list features such as SSO, SAML, SCIM provisioning, roles and permissions, and audit trail. Confirm in writing which of these your chosen edition provides, because access controls are often part of a paid tier, and verify them in a test environment before go-live.
What evidence should we keep to show masking works?
Keep a masking register, dated capture audit results, and release-level test records. The register lists each masked selector or field, the data class it protects, the owner who approved it and the date it was last verified. Audit results show which journeys were replayed, what was visible and what was masked, with screenshots or notes stored in a controlled location. Release records tie each verification to a build number, so you can show that the current version was checked. These artifacts help internal reviewers and external auditors assess controls, but they do not constitute an audit or certification. Ask your partner to define the evidence format up front, and confirm with your assessor which artifacts they expect.
What to ask an implementation partner about masking
Start by asking how the partner proves masking works, not only how it is configured. A masking setting that exists in a config file is not evidence that the rendered replay hides the value. Ask for the test method: whether they load seeded test accounts with known card numbers, balances and health or identity data into the real pages, record sessions, and then search the stored replay and the captured network payloads for those strings. Ask which pages are covered, including payment confirmation, account settings, search results and error states, and whether the test runs in each browser and screen size you support. Ask who keeps the test data and who can read the results.
Then ask what happens when the front end changes. Masking by CSS selector breaks quietly when a developer renames a class or moves a field into a new component, so the partner should describe how a front-end release triggers a masking regression run before it reaches production, who reviews the failures, and who has authority to block a release. Ask for the named approver on your side and on theirs. Also ask how new fields are classified, since a field nobody tagged is captured by default. For fixed-scope work, ask whether masking maintenance after go-live is covered by the agreed scope or by a separate retainer, and what the written scope says about that.
Ask how the fixed scope defines masking coverage. The scope should list the pages, flows, selectors, input types and network endpoints in or out of capture, and it should state what is excluded, such as third-party embeds, cobrowsing sessions or mobile apps if those are not part of the work. Ask for the vendor edition in writing: PostHog's README states that premium features from its ee directory need a PostHog license, and OpenReplay separates its open-source edition from Enterprise features such as SSO, SCIM and audit trail, so confirm which masking and access controls each edition provides. Finally, ask how they document configuration for reviewers: a versioned register of masking rules, the test evidence for each release, and who on your team is trained to read it. PostHog's self-host documentation says it does not provide customer support for self-hosted instances, so ask what support the partner provides instead and in what hours.
How DeployOpen scopes and supports masked session replay
DeployOpen plans, implements, hardens and documents self-hosted session replay inside your own cloud accounts, data centers or air-gapped networks. For masking work, the focus is on proving that sensitive values are removed in the browser before any recording leaves the page, and on keeping that proof valid through every front-end release.
How we scope
- The written scope lists which pages, flows and form fields need masking, and who signs off on that list.
- Scope covers masking rules by CSS selector, input redaction, private-by-default settings and network payload filters.
- Engagements are fixed-scope, with deliverables and timelines agreed in writing before work starts.
How we build
- We deploy the chosen platform, such as OpenReplay or PostHog, inside your infrastructure and configure capture-time sanitization.
- We write masking rules as versioned configuration and test them against sample pages that contain dummy card numbers and balances.
- We document each rule, its owner and the release it was last verified in, so reviewers can trace every masked field.
How we hand over
- You receive the deployed configuration, runbooks, a masking test suite and a record of what each rule covers.
- Handover includes a checklist for front-end teams to follow when they add or rename fields, pages or third-party scripts.
- We confirm with your teams which settings, access roles and retention limits remain under your control after go-live.
How we support
- Ongoing support, upgrades and continuous testing are available as retainers, including re-running masking tests after front-end releases.
- Where the platform's own support does not cover self-hosted deployments, we agree in the scope who handles fixes and upgrades.
- Support terms are set in the written agreement, so you know who responds to a masking gap found in production.

