'unsafe-inline' in script-src means any <script> tag injected by an attacker — through XSS, a compromised third-party dependency, or a DOM mutation — runs with full page context. It effectively negates CSP's primary purpose: a policy exists but an XSS payload executes anyway. OWASP A03 (Injection) flags this as a common misconfiguration where developers add a CSP for compliance but inadvertently include unsafe-inline to silence warnings, creating a security theater scenario. CWE-79 (XSS) and CWE-1336 (improper neutralization) both apply. The correct fix — cryptographically random nonces per request — is supported natively by all major frameworks.
High because `unsafe-inline` in `script-src` defeats XSS protection even when a CSP is present, reducing it to security theater.
Replace 'unsafe-inline' with per-request cryptographic nonces in middleware.ts. The nonce must be random on every request — static nonces provide zero protection.
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const csp = `default-src 'self'; script-src 'self' 'nonce-${nonce}'`
const response = NextResponse.next()
response.headers.set('Content-Security-Policy', csp)
response.headers.set('x-nonce', nonce)
return response
}
Pass the nonce to <Script nonce={nonce}> components via the request headers. Move any remaining inline scripts to external files or use hash-based allowlisting if the inline content is truly static.
ID: security-headers.headers.csp-no-unsafe-inline
Severity: high
What to look for: If a CSP is present, enumerate all directives in the policy and examine the script-src directive for 'unsafe-inline'. Note: Next.js uses nonce-based inline scripts by default, which is acceptable without unsafe-inline.
Pass criteria: CSP script-src contains 0 occurrences of 'unsafe-inline', OR uses nonce-based ('nonce-*') or hash-based ('sha256-*') inline script allowlisting that overrides the unsafe-inline directive.
Fail criteria: CSP includes at least 1 'unsafe-inline' in script-src without nonce or hash supplementation.
Skip (N/A) when: Skip when csp-present result is fail or skip — there is no CSP to evaluate for unsafe-inline directives.
Detail on fail: "CSP script-src includes 'unsafe-inline' which defeats much of CSP's XSS protection" or "CSP uses 'unsafe-inline' and 'unsafe-eval' in script-src"
Remediation: unsafe-inline in script-src allows any inline script to run, which largely defeats CSP's purpose. Instead, use nonces or hashes. In Next.js, generate a cryptographically random nonce per-request in middleware:
// middleware.ts — generate a nonce per request
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const csp = `default-src 'self'; script-src 'self' 'nonce-${nonce}'`
const response = NextResponse.next()
response.headers.set('Content-Security-Policy', csp)
response.headers.set('x-nonce', nonce) // pass to server components
return response
}
The nonce value must be cryptographically random and unique per request — never use a static nonce value, as that provides no security benefit. See the Next.js CSP documentation for the complete generateCspNonce() pattern and how to pass the nonce to <Script> components.
If you have inline scripts, either move them to external files or use the nonce approach your framework provides.