Skip to content

feat(docs): add reverse proxy setup guide with provider-specific instructions#5601

Merged
kafkas merged 6 commits into
mainfrom
devin/1779467679-reverse-proxy-docs
May 22, 2026
Merged

feat(docs): add reverse proxy setup guide with provider-specific instructions#5601
kafkas merged 6 commits into
mainfrom
devin/1779467679-reverse-proxy-docs

Conversation

@thesandlord
Copy link
Copy Markdown
Contributor

Summary

Adds a new documentation page explaining how to configure a reverse proxy for serving Fern docs from a subpath (e.g. mydomain.com/docs). Covers:

  1. Routing — How to set x-fern-host and Host headers so Fern identifies the correct docs site
  2. Caching — How to disable HTML caching on the reverse proxy to prevent stale deployment issues (the root cause of the ChunkLoadError bug we just fixed in fern-platform)

Provider-specific instructions for: Cloudflare Workers, AWS CloudFront, Netlify, Vercel, Nginx, Akamai, and Caddy.

Also updates the existing "Setting up your domain" page to cross-reference the new reverse proxy page from the subpath setup section.

Review & Testing Checklist for Human

  • Verify the new page renders correctly at /learn/docs/preview-publish/reverse-proxy in the preview deployment
  • Confirm all provider tabs render properly (7 tabs in Routing section, 7 tabs in Caching section)
  • Check that the cross-reference link from the subpath accordion on the "Setting up your domain" page resolves correctly
  • Review the Cloudflare Workers and CloudFront examples — these are the most common customer setups and should be technically accurate
  • Verify the x-fern-host header documentation matches current Fern behavior (header value is bare domain without subpath)

Notes

The origin URL used in all examples is app.buildwithfern.com, which is the production Fern docs origin. The x-fern-host header behavior is documented in packages/fern-docs/bundle/src/proxy.ts and packages/commons/docs-server/src/xfernhost/edge.ts in the fern-platform repo.

Link to Devin session: https://app.devin.ai/sessions/5705640d594a45858652bac81f125c9d
Requested by: @thesandlord

…ructions

Co-Authored-By: Sandeep Dinesh <sandeep@buildwithfern.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

A working reverse proxy does two things:

1. **Routes requests** from your subpath to Fern's origin, with the `x-fern-host` header set so Fern identifies your site.
2. **Disables caching** of HTML responses so visitors always receive the latest deployment.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [vale] reported by reviewdog 🐶
[FernStyles.Current] Avoid time-relative terms like 'latest' that become outdated

- Cache policy: `CachingDisabled` (AWS managed policy)
- Origin request policy: `AllViewer` (forwards all headers, query strings, and cookies)

If you need fine-grained control instead of `CachingDisabled`, create a custom cache policy with TTL values set to `0` and forward the `Host` and `x-fern-host` headers.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'TTL' has no definition.

```

<Warning>
Nginx does not natively support [Brotli](https://github.com/google/brotli) decompression. The `Accept-Encoding` override above prevents HTTP/2 transfer errors caused by Fern's default Brotli-compressed responses.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [vale] reported by reviewdog 🐶
[Microsoft.Contractions] Use 'doesn't' instead of 'does not'.


## Caching

Fern sets `Cache-Control: public, max-age=0, must-revalidate` on HTML responses, which tells your proxy not to cache page content. Most providers respect this header by default. However, if your provider overrides origin cache headers or applies a default TTL, you must explicitly disable caching for the proxied path.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'TTL' has no definition.

Caching stale HTML causes visitors to load a page that references JavaScript and CSS files from an older deployment. Those files no longer exist, so the page fails to hydrate and may display an error.

<Warning>
Do not cache HTML responses from Fern. Static assets (`/_next/static/*`) are served directly by Fern's CDN with long-lived cache headers and do not pass through your proxy.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [vale] reported by reviewdog 🐶
[Microsoft.Contractions] Use 'don't' instead of 'Do not'.

| Setting | Value |
|---|---|
| Minimum TTL | `0` |
| Maximum TTL | `0` |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'TTL' has no definition.

|---|---|
| Minimum TTL | `0` |
| Maximum TTL | `0` |
| Default TTL | `0` |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'TTL' has no definition.

| Default TTL | `0` |

<Warning>
CloudFront ignores `CDN-Cache-Control` and `Surrogate-Control` headers. It only reads the standard `Cache-Control` header. A custom cache policy with a non-zero `default-TTL` will cache responses regardless of the origin's `Cache-Control: max-age=0` directive.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'TTL' has no definition.

</Tab>
<Tab title="Caddy">

Caddy does not cache responses by default. No additional configuration is needed unless you have explicitly enabled caching with a `cache` directive or external module.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [vale] reported by reviewdog 🐶
[Microsoft.Contractions] Use 'doesn't' instead of 'does not'.


## Verify your setup

After configuring your proxy, confirm that requests are routed correctly and responses are not cached.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [vale] reported by reviewdog 🐶
[Microsoft.Contractions] Use 'aren't' instead of 'are not'.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

Co-Authored-By: Sandeep Dinesh <sandeep@buildwithfern.com>
- Cache policy: `CachingDisabled` (AWS managed policy)
- Origin request policy: `AllViewer` (forwards all headers, query strings, and cookies)

If you need fine-grained control instead of `CachingDisabled`, create a custom cache policy with time-to-live (TTL) values set to `0` and forward the `Host` and `x-fern-host` headers.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'TTL' has no definition.


Use the AWS managed `CachingDisabled` cache policy on the `/docs*` behavior. This policy sets `min-TTL`, `max-TTL`, and `default-TTL` to `0`, so CloudFront always fetches a fresh response from Fern.

If you use a custom cache policy instead, set all time-to-live (TTL) values to `0`:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'TTL' has no definition.

devin-ai-integration Bot and others added 2 commits May 22, 2026 17:40

Set the behavior to:

- **Origin**: the Fern origin you just created
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[FernStyles.Hedges] Avoid hedge words and filler like 'just'. Prefer direct statements.

- **Cache policy**: `CachingDisabled` (AWS managed policy)
- **Origin request policy**: `AllViewer` (forwards all headers, query strings, and cookies)

To use a custom cache policy instead of `CachingDisabled`, set min, max, and default TTL to `0` and forward `Host` and `x-fern-host`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'TTL' has no definition.

</Steps>

<Warning>
CloudFront ignores `CDN-Cache-Control` and `Surrogate-Control` — only the standard `Cache-Control` header is read. A custom cache policy with a non-zero default TTL caches responses regardless of Fern's `Cache-Control: max-age=0` directive.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'TTL' has no definition.

devin-ai-integration Bot and others added 2 commits May 22, 2026 17:59
…proxy guide

- Vercel: Remove incorrect 'has' conditions from rewrites (has is a match
  condition, not a header-setter). Add middleware example for x-fern-host.
- Caddy: Change handle_path to handle to preserve the /docs prefix in the
  upstream request.

Co-Authored-By: Anar Kafkas <anarkafkas@gmail.com>
Replace rewrites + middleware approach with routes + transforms,
which handles both the rewrite and the header in one vercel.json config.

Co-Authored-By: Anar Kafkas <anarkafkas@gmail.com>
@kafkas kafkas merged commit 189e7bd into main May 22, 2026
4 checks passed
@kafkas kafkas deleted the devin/1779467679-reverse-proxy-docs branch May 22, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants