Session replay for QA: rrweb, PostHog, and FullStory compared
A support ticket says "the Continue button does nothing". You open staging, click Continue ten times, and it works every time. Then you watch that user's session replay and see it in four seconds: a cookie banner is sitting invisibly on top of the button, swallowing every click.
Session replay is a recording of what happened in a user's browser. It turns "I can't reproduce it" into "I watched it happen".
Short version
- Session replay records the page as a stream of DOM changes, not as a video file.
- rrweb is the open source library that PostHog and many other tools build on.
- PostHog gives you replay next to product analytics; FullStory gives you replay next to search and funnels.
- Privacy masking, retention length, and pricing model differ more than the playback features do.
- Replay shows the browser only. It cannot show server state, database rows, or the text inside masked fields.
What session replay actually records
Most replay tools do not record your screen. They record the DOM, which is the browser's live structure of the page.
The recorder takes one full snapshot of the page when the session starts. After that it records only the changes: a class was added, a text node changed, an element was removed. It also records mouse movements, clicks, scrolls, key presses, and page navigations.
Playback rebuilds the page from those changes and replays them on a timeline. That is why replay files are small compared to video, and why you can usually select text inside a replay.
This design has one consequence that matters for QA. Anything that never touched the DOM was never recorded.
The three tools at a glance
| rrweb | PostHog | FullStory | |
|---|---|---|---|
| What it is | Open source recording library | Product analytics suite with replay | Experience analytics suite with replay |
| Who runs it | You | Cloud or self-hosted | Vendor cloud only |
| Setup effort | High. You build storage and a player | Low. Add the SDK, turn replay on | Low. Add the script |
| Default privacy | Records everything except password inputs | Masks all text inputs by default | Masks input values by default, with rules |
| Retention | Whatever your storage allows | Fixed windows by plan | Fixed windows by plan |
| Pricing model | Free code, you pay for storage | Per recording captured | Per session or seat, quoted |
| Best for | Custom needs and strict data rules | Teams already using PostHog events | Larger teams with support and CX users |
rrweb: the open source engine
rrweb is a JavaScript library that records and replays DOM changes. It is free and it is the foundation many commercial tools use, including PostHog.
You get the recorder and the player. You do not get storage, search, user identification, or access control. You build those.
Pick rrweb when your data cannot leave your own servers, or when you need to record something unusual, such as an internal admin tool with no seats to buy. Expect real engineering work: a session can produce megabytes of events, and someone has to own that pipeline.
PostHog: replay next to your events
PostHog is a product analytics tool that also records sessions using rrweb under the hood. Its strength for QA is the link between events and video.
If you track an event called checkout_failed, you can filter to sessions that fired it and watch only those. You can jump straight to the moment the event fired instead of scrubbing a twenty-minute session.
PostHog also offers a self-hosted option, which matters when a compliance team says recordings cannot sit in a vendor's cloud.
FullStory: replay next to search
FullStory records sessions and indexes what happened inside them, so you can search for behaviour rather than only for events you remembered to instrument.
For example, you can look for sessions containing a rage click, which is when a user clicks the same element several times in a few seconds because nothing responded. That single filter often finds broken buttons before any ticket arrives.
FullStory is the heaviest of the three in terms of setup politics. It is a vendor-hosted product bought by a team, not a library you drop in, and pricing is quoted rather than published.
Privacy: what gets masked, and who decides
Replay is a recording of real people using your product. Treat it as personal data.
Every tool offers three levers:
- Input masking. Replace typed characters with asterisks so the value is never sent.
- Element blocking. Skip an element entirely, so it appears as a grey box.
- Network body capture. Off by default in most tools, because request bodies often hold tokens and personal details.
The defaults differ, and that is the part teams get wrong. rrweb records text and inputs unless you tell it not to. PostHog and FullStory mask input values by default and expect you to open up the fields you actually need.
Bad: unmask a whole checkout form so testers can see what users typed, including the card field.
Good: unmask the country and coupon fields only, and add
data-ph-no-capture(PostHog) or the equivalent class on everything else.
Two rules that keep you out of trouble:
- Mask by default, then open specific fields. Never the other way around.
- Put anything sensitive behind an element block, not an input mask. Masking protects the value, blocking protects the whole element, including labels and error text.
Retention: how far back can you actually go
Retention is how long a recording stays before it is deleted. It decides whether replay helps with the bug you have today.
Hosted tools sell retention in fixed windows tied to your plan, often a month or a few months, with longer windows on higher tiers. With rrweb you set retention yourself, because it is just files in your own storage.
A bug reported on Monday about "last Tuesday" is fine on any plan. A regression traced back to a release six weeks ago is not. Before you promise a stakeholder that replay will answer a question, check the retention window and check whether the session was sampled at all.
Sampling means recording only a share of sessions, such as 10%, to control cost. If sampling is on, the specific session you want probably does not exist. For QA work, keep sampling at 100% on staging even if production is sampled.
Pricing models and where the bill surprises you
The three tools charge on different units, so a like-for-like comparison of headline numbers is meaningless.
- rrweb costs nothing to license. You pay for storage, bandwidth, and the engineer who maintains it.
- PostHog charges per recording captured, with a free monthly allowance. Cost scales with traffic and with how many sessions you choose to record.
- FullStory charges on quoted plans tied to session volume and seats. You talk to sales.
Two things inflate bills in practice. The first is recording every session on a high-traffic marketing site that nobody ever debugs. The second is turning on network payload capture globally, which multiplies the data per session.
A cheap setup for a QA team: record 100% on staging, sample production, and record 100% only for logged-in users who hit checkout.
What replay cannot show you
This is the part that causes wasted hours, so be blunt about it in your team.
Server state. Replay shows the response arrived and the page rendered an error. It does not show why the API returned 500. It cannot show you the queue depth, the feature flag the server evaluated, or the row that was locked.
Masked input values. If a field is masked, the characters are gone. You will see that the user typed nine characters into the postcode field, not which nine. That is the trade you agreed to when you enabled masking.
Anything outside the page. Browser extensions, native dialogs, print previews, the operating system file picker, and other tabs are invisible. A replay that "freezes" for eight seconds is often a user staring at a native dialog you cannot see.
Canvas, video, and some iframes. Pixels drawn on a <canvas> element are not DOM changes. Many tools skip them or record them at low quality and high cost. Cross-origin iframes are usually blank.
Real timing. Playback timing is reconstructed. Use it to understand order, not to measure performance. For performance numbers, use the browser's own tooling.
How to use replay inside a bug report
A link to a replay is evidence, not a bug report. Reviewers need to know where to look and what else was true at the time.
- Copy the replay URL with a timestamp so it opens at the failure, not at second zero.
- Write the exact moment in words: "at 02:14 the user clicks Continue and nothing happens".
- Add the console error and the failing request, since the replay may not include either.
- Note the browser, viewport, and account used, for example Chrome 141 at 1440x900 as
[email protected]. - Say what the expected behaviour was. A reviewer cannot guess it from the video.
Steps 3 and 4 are the ones people skip, because gathering them by hand means opening DevTools and copying details into the ticket. A browser-based reporting tool such as Crosscheck collects the console output, network requests, and environment details from the page itself when you file the bug, so the replay link arrives with the context it lacks.
Frequently asked questions
Is session replay legal to use on real users?
It depends on your jurisdiction and what you record. Replay usually counts as personal data, so you need a lawful basis, a mention in your privacy notice, and masking on sensitive fields. Ask your legal or privacy owner before enabling it in production.
Does session replay slow down the page?
The recorder adds a script and some ongoing work when the DOM changes a lot. On normal pages the effect is small. On pages with heavy animation or huge tables it is noticeable, so measure before and after on your slowest page.
Can I use replay on staging only?
Yes, and it is a good starting point. Staging has no real user data, so you can record with fewer masking rules and learn the tool before deciding what production should capture.
Why is my replay blank or missing large areas?
The most common causes are blocked elements, cross-origin iframes, and <canvas> content. Check your blocking rules first, since a broad selector such as blocking every div with a private class can hide more than you expected.
Should replay replace written reproduction steps?
No. Steps tell a developer how to reach the bug on their own machine. Replay proves it happened and shows the details a reporter forgot. Ship both.




