Use Accept header for skills HTML/markdown negotiation#1454
Conversation
Sec-Fetch-Mode/Sec-Fetch-Dest weren't reliable for distinguishing browsers from curl/agents on the CDN edge. Switch to the Accept header: browsers send `text/html,...` while curl/fetch/agents send `*/*` or omit it, so we serve HTML only when text/html is explicitly listed. Vary header updated to match.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe skills route now uses the request Accept header for content negotiation and caching: COMMON_HEADERS Vary is set to Accept, and the route serves HTML only when text/html appears before markdown-preferring media types in Accept. ChangesAccept Header Content Negotiation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryReplaces the
Confidence Score: 4/5Safe to merge; the change is small and the new Accept-based negotiation is more reliable than the Sec-Fetch headers it replaces. The logic correctly handles all common client types (browsers, curl, fetch). The only gap is a missing apps/skills/src/app/route.ts — specifically the Important Files Changed
Sequence DiagramsequenceDiagram
participant Browser
participant curl/agent
participant CDN
participant Route as route.ts GET
Browser->>CDN: GET / Accept: text/html,...
CDN->>Route: GET / Accept: text/html,...
Route->>Route: wantsHtml() → true
Route-->>CDN: 200 text/html + Vary: Accept
CDN-->>Browser: HTML landing page (cached by Accept variant)
curl/agent->>CDN: "GET / Accept: */* (or omitted)"
CDN->>Route: "GET / Accept: */*"
Route->>Route: wantsHtml() → false
Route-->>CDN: 200 text/markdown + Vary: Accept
CDN-->>curl/agent: SKILL.md (cached by Accept variant)
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
apps/skills/src/app/route.ts:438
The media-type token extracted from each `Accept` segment is not trimmed before the equality check. RFC 7231 allows optional whitespace around the semicolon, so a value like `text/html ;q=0.9` (space before `;`) would produce `"text/html "` from `split(";")[0]`, which fails the strict `=== "text/html"` test and falls back to markdown instead of HTML. Adding a second `.trim()` after the split makes the match robust.
```suggestion
return accept.split(",").some((part) => part.trim().split(";")[0].trim() === "text/html");
```
Reviews (1): Last reviewed commit: "Use Accept header for skills HTML/markdo..." | Re-trigger Greptile |
There was a problem hiding this comment.
Pull request overview
This PR updates the skill.stack-auth.com root route’s content negotiation so the CDN can reliably cache and serve the HTML landing page to browsers while returning the canonical SKILL.md markdown to curl/agents, using the Accept header rather than Sec-Fetch-*.
Changes:
- Switches content negotiation logic from
Sec-Fetch-Mode/Sec-Fetch-Destto detecting an explicittext/htmlinAccept. - Updates CDN caching behavior by changing the response
Varyheader toAccept.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/skills/src/app/route.ts`:
- Around line 434-439: The wantsHtml function fails to account for optional
whitespace and case-insensitive media types in Accept header tokens; update the
logic in wantsHtml to trim the media type after splitting on ";" and compare
using a case-insensitive match (e.g., call .trim() and .toLowerCase() on the
media type before comparing to "text/html") so entries like "text/html ; q=0.9"
and different-cased media types are correctly detected.
🪄 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: a4b01249-fc81-4f49-8446-21d36eb6ce9d
📒 Files selected for processing (1)
apps/skills/src/app/route.ts
- Trim + lowercase each media-type token so `text/html ;q=0.9` and mixed casing still match. - Require text/html to appear before */*, text/plain, text/markdown, or text/x-markdown so a client that prefers markdown still gets it.
Summary
Follow-up to #1452.
Sec-Fetch-Mode/Sec-Fetch-Destdidn't reliably split HTML vs. markdown at the CDN edge, so curl could still get the HTML landing page. Switch to theAcceptheader:Accept: text/html,...on top-level navigations.curl,fetch(), and agent fetchers send*/*or omitAccept.text/htmlis explicitly listed; everything else getsSKILL.md.Varyupdated toAcceptto match.Test plan
curl -sSL https://skill.stack-auth.com/ | head -3returns markdown frontmatterhttps://skill.stack-auth.com/still shows the HTML landing pageSummary by CodeRabbit
Bug Fixes
Chores