groundy
infrastructure & runtime

How Vercel Runs Its Own CDN in Front of Discourse: A Self-Dogfooding Case Study

Vercel fronts its own Discourse forum with its CDN, but the edge cache only serves anonymous reads. Logged-in pages fall to the origin by design.

8 min···4 sources ↓

Vercel’s own Community forum runs on Discourse, hosted somewhere else, with Vercel’s CDN bolted in front of it. Vercel’s writeup frames this as a low-effort modernization path, but the structural detail matters more than the marketing: Vercel’s CDN cache documentation lists user-specific content, sensitive user data, and per-request variation as cases where the CDN “isn’t the right fit.” For a forum, that puts logged-in pages outside what the edge is built to serve, leaving the origin to carry them.

What is Vercel actually running here?

The Vercel Community at community.vercel.com is a Discourse app hosted off-platform and proxied through Vercel’s CDN, rather than deployed natively on Vercel. According to Vercel’s post, the proxy buys the team three platform features layered over the unmodified forum: Web Analytics (anonymized, cookie-free referrer and demographic data), Firewall DDoS protection that “automatically prevented several attacks in the last year,” and Bot Management tuned to let trusted crawlers index the forum so posts surface in ChatGPT searches.

The scale sitting behind that proxy is not trivial. Discourse reports more than 22,000 communities, 1 billion-plus monthly page views, and 3 million-plus monthly posts, with individual hosted sites reaching 100,000-plus monthly active users. Most of that volume is anonymous read traffic, which is exactly what a CDN is good at absorbing, and it is also why the dogfooding example is worth copying carefully rather than wholesale.

How does the two-domain proxy lock down the origin?

The setup needs two domains: an inner host (the actual origin, shaped like your-site.discourse.com) and an outer host (the Vercel project domain users actually visit, such as community.vercel.com). A vercel.ts config (formerly vercel.json) rewrites all traffic on the outer host to the inner host while injecting an x-proxy-secret header the inner host validates:

import { type VercelConfig, routes, deploymentEnv } from '@vercel/config/v1'
export const config: VercelConfig = {
rewrites: [
routes.rewrite('/(.*)', deploymentEnv('INNER_HOST'), {
requestHeaders: {
'x-proxy-secret': deploymentEnv('PROXY_HEADER'),
},
}),
],
}

The inner host reads the x-proxy-secret header to validate traffic, so the origin cannot be hit directly by bypassing the CDN. That is the whole origin-protection story, and it is the one part of the architecture that holds whether or not any caching is happening.

What does Vercel’s edge cache actually cache?

This is the part the blog soft-pedals. Vercel’s CDN cache documentation is explicit about what the edge cache is good for: static pages that are the same for all users, API responses that don’t change frequently, static assets, and server-rendered pages with predictable cache lifetimes. It is equally explicit about what it is not good for, listing three cases where “CDN Cache isn’t the right fit”: user-specific content that relies on a Vary header, responses that include sensitive user data, and content that changes on every request to the same URL. The docs steer those cases toward Runtime Cache rather than the CDN.

To cache a dynamic response at the edge, the docs require a Cache-Control directive such as s-maxage=N, optionally combined with stale-while-revalidate or stale-if-error, on either a Vercel Function response or a route definition. Without one of those directives, the response is not cached at the edge.

For a Discourse forum, the consequence is concrete. An anonymous reader hitting a topic page is the canonical CDN-cache case: a static response, the same for every anonymous visitor. A signed-in member loading the same topic sees personalized HTML, which the docs themselves classify as a poor fit for CDN cache. The edge cache is not caching the forum; it is caching the forum’s anonymous read path.

Who serves the logged-in pages?

The origin does. The docs put user-specific content outside the CDN cache’s intended use, listing “responses include sensitive user data” and “content changes on every request to the same url” as cases where the documentation says CDN cache is not the right fit, and steering those cases to Runtime Cache. The load-bearing cache for any personalized route has to live closer to Discourse, in whatever caching the forum itself does, with the Vercel layer covering only the static and anonymous shares.

The blog never quantifies what share of Community traffic is logged-in versus anonymous, which is the number that would tell you how much the edge is actually doing. For a community where most sessions are short reads by anonymous visitors, the edge absorbs the bulk of the load. For a community skewed toward active members, the edge contribution shrinks and the origin stays warm by necessity. Vercel does not publish cache-hit ratios for this deployment, so the honest reading is that the edge’s value is DDoS shielding, bot filtering, and fast anonymous delivery, with the dynamic, authenticated share still carried by Discourse.

This division of labor is the real lesson, and it is the thing most “put Vercel in front of your app” how-to posts omit: plan a two-tier cache, edge for anonymous and static, origin for logged-in, and do not let a blog screenshot of a fast page load convince you the edge is serving authenticated content.

Why mount Next.js alongside Discourse on one domain?

This is where the dogfooding gets interesting beyond caching. Vercel uses Microfrontends to mount a Next.js app on the same domain as the Discourse app, configured via a microfrontends.json file that directs traffic for specific routes to separate Vercel projects. The post names three reasons for the split: creating new pages that would be impractical as Discourse plugins, overwriting Discourse pages that cannot be customized, and keeping users authenticated through Sign in with Vercel.

The post also mentions a /.well-known/workflow route that uses the Workflow Development Kit for event creation. The payoff of microfrontends over a single proxy is isolation: the project that talks to the third-party host can be locked down with its own environment variables and organization permissions, while the Next.js project stays free to call Vercel-internal services. You could approximate some of this with negative-match regex in the proxy, but the post argues that splitting projects gives cleaner boundaries.

Can a misconfigured edge cache leak sessions?

Yes, and this is the failure mode worth knowing before you copy the pattern. A third-party writeup describes a past incident in which cached SSR pages leaked user sessions between visitors because cache keys ignored Cookie and Authorization headers, with the proposed fix being header-based cache matching via Vary: Cookie, Authorization.

Be precise about provenance here. That account is a vendor SEO blog, not a Vercel incident report, so the “cache keys ignored Cookie/Authorization” framing should be treated as folklore rather than a confirmed root cause. The mechanism is plausible, and it matches a class of caching bug that has bitten many SSR setups, but treat the specifics as unverified.

The through-line from Vercel’s own docs is that Vary is the lever for user-specific content. The CDN cache documentation lists “user-specific content without the Vary header” as a case where CDN cache is not the right fit and steers it to Runtime Cache. The implication is that if you opt a route into edge caching via s-maxage and that route varies per user, the Vary header is what keeps one visitor’s response from being served to another. Treat the discipline as load-bearing whenever authenticated HTML and edge caching share a route.

What should teams caching forums take away?

The copyable architecture is a two-domain proxy with an x-proxy-secret locking the origin down, a microfrontends split when you need Next.js pages next to the legacy app, and a clear-eyed division of caching responsibilities: Vercel’s edge for static and anonymous responses, the origin’s own cache for anything user-specific. Vercel positions this as a way to add firewall protection, DDoS mitigation, and observability to platforms like Discourse or WordPress without migrating them completely, which is accurate as far as it goes. The cache you actually get is for the readers who are not logged in.

For teams running Discourse, WordPress, or any comment-heavy system, the checklist is short. Lock the origin behind a proxy secret. Add Vary headers only where a request header genuinely changes the response, and pair them with a caching directive like s-maxage. Treat any “the edge caches my authenticated app” claim with skepticism, because Vercel’s own docs classify user-specific content as a poor fit for CDN cache.

Frequently Asked Questions

How does this differ from Cloudflare’s guidance for fronting Discourse?

Cloudflare’s Discourse playbook leans on page rules, Tiered Cache, and Cache Reserve to shorten the path back to origin. Vercel’s proxy skips the intermediate-cache layer and instead isolates the origin behind an x-proxy-secret header, so logged-in traffic hits Discourse directly with no tiered hop in between.

Vercel’s edge already keys cache entries on Accept and Accept-Encoding by default, so every header you append to Vary multiplies the number of stored entries. A high-traffic route carrying Vary: Cookie, Authorization can swell cache size exponentially, which is why the docs warn to add Vary only where a request header genuinely changes the response.

Which routes does Vercel pull out of the Discourse proxy?

Two prefixes are split into a separate community-nextjs project: /.well-known/workflow/, which backs the Workflow Development Kit, and /live/. Both are wired in microfrontends.json, and moving them out gives the Next.js app its own environment variables and organization permissions instead of inheriting the proxy project’s trust boundary.

What platform primitives does the setup depend on?

The microfrontends split and the Workflow Development Kit route both sit on features Vercel spotlighted in its June 22, 2026 Ship recap. That makes the Community deployment a live reference for those primitives, but it also means the microfrontends.json shape and /.well-known/workflow contract will drift as the Workflow SDK ships, so treat the config as a dated snapshot.

Why proxy Discourse instead of deploying it natively on Vercel?

Discourse is a long-running Ruby on Rails app with its own database, background jobs, and origin cache, none of which fit Vercel’s Functions and edge runtime. Fronting the existing host with the CDN adds Firewall, Web Analytics, and Bot Management without porting the forum, which is why Vercel frames the proxy as securing the existing investment rather than replacing it.

sources · 4 cited

  1. How we run Vercel's CDN in front of Discoursevercel.comprimaryaccessed 2026-06-28
  2. Vercel CDN Cachevercel.comprimaryaccessed 2026-06-28
  3. Discoursediscourse.orgvendoraccessed 2026-06-28