filmroom

Filmroom/Privacy guide

Session replay and GDPR: what actually has to be true

A practitioner's guide to running session replay under GDPR. What counts as personal data, which masking defaults matter, how erasure requests actually work, and where self-hosting helps.

Not legal advice

This page explains how session replay technology works and which of its properties are relevant to GDPR. It is written by engineers, not lawyers. Have counsel review your specific setup.

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.

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.

Scroll the table sideways to compare.

Session replay tools compared on masking defaults, self-hosting, licensing and EU region
Compared onInputs masked by defaultText masked by defaultSelf-host the productOpen sourceEU region
FilmroomYes, in the browserNo, opt in per elementYes, one container plus PostgresNo, proprietaryYour choice, you pick the host
FullStoryPassword and card fields excluded; full Private by Default mode must be requested from supportNo, not unless Private by Default is enabledNot offeredNoYes, EU1 in Frankfurt, and existing accounts cannot migrate to it
HotjarYes, keystrokes suppressed client-side unless allowlistedNoNot offeredNoNot confirmed
LogRocketNo. inputSanitizer defaults to false; only password fields are always excludedNo. textSanitizer defaults to falseEnterprise tier only, quote requiredNoNot confirmed
PostHogYesNo, their docs say general text is not masked by defaultYes, but officially unsupported, and all paid features are cloud onlyYes, MIT with an ee/ carve-outYes, Frankfurt
Microsoft ClarityYes, in all modes, and not configurableConfigurable by modeNot offered; the MIT capture library is not a deployable productCapture library onlyNot confirmed
OpenReplayYes, defaultInputMode is obscuredEmails obscured by default, numbers notYesYes, AGPL v3Self-host anywhere

Cells marked "Not confirmed" are ones we have no first-party source for, so we state nothing rather than guess.

Sources: FullStory Private by Default, FullStory data residency, Hotjar keystroke suppression, LogRocket DOM reference, LogRocket privacy, PostHog replay privacy, PostHog self-host disclaimer, Clarity FAQ, OpenReplay sanitize data.

A checklist you can actually work through

  1. Write down your lawful basis and, if it is legitimate interest, do the balancing test and keep it. Legitimate interest is not a safe default for behavioural recording; it is a conclusion you have to show your work for.
  2. Confirm what your tool masks by default, from its documentation, not its marketing page. If inputs are not masked by default, that is a configuration task with a deadline, not a note.
  3. Decide which routes are never recordable (payment, account settings, anything showing another person's data) and block them at the recorder, not in review.
  4. Set a retention period, write it in your privacy notice, and make the software enforce it.
  5. Make sure you can find one person's recordings in one query. If you cannot, attach an identity.
  6. Enumerate every table that holds a copy of the who and where, not just the recordings table, and write the erasure query against all of them.
  7. Update your records of processing and your privacy notice to mention session recording and IP address storage specifically.
  8. If you are transferring outside the EEA, decide whether you would rather solve that with paperwork or by not transferring.

Frequently asked questions

Is session replay GDPR compliant?

The technology is neither compliant nor non-compliant by itself. Your deployment is what gets assessed. In practice a defensible deployment has four properties: sensitive values are masked in the visitor's browser before transmission, you have a documented lawful basis, recordings are retained for a defined and enforced period, and you can locate and delete one person's recordings on request. A tool can make each of those easy or hard, and that is what to evaluate it on.

Do I need consent to use session replay?

It depends on your jurisdiction, your lawful basis, and what you capture. Many organisations conclude they need consent for behavioural recording, particularly in the EEA and UK, and the safe implementation is to not load the recorder until consent is given. With Filmroom that means injecting the script tag conditionally, since the recorder cannot capture anything if it never loads. Ask counsel about your specific case rather than relying on a vendor opinion.

Does self-hosting session replay make me GDPR compliant?

No. It removes a processor from your chain and it can remove an international transfer, which takes real work off your plate, and it makes erasure requests something you can execute and evidence yourself. It does not give you a lawful basis, write your privacy notice, or set your retention period. Any vendor claiming their software makes you compliant is overselling.

What personal data does a session recording actually contain?

A serialized copy of the pages the visitor saw, their interactions with those pages, and metadata: IP address, user agent, referrer, browser time zone and language, and any identity you attached yourself. The page content is the part people underestimate, because a logged-in page can render plenty of personal data as ordinary text, which is why text masking and route blocking matter as much as input masking.

How do I delete one visitor's session recordings?

Attach an identity with identify() so recordings carry a durable visitor key, then filter the dashboard to that visitor and delete. When self-hosting, also delete the derived rows that keep a who-and-where snapshot by design (error occurrences, conversion goal hits, and AI annotations), which is a query against your own Postgres. Filmroom documents those tables specifically so a complete erasure is possible rather than approximate.

Does Filmroom send data to any third party?

When self-hosted, no. The recorder posts to your own ingest endpoint and everything is stored in your Postgres. Country is derived from browser signals in the browser, so there is no geo-IP service in the path. The one exception is the optional AI layer: if you enable it, you supply your own Anthropic or OpenAI key and reduced session transcripts are sent to that provider under your own account and your own agreement with them. Leave the key unset and the AI features are inert and nothing is sent anywhere.

Are inputs masked before they are sent, or after?

Before. The substitution happens in the visitor's browser, so the typed value is never transmitted and never stored. This is worth confirming for any tool you evaluate, because server-side filtering means the value was transmitted and you are relying on a promise about what happened to it next.

Point Filmroom at your site and read what it finds.

Every input masked by default, replays that stay self-contained, and an AI layer that tells you which sessions were worth watching.