Set the Vary: Accept header
Without Vary: Accept, CDNs and browser caches will happily serve the wrong representation to the wrong audience.
Vary: Accept is the single most-forgotten piece of content negotiation.
Without it, any cache between you and your reader — a CDN, a browser, a
corporate proxy — can serve the HTML version of a page to an agent, or
the Markdown version to a human, depending only on whoever asked first.
What it does
Vary tells downstream caches: the representation you just cached
depends on the value of this request header. Cache entries are keyed
by the URL plus the listed headers.
HTTP/1.1 200 OK
Content-Type: text/markdown; charset=utf-8
Vary: Accept
Cache-Control: public, max-age=300
Now the CDN stores two separate cache entries for /article: one for
Accept: text/markdown, another for Accept: text/html. Each reader
gets the right bytes.
What it looks like when you forget
An agent requests your article, gets Markdown, and the CDN caches it
keyed on URL alone. Ten seconds later a browser requests the same URL —
and the CDN happily returns a text/markdown blob that renders as raw
text in the browser. Or the reverse: the browser primes the cache with
HTML, the agent gets a wall of <div> soup.
Gotchas
Vary: *disables caching entirely. You almost never want this.Vary: Acceptis case-insensitive as a header name, but the values you pass inAcceptare matched literally by most CDNs. That’s why normalizingAccepton your origin is important (see the Accept parsing guide).- If you also vary by
Accept-Encoding, list them together:Vary: Accept, Accept-Encoding.
Verify it
curl -sI -H "Accept: text/markdown" https://yoursite.com/article | grep -i vary
You should see Vary: Accept (possibly alongside other values). If
it’s missing, your CDN is a timebomb.