Skip to content

fix(web): serve markdown before the filesystem check - #7

Merged
KacemMathlouthi merged 2 commits into
mainfrom
fix/markdown-negotiation
Aug 21, 2026
Merged

KacemMathlouthi merged 2 commits into
mainfrom
fix/markdown-negotiation

Conversation

@KacemMathlouthi

@KacemMathlouthi KacemMathlouthi commented Aug 21, 2026 •

Copy link
Copy Markdown
Owner

#6 shipped the markdown twins but the negotiation never fired in production: Accept: text/markdown on /usage still returned HTML, while Vary was applied correctly.

The cause is Vercel's routing order: redirects, headers, filesystem, then rewrites. cleanUrls resolves /usage to usage.html at the filesystem stage, and rewrites are only consulted for paths that matched no file, so a rewrite can never take over a URL a static file already answers. The has condition was fine; it was simply never reached.

Legacy routes run before the filesystem check, so that is what this uses. It also fixes the second essential gap for free: only routes can pair a dest with a status, so an agent that asks for markdown on a dead URL now gets 404.md with a real 404 status, instead of the HTML page.

routes cannot be combined with cleanUrls, trailingSlash, headers or rewrites, so those are expressed in the table: asset caching, /x.html and /x/ canonicalisation, the six page URLs, Vary: Accept on every negotiable response, and the 404 fallbacks.

Verification

The routing layer only runs on Vercel, so this was checked three ways before pushing:

  • Both configs validate against Vercel's published schema (openapi.vercel.sh/vercel.json).
  • Both pass normalizeRoutes from @vercel/routing-utils, the check Vercel's own build runs.
  • A simulator walks requests through the table against the real dist tree:
html /                 -> 200 /index.html            vary
md   /                 -> 200 /index.md   text/markdown  vary
html /usage            -> 200 /usage.html            vary
md   /usage            -> 200 /usage.md   text/markdown  vary
md   /about /contact /privacy /playground -> 200 *.md text/markdown vary
html /usage.html       -> 308 /usage
html /index.html       -> 308 /
html /about/           -> 308 /about
html /llms.txt /sitemap.xml /robots.txt /r/registry.json -> 200
html /nope             -> 404 /404.html
md   /nope             -> 404 /404.md    text/markdown

Worth confirming against the deployed site once merged, since only the platform proves the last mile.


Summary by cubic

Fixes content negotiation so pages serve Markdown before the filesystem check on Vercel. Previously, Accept: text/markdown returned HTML; now the same URL returns its .md twin with Content-Type: text/markdown, and dead URLs return 404.md with a real 404.

  • Switches from rewrites/headers/cleanUrls/trailingSlash to legacy routes so negotiation runs before the filesystem. Adds 308 canonicalization for /x.html and /x/ to /x, and keeps long‑cache for /assets/*.
  • Applies Vary: Accept, Accept-Encoding to negotiated routes and sets Content-Type: text/markdown; charset=utf-8 for .md.
  • Adds catch‑alls after handle: filesystem: Markdown clients get /404.md with 404; others get /404.html with 404.
  • Migration: when adding a page, add its Markdown negotiation entry to both vercel.json (authoritative) and apps/web/vercel.json (mirror). Verify in the deployed environment that Accept: text/markdown on /, /usage, /playground, /about, /contact, /privacy returns the .md body.

Written for commit 228f78a. Summary will update on new commits.

Review in cubic

@vercel

vercel Bot commented Aug 21, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
benday-playground Ready Ready Preview Aug 21, 2026 10:44pm

@KacemMathlouthi
KacemMathlouthi merged commit ed1ed19 into main Aug 21, 2026
4 checks passed

This branch was successfully deployed

1 active deployment
Preview — 228f78a1 Deployed Aug 21, 2026 by vercel[bot]
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