Skip to content

fix: match HTML Content-Type case-insensitively when advertising markdown twin - #90

Merged
thepushkaraj merged 1 commit into
mainfrom
fix/html-content-type-case-insensitive
Aug 23, 2026
Merged

fix: match HTML Content-Type case-insensitively when advertising markdown twin#90
thepushkaraj merged 1 commit into
mainfrom
fix/html-content-type-case-insensitive

Conversation

@dodo-squirrels

Copy link
Copy Markdown

Summary

HTTP media types are case-insensitive (RFC 7231 §3.1.1.1), but five edge adapters gated markdown-twin advertisement on a case-sensitive contentType.includes("text/html"). An upstream that emits Content-Type: Text/HTML (or any non-lowercase spelling) skipped the negotiable-HTML block entirely, so the response was returned:

  • without Vary: Accept — a shared/CDN cache keyed only on URL could then serve HTML to a client that asked for markdown (or vice-versa), the exact cache-correctness bug the Vary header exists to prevent; and
  • without the Link rel="alternate"; type="text/markdown" header — so the markdown twin was never advertised to that page.

The check now lowercases the header before matching, bringing these adapters in line with the astro, nuxt, and sveltekit adapters which already did .toLowerCase().includes("text/html").

This was originally raised as a Copilot review nit on #73; it's a pre-existing latent issue (not introduced by #73) and applies to five adapters, so it's fixed here as a self-contained repo-wide cleanup.

Changes

Case-insensitive HTML Content-Type match in:

  • packages/cloudflare/src/worker.ts
  • packages/deno/src/handler.ts
  • packages/fastly/src/handler.ts
  • packages/netlify/src/worker.ts
  • packages/vercel/src/middleware.ts

Each adapter gets a regression test asserting that an HTML response with a mixed-case Content-Type: Text/HTML; charset=UTF-8 still receives both the Link alternate header and Vary: Accept.

Adds a patch changeset for the five affected packages.

Verification

  • All five adapter suites pass: cloudflare 27, deno 25, fastly 21, netlify 31, vercel 32 (each +1 new test).
  • Confirmed the new test fails without the src fix and passes with it (genuine regression guard).
  • tsc --noEmit clean on all five packages.
  • Behavior is unchanged for the already-lowercase text/html path; this only widens the match to spec-compliant casing variants.

Notes

  • No changes to astro/nuxt/sveltekit (already correct) or nextjs (no content-type gate).
  • Formatting: the repo's source is not currently prettier-clean; edits follow each file's existing style and do not touch unrelated lines, so prettier --write was intentionally not run to keep the diff surgical.

Created with Squirrels

…down twin

HTTP media types are case-insensitive (RFC 7231 §3.1.1.1), but the cloudflare,
deno, fastly, netlify and vercel adapters gated Vary: Accept and the Link
alternate header on a case-sensitive contentType.includes("text/html"). An
upstream emitting Content-Type: Text/HTML skipped the block, dropping Vary
(risking a shared cache serving HTML to a markdown client) and the markdown
twin Link header. The check now lowercases first, matching astro, nuxt and
sveltekit which already did this. Adds a mixed-case regression test per adapter.
@vercel

vercel Bot commented Aug 23, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
dualmark Ready Ready Preview Aug 23, 2026 6:10pm

Request Review

@thepushkaraj
thepushkaraj merged commit 6b8572e into main Aug 23, 2026
16 checks passed
@thepushkaraj
thepushkaraj deleted the fix/html-content-type-case-insensitive branch August 23, 2026 18:15
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.

1 participant