For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Book a demoLog inStart for free
  • Getting started
    • Overview
    • How it works
    • Quickstart
    • Project structure
    • Customer showcase
    • Changelog
  • Configuration
    • Overview
    • Site-level settings
    • Page-level settings
  • Writing content
    • Markdown basics
    • Rich media in Markdown
    • Fern Editor
    • Reusable snippets
  • AI features
    • Overview
    • Fern Writer
    • AI-generated examples
    • Markdown access
      • Overview
      • Customize LLM output
      • Agent directives
      • Analytics and integration
    • MCP server
    • API catalog discovery
      • Preview changes
      • Publishing your docs
      • Setting up your domain
      • Reverse proxy setup
      • Multi-source docs
  • Public API
    • GETJWT from Fern API key
    • GETAlgolia search credentials
    • GETCurrent user information
  • Fern Writer API
    • GETGet Fern Writer Install Link
Checking status...
SOC2Soc 2 Type II
© 2026 Fern • Birch Solutions, Inc., a Postman company

Documentation

SDKsDocsAsk FernCLI Reference

API Definitions

OpenAPIAsyncAPIOpenRPCgRPC

Resources

BlogSupportPricing

Company

Brand KitPrivacy PolicyTerms of Service
LogoLogo
Book a demoLog inStart for free
On this page
  • How it works
  • Set up your provider
  • Verify your setup
Preview & publish

Reverse proxy setup

||View as Markdown|
Was this page helpful?
Edit this page
Previous

Bring your custom domain

Next

Multi-source docs

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.

How it works

A working reverse proxy does two things:

  1. Routes requests from your subpath to Fern’s origin at app.buildwithfern.com, preserving the original path. The x-fern-host header tells Fern which docs site to serve.
  2. Disables caching of HTML responses so visitors always receive the current deployment.

Every proxied request needs two headers:

HeaderValuePurpose
x-fern-hostYour bare domain without the subpath (e.g. mydomain.com, not mydomain.com/docs)Tells Fern which docs site to serve
Hostapp.buildwithfern.comRoutes the request to Fern’s origin

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.

Set up your provider

Cloudflare Workers
1

Create the Worker

Create a Cloudflare Worker that intercepts requests to your docs subpath and proxies them to Fern:

1const SUBPATH = "/docs";
2const FERN_HOST = "mydomain.com";
3
4export default {
5 async fetch(request) {
6 const url = new URL(request.url);
7
8 if (
9 url.pathname !== SUBPATH &&
10 !url.pathname.startsWith(`${SUBPATH}/`)
11 ) {
12 return new Response("Not found", { status: 404 });
13 }
14
15 const upstreamUrl = new URL(request.url);
16 upstreamUrl.protocol = "https:";
17 upstreamUrl.hostname = "app.buildwithfern.com";
18 upstreamUrl.port = "";
19
20 const proxiedRequest = new Request(upstreamUrl, request);
21 proxiedRequest.headers.set("x-fern-host", FERN_HOST);
22
23 return fetch(proxiedRequest);
24 },
25};

Replace SUBPATH and FERN_HOST with your subpath and bare domain.

2

Attach the Worker to a route

In the Cloudflare dashboard under Workers Routes, attach the Worker to a route pattern like mydomain.com/docs/*.

3

Check for conflicting cache rules

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.”

AWS CloudFront
1

Add a Fern origin

In your CloudFront distribution, go to Origins and create a new origin:

SettingValue
Origin domainapp.buildwithfern.com
ProtocolHTTPS only
HTTPS port443
Minimum origin SSL protocolTLSv1.2

Under Add custom header, add:

Header nameValue
x-fern-hostmydomain.com

Replace mydomain.com with your bare domain (without the subpath).

2

Create a cache behavior for your docs path

Go to Behaviors and create a new behavior:

SettingValue
Path pattern/docs*
OriginThe Fern origin you just created
Viewer protocol policyRedirect HTTP to HTTPS
Cache policyCachingDisabled (AWS managed)
Origin request policyAllViewerExceptHostHeader (AWS managed)

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.

3

Verify the behavior order

CloudFront evaluates behaviors in order. Ensure your /docs* behavior appears above the default (*) behavior so docs requests are routed to Fern’s origin rather than your primary site.

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.

Netlify
1

Add a rewrite rule

In netlify.toml (or _redirects):

netlify.toml
1[[redirects]]
2 from = "/docs/*"
3 to = "https://app.buildwithfern.com/docs/:splat"
4 status = 200
5 force = true
6
7 [redirects.headers]
8 x-fern-host = "mydomain.com"

Netlify rewrites with status = 200 act as a reverse proxy — the visitor’s browser sees your domain while the content comes from Fern. Netlify respects the origin’s Cache-Control header, so no extra caching configuration is needed.

Vercel
1

Add a route with a transform

Use a route with a transform rule to rewrite the path and set x-fern-host in one step:

vercel.json
1{
2 "$schema": "https://openapi.vercel.sh/vercel.json",
3 "routes": [
4 {
5 "src": "/docs(/.*)?",
6 "dest": "https://app.buildwithfern.com/docs$1",
7 "transforms": [
8 {
9 "type": "request.headers",
10 "op": "set",
11 "target": { "key": "x-fern-host" },
12 "args": "mydomain.com"
13 }
14 ]
15 }
16 ]
17}

Vercel respects the origin’s Cache-Control header for rewrite destinations, so no extra caching configuration is needed.

Nginx
1

Add a location block

1location /docs {
2 proxy_pass https://app.buildwithfern.com;
3 proxy_set_header Host app.buildwithfern.com;
4 proxy_set_header x-fern-host mydomain.com;
5 proxy_set_header X-Real-IP $remote_addr;
6 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
7 proxy_set_header X-Forwarded-Proto $scheme;
8 proxy_ssl_server_name on;
9
10 # Prevent encoding issues with Brotli responses
11 proxy_set_header Accept-Encoding "gzip, deflate";
12
13 # Do not cache HTML
14 proxy_no_cache 1;
15 proxy_cache_bypass 1;
16 add_header Cache-Control "public, max-age=0, must-revalidate" always;
17}

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.

Akamai
1

Add a routing rule

In a Site Delivery or Ion property, add a rule matching path /docs*:

  • Origin Server: app.buildwithfern.com
  • Forward Host Header: Origin Hostname

Add a Modify Outgoing Request Header behavior:

ActionHeader nameValue
Addx-fern-hostmydomain.com
2

Disable caching on the rule

Add a Caching behavior to the same rule:

SettingValue
Caching OptionNo Store

Alternatively, set Honor Origin Cache-Control to Yes so Akamai respects Fern’s Cache-Control: public, max-age=0, must-revalidate header.

Caddy
1

Add to your Caddyfile

1mydomain.com {
2 handle /docs* {
3 reverse_proxy https://app.buildwithfern.com {
4 header_up Host app.buildwithfern.com
5 header_up x-fern-host mydomain.com
6 }
7 }
8}

Caddy doesn’t cache responses by default, so no extra caching configuration is needed unless you’ve explicitly enabled caching with a cache directive or external module.

Verify your setup

Run these checks against your live subpath:

$# Confirm the page returns HTML (not an error or RSC payload)
$curl -sI https://mydomain.com/docs | grep -i content-type
$# Expected: content-type: text/html; charset=utf-8
$
$# Verify Cache-Control on the HTML response
$curl -sI https://mydomain.com/docs | grep -i cache-control
$# Expected: cache-control: public, max-age=0, must-revalidate
$
$# Verify the page is not cached by your proxy (age should be 0 or absent)
$curl -sI https://mydomain.com/docs | grep -i "^age:"

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.