Skip to content

fix: always set Vary: Accept on HTML even when the Link header is disabled#73

Open
abhay-codes07 wants to merge 1 commit into
dodopayments:mainfrom
abhay-codes07:fix/vary-accept-independent-of-link-header
Open

fix: always set Vary: Accept on HTML even when the Link header is disabled#73
abhay-codes07 wants to merge 1 commit into
dodopayments:mainfrom
abhay-codes07:fix/vary-accept-independent-of-link-header

Conversation

@abhay-codes07

Copy link
Copy Markdown

Summary

Turning off the Link header (enableLinkHeader: false, or injectLinkHeader: false on nextjs/sveltekit) also dropped Vary: Accept from negotiable HTML responses, because the Vary logic was nested inside the Link-header block. That is a cache-correctness bug: a shared cache keyed on the URL can then serve HTML to a markdown client or the reverse. This decouples the two so Vary is always set.

Closes #72.

Changes

  • cloudflare, netlify, vercel, nextjs and sveltekit now always set Vary: Accept on a negotiable HTML response, and gate only the Link header on the feature flag. This matches the deno and fastly adapters, which already did it this way.
  • Added a regression test in each of the five adapters: with the Link header disabled, a browser request to an HTML page still gets Vary: Accept and no Link header.

Type

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change
  • Spec change (requires bump in AEO_SPEC_VERSION)
  • Docs / examples / tooling only

Verification

  • bun run build passes
  • bun run test passes
  • bun run typecheck passes
  • If touching examples: ran dualmark verify (no examples touched)
  • If touching /spec/: ran sync-spec (no spec files touched)

Changeset

  • Added a changeset (patch for the five adapters)

cc @thepushkaraj @aagarwal1012. This follows the same pattern deno/fastly already use, so it should be low risk, but happy to adjust if you would rather I factor the shared Vary-append into a small core helper.

…abled

Several adapters gated the Vary: Accept header on the Link-header feature
flag, so setting enableLinkHeader/injectLinkHeader to false left negotiable
HTML responses with no Vary header. A shared cache keyed on URL alone could
then serve HTML to a markdown client or the reverse.

Per content-negotiation.md section 3, Vary: Accept is required on every
response whose representation depends on Accept, independent of the Link
header. cloudflare, netlify, vercel, nextjs and sveltekit now always emit
Vary: Accept on negotiable HTML and gate only the Link header, matching the
deno and fastly adapters.
Copilot AI review requested due to automatic review settings July 1, 2026 02:11
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

@abhay-codes07 is attempting to deploy a commit to the Dodo Payments Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown

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 fixes a cache-correctness bug where disabling Link-header injection also (incorrectly) removed Vary: Accept from negotiable HTML responses, potentially causing shared caches to serve HTML to markdown clients (and vice versa). The change decouples Vary: Accept from Link-header injection across several adapters and adds regression tests to lock the behavior in.

Changes:

  • Decouple Vary: Accept from Link-header injection in the cloudflare, netlify, vercel, nextjs, and sveltekit adapters so Vary: Accept is always emitted for negotiable HTML.
  • Add regression tests in each adapter to verify: with Link disabled, HTML responses still include Vary: Accept and do not include Link.
  • Add a changeset for patch releases of the affected adapter packages.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/vercel/src/middleware.ts Always appends Vary: Accept for negotiable HTML; gates only Link on enableLinkHeader.
packages/vercel/test/middleware.test.ts Adds regression test for Vary: Accept when Link header is disabled.
packages/sveltekit/src/handle.ts Ensures Vary: Accept is set even when injectLinkHeader is false (while keeping Link injection optional).
packages/sveltekit/test/handle.test.ts Adds regression test for Vary: Accept when Link header is disabled.
packages/nextjs/src/middleware.ts Moves appendVaryAccept outside the Link-header injection block so it always runs on the negotiable HTML branch.
packages/nextjs/test/middleware.test.ts Adds regression test for Vary: Accept when Link header is disabled.
packages/netlify/src/worker.ts Ensures Vary: Accept is set independently of enableLinkHeader; Link injection remains gated.
packages/netlify/test/worker.test.ts Adds regression test for Vary: Accept when Link header is disabled.
packages/cloudflare/src/worker.ts Ensures Vary: Accept is set independently of enableLinkHeader; Link injection remains gated.
packages/cloudflare/test/worker.test.ts Adds regression test for Vary: Accept when Link header is disabled.
.changeset/vary-accept-independent-of-link-header.md Patch changeset documenting the behavior fix across the five adapters.

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

Comment on lines 290 to 294
const ct = upstreamResponse.headers.get("content-type");
// Passthrough responses (e.g. NextResponse.next()) have no content-type yet —
// always inject. For concrete responses, only inject on text/html.
if (!ct || ct.includes("text/html")) {
const mdPath = toMarkdownPath(pathname);
enableLinkHeader &&
!shouldSkip(pathname, skipPrefixes, skipExtensions) &&
!pathname.endsWith(".md") &&
originResponse.headers.get("content-type")?.includes("text/html")
enableLinkHeader &&
!shouldSkip(pathname, skipPrefixes, skipExtensions) &&
!pathname.endsWith(".md") &&
upstreamResponse.headers.get("content-type")?.includes("text/html")
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.

Vary: Accept dropped on HTML when the Link header is disabled (spec section 3)

2 participants