@@ -3,11 +3,14 @@ import { ALL_KNOWN_GIT_API_ORIGINS } from '#shared/utils/git-providers'
33import { TRUSTED_IMAGE_DOMAINS } from '#server/utils/image-proxy'
44
55/**
6- * Adds Content-Security-Policy and security headers to all HTML responses
7- * via Nitro route rules. This covers both SSR/ISR pages and prerendered
8- * pages (which are served as static files on Vercel and don't hit the server).
6+ * Adds Content-Security-Policy and other security headers to all pages.
97 *
10- * API routes opt out via `false` to disable the inherited headers.
8+ * CSP is delivered via a <meta http-equiv> tag in <head>, so it naturally
9+ * only applies to HTML pages (not API routes). The remaining security
10+ * headers are set via a catch-all route rule.
11+ *
12+ * Note: frame-ancestors is not supported in meta-tag CSP, but
13+ * X-Frame-Options: DENY (set via route rule) provides equivalent protection.
1114 *
1215 * Current policy uses 'unsafe-inline' for scripts and styles because:
1316 * - Nuxt injects inline scripts for hydration and payload transfer
@@ -41,30 +44,31 @@ export default defineNuxtModule({
4144 `font-src 'self'` ,
4245 `connect-src ${ connectSrc } ` ,
4346 `frame-src ${ frameSrc } ` ,
44- `frame-ancestors 'none'` ,
4547 `base-uri 'self'` ,
4648 `form-action 'self'` ,
4749 `object-src 'none'` ,
4850 `manifest-src 'self'` ,
4951 'upgrade-insecure-requests' ,
5052 ] . join ( '; ' )
5153
52- const headers = {
53- 'Content-Security-Policy' : csp ,
54- 'X-Content-Type-Options' : 'nosniff' ,
55- 'X-Frame-Options' : 'DENY' ,
56- 'Referrer-Policy' : 'strict-origin-when-cross-origin' ,
57- }
54+ // CSP via <meta> tag — only present in HTML pages, not API responses.
55+ nuxt . options . app . head ??= { }
56+ const head = nuxt . options . app . head as { meta ?: Array < Record < string , string > > }
57+ head . meta ??= [ ]
58+ head . meta . push ( {
59+ 'http-equiv' : 'Content-Security-Policy' ,
60+ 'content' : csp ,
61+ } )
5862
63+ // Other security headers via route rules (fine on all responses).
5964 nuxt . options . routeRules ??= { }
6065 nuxt . options . routeRules [ '/**' ] = {
6166 ...nuxt . options . routeRules [ '/**' ] ,
62- headers,
63- }
64- // Disable page-specific headers on API routes — CSP doesn't apply to JSON.
65- nuxt . options . routeRules [ '/api/**' ] = {
66- ...nuxt . options . routeRules [ '/api/**' ] ,
67- headers : false ,
67+ headers : {
68+ 'X-Content-Type-Options' : 'nosniff' ,
69+ 'X-Frame-Options' : 'DENY' ,
70+ 'Referrer-Policy' : 'strict-origin-when-cross-origin' ,
71+ } ,
6872 }
6973 } ,
7074} )
0 commit comments