Add Vary: Accept header and document nginx content negotiation config#3548
Add Vary: Accept header and document nginx content negotiation config#3548mich-elle-luna wants to merge 3 commits into
Conversation
- Add Vary: Accept to all hugo serve responses so caches know responses vary by Accept header - Serve .html.md files with Content-Type: text/markdown during local dev - Document the production nginx config needed for HTTP-level content negotiation (Accept: text/markdown → serve index.html.md) in AI_AGENT_DEVELOPER_GUIDE.md The <link rel="alternate" type="text/markdown"> element is already generated for every page by the AlternativeOutputFormats loop in baseof.html — no changes needed there. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🛡️ Jit Security Scan Results✅ No security findings were detected in this PR
Security scan by Jit
|
|
I don't know much about the server config but below is what the LLMs had to say about it. This might be a job for the new Content negotiation review — verdictTwo independent reviews (Claude + Codex) of the content-negotiation setup in Agreed findings (both reviewers)
Caught by Codex (highest impact)
Caught by Claude
Suggested priority
Reviewed by Claude (Opus 4.8) and Codex; no code changed. |
- Add home to [outputs] so the homepage and taxonomy pages emit
.html.md and .json variants (previously fell back to [HTML, RSS])
- Fix nginx Content-Type: use types{} block instead of add_header so
.md files get the correct MIME from the types table rather than a
header that can't override nginx's own content-type
- Add always flag to all Vary: Accept add_header directives so the
header is sent on non-2xx responses (404/5xx) for cache correctness
- Harden Accept map: exclude q=0 (explicit refusal) to avoid serving
markdown to clients that don't want it
- Fix rewrite regex: replace [^.]+ (skips dot-paths) with [^.]*[^/]
so version directories (7.4/) and dotless paths are handled correctly
- Clarify dev server limitation: hugo serve sets headers only and does
not perform Accept-based rewriting; full negotiation requires nginx
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
paoloredis
left a comment
There was a problem hiding this comment.
We're going to need the following files for this change to work:
layouts/home.mdlayouts/home.json
I'm not sure who the nginx instructions in the README are for. Is it for the agents?
|
Hi thanks for reviewing this. It is for agents, but can't break anything for humans of course. This comes from the isitagentready.com reporting site by cloudflare, so if you run the checks on that site, this PR (and the other PR for the link header) come back as areas we can improve. So if we can do this and improve our score to show we can get the same score as these other hosted docs sites, that would be great. |
- Add layouts/home.md and layouts/home.json so Hugo generates Markdown and JSON output formats for the homepage (config.toml lists both in home outputs but had no corresponding templates) - Remove nginx content negotiation section from AI_AGENT_DEVELOPER_GUIDE.md; production routing config belongs in ops/infra docs, not the AI agent guide Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d92baf2. Configure here.
| {{- with .File -}} | ||
| {{- $baseName := .ContentBaseName -}} | ||
| {{- if or (not $baseName) (eq $baseName "_index") -}} | ||
| {{- $childId = $.Title | urlize -}} |
There was a problem hiding this comment.
Wrong child id in loop
Medium Severity
In the children loop, when a child’s ContentBaseName is missing or _index, id is set from $.Title (the home page) instead of the child page’s title. Top-level section entries can share the same wrong id in home index.json, which misleads consumers of the JSON/NDJSON feed.
Reviewed by Cursor Bugbot for commit d92baf2. Configure here.
| page = ["HTML", "Markdown", "JSON"] | ||
|
|
||
| # Content negotiation headers (hugo serve only). | ||
| # For production nginx config see AI_AGENT_DEVELOPER_GUIDE.md. |
There was a problem hiding this comment.
This comment is outdated now
|
I have now deployed a transform rule on Cloudflare so now the docs honor the |
|
isitagentready.com still reports |


The element is already generated for every page by the AlternativeOutputFormats loop in baseof.html — no changes needed there.
Note
Low Risk
Build-time Hugo templates and local dev server headers only; no runtime app, auth, or data-path changes in this diff.
Overview
Extends AI/LLM alternate formats to the site home page and aligns local dev with Accept-based content negotiation.
config.tomlnow listshomein[outputs]with HTML, RSS, Markdown, and JSON (matching sections/pages). A trailing[server]block configureshugo serveonly:Vary: Accepton all routes andContent-Type: text/markdownfor**.html.md**, with a pointer to production nginx setup in the developer guide.New
layouts/home.mdandlayouts/home.jsongenerate markdown (metadata block + processed body viaprocess-markdown-content.html) and structured JSON (home metadata, processed content, and achildrenlist of top-level pages with id/title/url/summary), following the same patterns as_default/sectionandsingletemplates.Reviewed by Cursor Bugbot for commit d92baf2. Bugbot is set up for automated code reviews on this repo. Configure here.