Vercel CSP Integration
Add CSP headers to your Vercel deployment using configuration or middleware.
Prerequisites
Section titled “Prerequisites”- Vercel project deployed
- Access to vercel.json or middleware
Option 1: vercel.json Headers
Section titled “Option 1: vercel.json Headers”Add to vercel.json in your project root:
{ "headers": [ { "source": "/(.*)", "headers": [ { "key": "Content-Security-Policy-Report-Only", "value": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; report-uri https://ingest.headerhawk.com/csp/YOUR_SITE_ID" } ] } ]}Option 2: Edge Middleware (Dynamic)
Section titled “Option 2: Edge Middleware (Dynamic)”For dynamic policies or nonces, use middleware.
Create 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-Report-Only", "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; report-uri https://ingest.headerhawk.com/csp/YOUR_SITE_ID" );
return response;}
export const config = { matcher: "/((?!api|_next/static|_next/image|favicon.ico).*)",};Per-Path Policies
Section titled “Per-Path Policies”Different policies for different routes:
{ "headers": [ { "source": "/admin/(.*)", "headers": [ { "key": "Content-Security-Policy", "value": "default-src 'self'; script-src 'self'" } ] }, { "source": "/(.*)", "headers": [ { "key": "Content-Security-Policy-Report-Only", "value": "default-src 'self'; script-src 'self' 'unsafe-inline'; report-uri https://ingest.headerhawk.com/csp/YOUR_SITE_ID" } ] } ]}With Nonces
Section titled “With Nonces”Generate nonces in middleware for inline scripts:
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 response = NextResponse.next();
response.headers.set( "Content-Security-Policy-Report-Only", `default-src 'self'; script-src 'self' 'nonce-${nonce}'; report-uri https://ingest.headerhawk.com/csp/YOUR_SITE_ID` );
// Pass nonce to your app response.headers.set("x-nonce", nonce);
return response;}Troubleshooting
Section titled “Troubleshooting”Headers Not Applied
Section titled “Headers Not Applied”- Redeploy after changing vercel.json
- Check the source pattern matches your routes
- Verify JSON syntax is valid (no trailing commas)
Middleware Not Running
Section titled “Middleware Not Running”- Check the matcher config
- Ensure middleware.ts is in the project root (or
src/) - Check Vercel function logs
Preview vs Production
Section titled “Preview vs Production”Headers apply to both preview and production deployments. Use environment variables for different policies:
const reportUri = process.env.VERCEL_ENV === "production" ? "https://ingest.headerhawk.com/csp/PROD_SITE_ID" : "https://ingest.headerhawk.com/csp/DEV_SITE_ID";Verification
Section titled “Verification”After deployment:
curl -I https://your-project.vercel.app | grep -i content-security-policy