Security
headless-prerender drives a real browser and fetches whatever URL it is given. Treat an unprotected instance as a server-side request forgery (SSRF) proxy. Apply these controls before exposing it.
1. Restrict which origins can be rendered
Without allowedOrigins, any http(s) URL is fair game - including http://169.254.169.254/ (cloud metadata) and internal hostnames.
headless-prerender serve \
--allow-origin https://www.example.com \
--allow-origin https://blog.example.comnew PrerenderServer({
allowedOrigins: ["https://www.example.com", "https://blog.example.com"],
});A request for any other origin is rejected before a browser page is opened.
2. Require a token
headless-prerender serve --token "$(openssl rand -hex 32)"Callers must then supply it as ?token=... or an x-prerender-token header. Your reverse proxy adds this; external callers cannot.
3. Do not expose the port publicly
Bind to localhost or a private interface and let only your proxy reach it:
headless-prerender serve --host 127.0.0.1Or keep --host 0.0.0.0 inside a private network / container network with no public route, plus a firewall rule.
4. Protocol enforcement (built in)
Non-http(s) URLs (file:, ftp:, data:, ...) are always rejected. You do not need to configure this.
5. Run the browser sandboxed
The bundled launch flags include --no-sandbox so Chromium starts inside containers without extra privileges. If you can run with the sandbox enabled (non-root user, proper kernel support), that is stronger - launch your own Renderer with launchArgs that omit --no-sandbox, or run the container as a non-root user with --cap-add SYS_ADMIN avoided in favour of a seccomp profile.
6. Resource limits
A headless browser can consume significant memory and CPU.
- Run it in a container with memory and CPU limits.
- Keep
--timeoutmodest (the default is 20 s) so hung pages are abandoned. - Put a concurrency limit in your proxy if bot traffic is bursty.
- Restart the process periodically (e.g. a scheduled container restart) to reclaim memory.
Hardening checklist
- [ ]
allowedOriginsset to your site's origins only - [ ]
tokenset and required - [ ] Port not reachable from the public internet
- [ ] Runs in a container with memory/CPU limits
- [ ] Timeout kept low
- [ ] Process restarted on a schedule or on deploy