Skip to content

Commit bd606f1

Browse files
InfantLabclaude
andcommitted
fix(api-docs): allow Scalar CDN in CSP for /api-docs
Scalar's reference UI (served by Nitro at /api-docs) loads its bundle from cdn.jsdelivr.net, which was blocked by the global script-src CSP. Add a scoped CSP variant that allows jsdelivr + Google Fonts for the docs route only; the rest of the app keeps the strict policy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f627ddc commit bd606f1

1 file changed

Lines changed: 18 additions & 1 deletion

File tree

app/server/middleware/security-headers.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,37 @@ const CSP = [
1717
"frame-ancestors 'none'",
1818
].join("; ");
1919

20+
/**
21+
* Relaxed CSP for /api-docs — Nitro's Scalar UI loads its bundle from
22+
* cdn.jsdelivr.net and pulls fonts from Google Fonts. Kept scoped to the
23+
* docs route so the rest of the app keeps the strict CSP above.
24+
*/
25+
const DOCS_CSP = [
26+
"default-src 'self'",
27+
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net",
28+
"style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com",
29+
"img-src 'self' data: https:",
30+
"font-src 'self' data: https://fonts.gstatic.com https://cdn.jsdelivr.net",
31+
"connect-src 'self' https://cdn.jsdelivr.net",
32+
"frame-ancestors 'none'",
33+
].join("; ");
34+
2035
export default defineEventHandler((event) => {
2136
// Skip API routes that return JSON — they don't need CSP
2237
if (event.path.startsWith("/api/")) {
2338
return;
2439
}
2540

41+
const isDocs = event.path.startsWith("/api-docs");
42+
2643
setResponseHeaders(event, {
2744
"X-Content-Type-Options": "nosniff",
2845
"X-Frame-Options": "DENY",
2946
"X-XSS-Protection": "0",
3047
"Referrer-Policy": "strict-origin-when-cross-origin",
3148
"Permissions-Policy":
3249
"camera=(), microphone=(self), geolocation=(), payment=()",
33-
"Content-Security-Policy": CSP,
50+
"Content-Security-Policy": isDocs ? DOCS_CSP : CSP,
3451
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
3552
});
3653
});

0 commit comments

Comments
 (0)