When you host Fern docs on a subpath like mydomain.com/docs, your infrastructure must proxy requests from that path to Fern’s origin. Subdomain setups (docs.mydomain.com) use a CNAME record instead and don’t require a reverse proxy.
A working reverse proxy does two things:
app.buildwithfern.com, preserving the original path. The x-fern-host header tells Fern which docs site to serve.Every proxied request needs two headers:
Fern sets Cache-Control: public, max-age=0, must-revalidate on HTML responses, which most providers respect by default. If yours overrides origin cache headers or applies its own time-to-live, explicitly disable caching for the proxied path.
Don’t cache HTML responses from Fern. Cached HTML references JavaScript and CSS from older deployments — those files no longer exist, so pages fail to load. Static assets (/_next/static/*) are served directly by Fern’s CDN and don’t pass through your proxy.
Create a Cloudflare Worker that intercepts requests to your docs subpath and proxies them to Fern:
Replace SUBPATH and FERN_HOST with your subpath and bare domain.
In the Cloudflare dashboard under Workers Routes, attach the Worker to a route pattern like mydomain.com/docs/*.
The Worker forwards Fern’s Cache-Control header, so caching works automatically. In the Cloudflare dashboard, confirm that no Page Rule or Cache Rule sets “Cache Level: Cache Everything” for /docs* — if one exists, remove it or override it with “Cache Level: Bypass.”
In your CloudFront distribution, go to Origins and create a new origin:
Under Add custom header, add:
Replace mydomain.com with your bare domain (without the subpath).
Go to Behaviors and create a new behavior:
AllViewerExceptHostHeader forwards all viewer headers, query strings, and cookies to the origin while letting CloudFront set the Host header to app.buildwithfern.com. This is required — Fern’s origin uses the Host header for routing and rejects requests with an unrecognized host.
Do not use the AllViewer origin request policy. It forwards the viewer’s Host header (your domain) instead of the origin’s, which causes Fern’s origin to return errors or raw React Server Component payloads instead of HTML.
CloudFront ignores CDN-Cache-Control and Surrogate-Control — only the standard Cache-Control header is read. If you use a custom cache policy instead of CachingDisabled, set the default, minimum, and maximum TTL to 0. A non-zero default TTL caches HTML responses regardless of Fern’s Cache-Control: max-age=0 directive, which can cause stale content and errors.
Use a route with a transform rule to rewrite the path and set x-fern-host in one step:
Vercel respects the origin’s Cache-Control header for rewrite destinations, so no extra caching configuration is needed.
Nginx doesn’t natively support Brotli decompression. The Accept-Encoding override above prevents HTTP/2 transfer errors caused by Fern’s default Brotli-compressed responses.
Run these checks against your live subpath:
If content-type is text/x-component instead of text/html, your proxy is forwarding the viewer’s Host header to Fern’s origin. Ensure the Host header sent to the origin is app.buildwithfern.com.
If the age header is present and non-zero, your proxy is serving a cached response. Revisit your provider’s configuration to ensure HTML caching is disabled.