How to Add Security Headers in Next.js
Next.js does not add security headers by default. Without them, your app is vulnerable to XSS, clickjacking, MIME sniffing, and other browser-based attacks. This guide shows you how to add comprehensive security headers using next.config.js and middleware.
Find security issues automatically before attackers do.
Follow These Steps
Add basic security headers in next.config.js
Configure headers that apply to all routes.
// next.config.js
const securityHeaders = [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-XSS-Protection', value: '0' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' }
]
module.exports = {
async headers() {
return [{ source: '/(.*)', headers: securityHeaders }]
}
}X-XSS-Protection is set to 0 because modern browsers have deprecated it. CSP is the proper replacement.
Add Content Security Policy with nonce-based script loading
Use middleware to generate a unique nonce for each request, allowing inline scripts while blocking XSS.
// middleware.ts
import { NextResponse } from 'next/server'
export function middleware(request: Request) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const cspHeader = [
`default-src 'self'`,
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`,
`style-src 'self' 'unsafe-inline'`,
`img-src 'self' blob: data:`,
`font-src 'self'`,
`connect-src 'self'`,
`frame-ancestors 'none'`,
`base-uri 'self'`,
`form-action 'self'`
].join('; ')
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-nonce', nonce)
const response = NextResponse.next({ request: { headers: requestHeaders } })
response.headers.set('Content-Security-Policy', cspHeader)
return response
}Pass the nonce to Script components
Use the nonce from the middleware in your Script tags.
// app/layout.tsx
import { headers } from 'next/headers'
import Script from 'next/script'
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const headersList = await headers()
const nonce = headersList.get('x-nonce') || ''
return (
<html lang="en">
<body>
{children}
<Script nonce={nonce} src="/analytics.js" />
</body>
</html>
)
}Configure Permissions-Policy based on your app needs
Adjust the Permissions-Policy header to match what browser APIs your app actually uses.
// If your app uses geolocation:
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=(self)' }
// If your app uses camera (video chat):
{ key: 'Permissions-Policy', value: 'camera=(self), microphone=(self), geolocation=()' }
// If your app uses none of these (most common):
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' }Check if your app uses geolocation, camera, or microphone before setting Permissions-Policy. Blocking APIs your app needs will break functionality.
Verify headers are applied
Check that headers are present on your deployed site.
# Check headers with curl
curl -I https://yourdomain.com
# Or use browser DevTools > Network tab > click any request > HeadersRun a VAS scan to automatically verify all security headers are configured correctly.
What You'll Achieve
Your Next.js app has comprehensive security headers including X-Content-Type-Options, X-Frame-Options, HSTS, Referrer-Policy, Permissions-Policy, and a nonce-based Content Security Policy.
Common Mistakes to Avoid
Mistake
Using unsafe-inline for script-src in CSP
Fix
Use nonce-based CSP instead. Generate a unique nonce per request in middleware and pass it to Script components.
Mistake
Setting Permissions-Policy that blocks APIs your app uses
Fix
Check if your app uses geolocation, camera, or microphone before blocking them. Use (self) instead of () for APIs your app needs.
Mistake
Only adding headers in development, not production
Fix
next.config.js headers apply to all environments. Verify with curl or DevTools after deploying to production.
Frequently Asked Questions
Do I need both next.config.js headers and middleware?
Use next.config.js for static headers (HSTS, X-Frame-Options). Use middleware for dynamic headers like CSP with per-request nonces. They work together.
Will security headers break my Next.js app?
Most headers are safe to add. CSP is the most likely to cause issues if configured too restrictively. Start with report-only mode and monitor for violations before enforcing.
Does Vercel add security headers automatically?
No. Vercel provides HTTPS but does not add application security headers. You must configure them in your Next.js application.
Ready to Secure Your App?
VAS automatically scans your deployed app for the security issues covered in this guide. Get actionable results in minutes.
Start Security Scan