Session replay that never leaves your servers
Replayowl records what visitors do on your site and plays it back the way they saw it. You run it on Cloudflare Workers or Node, recordings go to your own bucket, and no third party ever sees your users' sessions.
Open the Shop menu, add something to the cart, type in the checkout form. Then press Replay.
This is the real recorder and player running in your browser. Your session stays in this tab.
More than a replay tab in an analytics tool
Several analytics products now include session replay as a side feature. Replayowl only does replay, so it goes further on the parts that matter when you are trying to understand one visitor's problem.
- Whole sessions, across pages
- Page loads in the same tab within 30 minutes are merged into one session. There is no fixed recording length, and the address bar in the player shows which page you are on.
- Errors next to what caused them
- Console errors, warnings, uncaught exceptions and rejected promises are recorded with stack traces and marked on the timeline, so you can watch the clicks that led to them.
- Find the session you were told about
- Search by your own user ID, a page the visitor opened, duration, device, sessions with errors or sessions with rage clicks. Searches live in the URL, so you can share them.
- Replays that match the page
- Hover menus, focus styles, Shadow DOM, CSS-in-JS and constructed stylesheets are reproduced. On Wikipedia, react.dev and GitHub, the replayed DOM matched the live page node for node, with no text differences.
Measured, not promised
A recorder you add to every page should be small and cheap to run. These numbers come from the current build.
| Recorder size | 21.0 KB minified, 7.8 KB gzipped |
|---|---|
| Runtime dependencies in the recorder | None |
| Uploads | One batch every 4 seconds, gzipped above 2 KB, sent in order with retries |
| Seeking in a 5,700-node page | 3 to 7 ms per seek |
| Server work per replay | Close to zero. The browser downloads stored batches straight from storage and merges them itself. |
Recorder size measured on 2026-10-13 from version 0.1.0. Run npm run build in the repository to check it yourself.
Four steps to your first replay
The server is one Cloudflare Worker. Recordings go to any S3-compatible bucket and session data to D1 or Postgres.
- Create a bucket (R2, S3, MinIO and others work) and a D1 database.
- Put the bucket and database in
wrangler.jsoncand store the access keys as secrets. - Run
npm run deploy, open the dashboard and set the admin password. - Add one script tag to your site.
git clone https://github.com/replayowl/replayowl
cd replayowl && npm install
npx wrangler d1 create replayowl
npx wrangler secret put S3_ACCESS_KEY_ID
npx wrangler secret put S3_SECRET_ACCESS_KEY
npx wrangler secret put SETUP_TOKEN
npm run deploy<script src="https://replay.example.com/recorder.js"
data-endpoint="https://replay.example.com/api/ingest" defer></script>Private by default
Masking happens in the visitor's browser, so the original text never reaches your server. Out of the box Replayowl masks every text input, email addresses and runs of 12 to 19 digits. Password and card fields can never be unmasked.
Mark any element with data-ro-mask to hide its text, or data-ro-block to leave it out of the recording entirely.
What it does not do yet
- Heatmaps.
- Recording only when an error happens.
- Contents of iframes, canvas elements and closed shadow roots.
- Images and fonts are loaded from your site at replay time, so if they change or disappear, the replay changes too.
- Single sign-on and multiple users. There is one admin password.
Try it on your own site
Deploy the Worker, paste the script tag, and watch your first session a minute later.