Most articles on this topic are a list of reassurances. This one is a list of mechanisms, because under GDPR the mechanism is what you have to be able to describe when somebody asks.
Session replay does not record video. It records a serialized snapshot of the page, plus a timestamped stream of DOM mutations, mouse positions, scrolls, and input events. Replay rebuilds the page in a sandboxed iframe and plays the events back on a virtual clock. That distinction matters legally, because it means the question is never "is video personal data". The question is always: which specific fields left the visitor's browser, where did they go, who can read them, and how do you delete them.
Four things determine whether your setup is defensible. Everything else is detail.
1. Whether the sensitive values ever leave the browser
This is the single most important fact about any session replay tool, and it is the one most buyers never check.
There are two fundamentally different designs. In the first, the recorder captures everything and the vendor filters it on their servers. In the second, the recorder replaces the value with a mask in the visitor's browser, before any network request is made, so the real value never exists outside the device. Only the second one is data minimisation in the sense Article 5(1)(c) means it. In the first one, you have processed the data and are relying on a promise about what happened next.
The defaults are what matter here, not the capability. Every tool in this category can be configured to mask. The question is what it does on the day you paste the script tag and forget about it, because that is the state your site will be in for the next two years.
Filmroom masks every input by default. Not on the server, not on playback. The recorder substitutes the mask in the browser, so the typed value is never transmitted and never stored. To redact more than inputs, you mark elements in your markup: class="sr-mask" or data-mask hides text while keeping the layout intact, and class="sr-block" or data-block replaces the element with a placeholder so nothing inside it is recorded at all. Both apply to every descendant.
Two things follow from that design which are worth stating plainly, because they cut both ways. Masking in the browser means a misconfiguration cannot be fixed retroactively for sessions already recorded, since the data was never captured to begin with. And it means we cannot show you what we masked, because we do not have it.
2. Whether a third party is in the chain at all
Here is the part that has changed materially in the last two years, and it is not a GDPR question so much as a litigation-risk question that lands on the same desk.
US wiretapping suits under statutes like California's CIPA have been aimed at session replay specifically, and the element plaintiffs keep pleading is that the site transmitted the visitor's interactions to a third-party vendor without consent. Courts are genuinely split on whether replay meets the statute's interception element, and the split has not stopped settlements from landing. Ask your own counsel where your jurisdiction currently sits; we are not going to summarise live litigation for you.
We are not going to tell you that self-hosting makes you compliant, because it does not, and anyone who tells you a piece of software makes you compliant is selling you something. What we will say is narrower and checkable: if you host the recorder and the database yourself, there is no third-party vendor in the transfer chain, which is the specific factual element those complaints are built around. Your infrastructure provider is still a processor and still needs a data processing agreement. But the vendor-receives-your-visitors-keystrokes fact pattern simply does not exist in that architecture.
The same architecture removes the international transfer problem under Chapter V. If the container and the Postgres database are both in Frankfurt, there is no transfer to assess, no standard contractual clauses to maintain for the replay data, and no transfer impact assessment to redo the next time the adequacy framework is litigated.
Filmroom self-hosts as one Docker image plus one Postgres database. Migrations run on boot. There is no separate analytics store, no queue, and no worker fleet, which matters here for an unglamorous reason: the fewer systems the data is copied into, the fewer places you have to search when somebody asks you to delete it.
Filmroom is proprietary software, not open source. You can run it yourself and own the database. You cannot read the source. If independently auditable source code is a hard requirement for you, OpenReplay is AGPL v3 licensed and is the honest recommendation.
3. Whether you can actually answer an erasure request
This is where most session replay deployments quietly fail, and almost no vendor page discusses it.
Article 17 requires you to delete a data subject's personal data on request. To do that with session recordings, you have to be able to find every recording belonging to one person. If your recordings are keyed only by an anonymous session id, you cannot. You will be reduced to searching by IP address and hoping.
What makes this tractable is identity plus a durable per-visitor key. In Filmroom you attach an identity with window.layerbase.sr.identify({ id, name, email }) or with data-user-* attributes on the script tag (the Next.js guide shows how to resolve that on the server without shipping auth code to the client). Every recording then carries a visitor key derived from that identity (falling back to IP, then session id), and the dashboard has a single-visitor filter that pulls up everything one visitor did. That is the query an erasure request turns into.
Now the honest part, and it is the reason this section exists.
IP addresses are stored, and an IP address is personal data under GDPR. Filmroom stores the client IP on each recording. It is used for rate limiting on ingest, and for propagating a known identity to that visitor's earlier anonymous sessions from the same address. You should assume it is in scope for a DSAR and you should account for it in your records of processing. You can label an address with a human-readable alias, and you can add an address to a do-not-record list so nothing from it is ever stored again, but the field exists and pretending otherwise would be dishonest.
Deleting a recording does not by itself delete everything derived from it. This is a deliberate design decision and you need to know about it. Filmroom extracts a few things out of the raw event stream at ingest so they survive the recording being pruned: front-end error occurrences, conversion goal hits, and AI session annotations. Each of those rows keeps a small snapshot of who and where (identity, IP, landing page) so that a conversion or a bug report does not silently vanish from your analytics when the recording behind it ages out. That is the right default for analytics and the wrong default for an erasure request, so a complete erasure has to cover those tables too.
The saving grace is that they are ordinary rows in your Postgres database, keyed by the same visitor identity, so a complete erasure is a query you can write, run, and evidence. Compare that with a hosted tool, where a complete erasure is a support ticket and a promise. We would rather document the extra tables than let you discover them during an audit.
Storage limitation is configurable and you should configure it. Article 5(1)(e) says you keep personal data no longer than necessary, which means an unbounded recording archive is a problem whatever else you do right. Filmroom gives you an age-based retention limit in days, a total storage cap, and low-value session disposal thresholds (discard sessions under a wall-clock duration, or under an amount of genuinely active time). Set them deliberately, write the number down in your retention policy, and let the pruner enforce it.
One more data minimisation detail worth knowing, because it is unusual: Filmroom derives a visitor's country without any IP geolocation lookup. The recorder reports the browser's time zone and language, and the country is inferred from those client-side. There is no external geo-IP service in the request path, so there is no additional processor to disclose and no additional data leaving your infrastructure to get a flag icon on a dashboard.
4. Whether recording is gated on consent, and gated properly
Whether you need consent for session replay depends on your jurisdiction, your lawful basis, and what you capture. What is not in dispute is that if you decide you need it, the gate has to actually work, which means nothing is captured until the gate opens. A recorder that captures and then discards has already processed the data.
Filmroom gives you three separate levers and it is worth understanding which one to reach for:
- Gate the script. The recorder is a plain script tag. If you only inject it after your consent manager reports a positive signal, nothing loads and nothing is captured. This is the correct mechanism for consent and it is the one to use. Note plainly: Filmroom has no built-in integration with a specific consent platform, so wiring the tag to your CMP is your code.
- Gate the page.
data-allow and data-deny accept URL globs and decide whether a page records at all. A pattern starting with / matches the path (/checkout/*), anything else matches the host (admin.*), so you can exclude a whole subdomain. A denied page loads no recorder and makes no network request whatsoever. Useful for a route you have decided is never recordable, such as a payment step or an internal admin area. - Gate the visitor. A do-not-record list matches on IP address, on the email from
identify(), or on an identity id. Matching batches are dropped at ingest before anything is stored. This is the mechanism for honoring an individual objection under Article 21 going forward.
Automatic detection of sensitive data you forgot to mask
Every masking configuration is eventually wrong, because somebody ships a new page and does not know the convention. So Filmroom scans the rendered textof recorded pages (never inputs, those were already masked in the browser) for patterns that indicate a leak: email addresses, Luhn-valid card numbers, dashed US social security numbers, and prefixed secret tokens. Anything found is filed as a redaction flag with a masked sample and a best-effort CSS selector, and rolled up on one screen, deduplicated by selector, so you see "this element on this template is leaking" once rather than once per visitor. You fix the page, mark it resolved, and it drops off the list.
Phone numbers are deliberately not detected. The false positive rate on loose digit sequences is hopeless and a detector that cries wolf gets ignored, which is worse than not having one. We would rather tell you that than claim coverage we do not have.
You can also click any element in a replay to flag it as sensitive, which captures its selector into the same rollup. That is the workflow for the leaks a regex will never catch.
How the tools compare on the facts that matter
Every cell below traces to vendor documentation, linked under the table. Where a fact is not published we say so rather than guessing.