Missing security headers leave browsers without the instructions needed to block clickjacking, MIME-type sniffing attacks, and content injection — gaps that attackers exploit through iframe embeds, malicious redirects, and injected scripts. NIST 800-53 rev5 SC-8 covers transmission integrity and SI-3 covers malicious code protection; both are satisfied in part by response headers that tell browsers what to trust. FedRAMP rev5 SC-8 applies to all cloud systems serving federal users. OWASP A05 (Security Misconfiguration) and CWE-693 (Protection Mechanism Failure) specifically call out absent security headers. A CSP header alone can neutralize most XSS payloads that slip through input validation.
High because absent security headers expose every page visitor to clickjacking, MIME-confusion exploits, and reflected XSS attacks that require no server-side vulnerability to execute.
Configure all four required headers in next.config.js at the framework level so they apply to every route including API responses.
// next.config.js
const securityHeaders = [
{ key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:" },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains; preload' }
]
const nextConfig = {
async headers() {
return [{ source: '/:path*', headers: securityHeaders }]
}
}
export default nextConfig
Verify the headers are live in production using curl -I https://yourdomain.com — not just in dev. If your CSP blocks legitimate inline scripts, migrate to nonce-based CSP rather than weakening to unsafe-inline for all sources.
ID: gov-fisma-fedramp.system-protection.security-headers
Severity: high
What to look for: Count all locations where security headers are configured: next.config.js/next.config.ts, middleware.ts, vercel.json, netlify.toml, or API configuration. For each header, quote the actual configured value found. Look for Content-Security-Policy (CSP), X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security (HSTS). Count how many of the 4 required headers are present.
Pass criteria: All 4 key headers are present and correctly configured: CSP with default-src 'self' or more restrictive, X-Frame-Options set to DENY or SAMEORIGIN, X-Content-Type-Options set to nosniff, HSTS with max-age of at least 31536000. Report even on pass: list all 4 headers and their configured values.
Fail criteria: Any of the 4 headers is missing, or headers are misconfigured (e.g., CSP allows all sources with *, X-Frame-Options is ALLOWALL). Do not pass when headers are configured only in development config but not in production.
Skip (N/A) when: Never — security headers apply to all web projects.
Detail on fail: List which headers are missing or misconfigured. Example: "CSP header missing. X-Frame-Options set to ALLOWALL (should be DENY or SAMEORIGIN). HSTS header present. 2 of 4 headers configured."
Remediation: Add security headers in next.config.js or middleware:
// next.config.js
const nextConfig = {
headers: async () => [
{
source: '/:path*',
headers: [
{ key: 'Content-Security-Policy', value: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' }
]
}
]
}
export default nextConfig
Or use middleware.ts:
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const response = NextResponse.next()
response.headers.set('Content-Security-Policy', "default-src 'self'")
response.headers.set('X-Frame-Options', 'DENY')
response.headers.set('X-Content-Type-Options', 'nosniff')
response.headers.set('Strict-Transport-Security', 'max-age=31536000')
return response
}