This document defines the wire-protocol contract that every Prerender integration library MUST satisfy. It is the single source of truth for what an integration sends to the Prerender service.
If you are writing a new Prerender integration (Express, Fastify, Rails, FastAPI, Spring, Gin, …) this is the spec to conform to. Run your integration against mock-server.mjs and the scenarios.json manifest to verify.
An integration MUST request a prerendered page from the Prerender service if all of the following hold:
- HTTP method is
GET. - A
User-Agentrequest header is present and non-empty. - The request path does not end with one of the ignored static-asset extensions.
- At least one of:
- The query string contains the parameter
_escaped_fragment_(key alone is sufficient — value may be empty). - The request carries the header
X-Bufferbot(any non-empty value). - The
User-Agentheader matches one of the crawler user agents (case-insensitive substring match).
- The query string contains the parameter
Otherwise the integration MUST pass the request through to the underlying application unchanged.
Match is case-insensitive substring: lowercase the incoming UA and test ua.includes(token) for each token below.
googlebot
yahoo
bingbot
baiduspider
facebookexternalhit
twitterbot
rogerbot
linkedinbot
embedly
quora link preview
showyoubot
outbrain
pinterest
slackbot
developers.google.com/+/web/snippet
w3c_validator
perplexity
oai-searchbot
chatgpt-user
gptbot
claudebot
amazonbot
This list is the canonical source. Integrations SHOULD copy it verbatim. New entries are added here first and propagated to integrations in a coordinated bump.
Match is case-insensitive suffix on request.path (not the full URL — exclude query string).
.js .css .xml .less .png .jpg .jpeg .gif .pdf .doc .txt .ico .rss .zip
.mp3 .rar .exe .wmv .avi .ppt .mpg .mpeg .tif .wav .mov .psd .ai .xls
.mp4 .m4a .swf .dat .dmg .iso .flv .m4v .torrent .ttf .woff .svg
When the integration decides to prerender (per §1), it MUST issue exactly one HTTP request to the Prerender service shaped as follows.
GET
{serviceUrl}/{originalScheme}://{originalHost}{originalPathAndQuery}
Where:
serviceUrlis the configured service URL (defaulthttps://service.prerender.io/). Trailing slash is normalized — the integration MUST ensure exactly one/betweenserviceUrland the target URL.originalSchemeis the scheme of the incoming request (httporhttps), unless an explicitprotocoloverride is configured.originalHostis the value of the incomingHostheader.originalPathAndQueryis the request path plus query string (preserving raw encoding; query string included verbatim when non-empty, otherwise omitted entirely along with the?).
Example: an incoming request to https://example.com/blog/post-1?ref=twitter with default service URL produces:
GET https://service.prerender.io/https://example.com/blog/post-1?ref=twitter
| Header | Value |
|---|---|
User-Agent |
The exact value of the original request's User-Agent header. The renderer needs the bot's UA to render the page as that bot would see it — integrations MUST NOT replace it with their own identifier. |
X-Prerender-Int-Type |
The integration's canonical identifier — see §5. |
X-Prerender-Int-Version |
The integration's own released version, e.g. 1.0.2. Hardcoded constant per integration; bumped on each release. Lets support tickets like "all v1.0.x koa requests fail after Y" be answered from logs alone. |
X-Prerender-Request-Id |
A fresh UUID v4 generated by the integration for this single outgoing request. Lets support correlate "customer says X happened at 14:32" with a specific backend log line. MUST be a new value per outgoing request — never reused, never derived from the incoming request. |
| Header | When sent | Value |
|---|---|---|
X-Prerender-Token |
When token is configured |
The configured token verbatim. Omit the header entirely when unconfigured — do not send empty string. |
Authorization(Prerender usesX-Prerender-Token, not standard auth).- Hop-by-hop headers from the original request (
Connection,Transfer-Encoding,Keep-Alive, etc.). - Cookies from the original request.
The integration MUST NOT follow redirects from the Prerender service automatically. Redirect responses (3xx) MUST be passed through to the original client with their Location header intact.
The header value identifies which integration emitted the request, used for backend telemetry and support.
Current convention (preserved for backwards compatibility): PascalCase, no separators.
| Integration | Value |
|---|---|
| Laravel | Laravel |
| Java (Servlet Filter) | Java |
| ASP.NET Core | AspNetCore |
| Django | Django |
| Koa | Koa |
| Hapi | Hapi |
New integrations: pick a PascalCase identifier that names the framework, not the language (e.g., Express, Fastify, Rails, FastAPI, Spring, Gin, Phoenix). When a framework name has its own canonical casing (e.g., FastAPI, AspNetCore), preserve it.
Status: this convention is documented because it reflects what is already deployed in 6 integrations. Future work may normalize to lowercase-kebab (
asp-net-core,fast-api) — if so, the backend will accept both.
The integration MUST:
- Forward the response status code verbatim to the original client.
- Forward the response body verbatim.
- Forward response headers, EXCEPT the following hop-by-hop / framework-managed headers which MUST be dropped:
Content-EncodingContent-LengthTransfer-EncodingConnection
If the request to the Prerender service fails with a network/connection error, the integration MUST fall back to the underlying application (i.e., behave as if shouldPrerender had returned false). It MUST NOT propagate the error to the original client.
If the Prerender service returns a non-2xx, non-3xx status, the integration SHOULD forward it as-is (default). Implementations MAY expose a softHttpCodes opt-in that converts 4xx/5xx into either a passthrough or a custom error page.
See scenarios.json for the canonical scenario manifest. Each scenario defines:
- An incoming request to the integration (method, path, query, headers).
- Whether the integration should prerender.
- If prerendering: the expected outgoing request to the mock (URL, headers).
To verify your integration:
- Spawn the mock:
node mock-server.mjs(defaults to:9090). - Configure your integration with
serviceUrl: http://localhost:9090/andtoken: test-token. - For each scenario, hit your integration with the input request, then
GET http://localhost:9090/__requestsand assert the recorded outgoing request matches the expected shape. - Reset between scenarios with
POST http://localhost:9090/__reset.
See README.md for a full example.