Skip to content

feat(vercel): add Vary: accept so CDN serves markdown to agents#265

Open
sriramveeraghanta wants to merge 1 commit intomasterfrom
feat/markdown-vary-accept
Open

feat(vercel): add Vary: accept so CDN serves markdown to agents#265
sriramveeraghanta wants to merge 1 commit intomasterfrom
feat/markdown-vary-accept

Conversation

@sriramveeraghanta
Copy link
Copy Markdown
Member

@sriramveeraghanta sriramveeraghanta commented Apr 21, 2026

Summary

Why

PR #263 wired up buildEnd to copy .md into dist/ and added a rewrite rule that routes Accept: text/markdown to /:path*.md. In practice, the rewrite only wins on a cold cache — once HTML is cached for a path, Vercel's CDN serves that HTML for all subsequent requests regardless of Accept header, because responses don't advertise that they vary on it.

Advertising Vary: accept lets the CDN split the cache so HTML and markdown coexist as separate entries. Cloudflare's Markdown for Agents reference and the markdown-negotiation skill both specify this header.

Test plan

After the Vercel preview deploys:

  • curl -I -H "Accept: text/markdown" <preview>/api-reference/introduction returns content-type: text/markdown; charset=utf-8
  • curl -I <preview>/api-reference/introduction (no Accept) returns content-type: text/html; charset=utf-8
  • Both responses include vary: accept
  • Direct .md fetch still works: curl -I <preview>/api-reference/introduction.md
  • Scan with https://isitagentready.comchecks.contentAccessibility.markdownNegotiation.status returns pass

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced HTTP response headers to improve content negotiation and optimize caching behavior across the application.

The existing Accept: text/markdown rewrite was being masked by Vercel's
CDN caching the first variant (HTML) and serving it for all subsequent
requests regardless of Accept header. Advertising Vary: accept lets the
CDN cache HTML and markdown as separate entries so agents actually get
markdown back.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
developer-docs Ready Ready Preview, Comment Apr 21, 2026 3:18pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

📝 Walkthrough

Walkthrough

The pull request adds a Vary: accept HTTP response header to the vercel.json configuration for all routes matching the root path pattern, affecting content negotiation and caching behavior for deployed responses.

Changes

Cohort / File(s) Summary
HTTP Header Configuration
vercel.json
Added Vary: accept header to the headers configuration for routes matching /(.*)

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Poem

🐰 A tiny header hops into place,
Vary: accept with elegance and grace,
Through vercel's routes it now takes flight,
Content negotiation shines so bright! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a Vary: accept header to vercel.json to enable proper CDN caching of markdown content for agents.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/markdown-vary-accept

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@vercel.json`:
- Around line 11-14: Replace the Vary header value "accept" with "Accept" to
match HTTP header casing (change the value for the object with "key": "Vary"),
then deploy and monitor CDN cache hit-rate; if cache fragmentation increases,
implement a more targeted negotiation: add a custom middleware header (e.g.,
"X-Preferred-Format" or similar) that maps incoming Accept strings to a small
set like "markdown" or "html", set that header on responses and change Vary to
that custom header instead to avoid high cardinality.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 29af9a9c-1f6b-4877-b598-72e344c9f367

📥 Commits

Reviewing files that changed from the base of the PR and between 6757ff2 and b6253c8.

📒 Files selected for processing (1)
  • vercel.json

Comment thread vercel.json
Comment on lines +11 to 14
{
"key": "Vary",
"value": "accept"
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

How does Vercel's edge CDN handle Vary: Accept — does it normalize the Accept header into buckets or key the cache on the raw value?

💡 Result:

Vercel's Edge CDN keys the cache on the raw value of the Accept header rather than normalizing it into buckets. The Accept header is included in the cache key by default, even without an explicit Vary: Accept response header. When a Vary: Accept header is set (along with a caching Cache-Control like s-maxage), the CDN creates unique cache entries for each distinct combination of the base cache key and the raw Accept header value from the request. This standard HTTP Vary behavior ensures correct content negotiation (e.g., serving WebP vs JPEG based on browser Accept headers) but can lead to high cache fragmentation if Accept values vary widely across clients, as no normalization or bucketing is mentioned or implied in official docs.

Citations:


Change "accept" to "Accept" to match HTTP header naming conventions (line 13).

This is functionally correct and solves the stale-HTML-for-agents problem. However, Vary: Accept creates unique cache entries for each raw Accept header value on Vercel's CDN (no normalization is performed). Since browsers send highly variable Accept headers (e.g. text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 with differences across Chrome/Firefox/Safari/versions), this significantly fragments the cache by keying on the full raw header string.

Monitor hit-rate impact after rollout. If cache efficiency regresses, consider a more targeted approach using custom middleware headers that only distinguish markdown vs. html, then Vary on that instead—this gives the same content negotiation without the cardinality cost.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vercel.json` around lines 11 - 14, Replace the Vary header value "accept"
with "Accept" to match HTTP header casing (change the value for the object with
"key": "Vary"), then deploy and monitor CDN cache hit-rate; if cache
fragmentation increases, implement a more targeted negotiation: add a custom
middleware header (e.g., "X-Preferred-Format" or similar) that maps incoming
Accept strings to a small set like "markdown" or "html", set that header on
responses and change Vary to that custom header instead to avoid high
cardinality.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the Vercel deployment configuration to ensure Accept-based markdown content negotiation works correctly when responses are cached at the CDN edge.

Changes:

  • Add a global Vary: accept response header so Vercel’s CDN caches HTML and markdown variants separately.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread vercel.json
},
{
"key": "Vary",
"value": "accept"
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

Consider using the canonical header name casing in the Vary value (i.e., Accept instead of accept) for consistency with HTTP specs and the external references mentioned in the PR description, and to reduce the chance of any intermediary treating the token case-sensitively.

Suggested change
"value": "accept"
"value": "Accept"

Copilot uses AI. Check for mistakes.
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.

2 participants