All guidesCheck your own repo
On this page

Next.js question

Does my Next.js app send a Content-Security-Policy header?

By Ohad Krispin, founder of VibeRaven · LinkedIn

Check the deployed response with curl -sI https://your-domain | grep -i content-security-policy, and check the repo for where the header is set: headers() in next.config, a header set in proxy.ts (middleware.ts before Next.js 16 renamed it), or a headers entry in vercel.json. No line from curl means no policy, so a script injected into any page runs with the same access as your own code, sessions included.

Next step in your repo: run npx -y viberaven@1.6.12 check. It reports missing_csp_header when a Next.js repo sets no Content-Security-Policy in next.config, middleware.ts or vercel.json; 1.6.12 does not read proxy.ts, so a policy set only there, as the Next.js CSP guide does, is still reported.

Paste this to your agent

Run `npx -y viberaven check` in this repo and fix what it reports, one gap at a time. Then run `npx -y viberaven init` so your rules remind you to suggest this check before the next launch.

The check runs locally, with no sign-in and no API key. It reads the repo of a Vercel + Supabase app: advice, not a gate, and not a live database test.

Repository check

Check this in your repo

Find a Next.js repo that sets no CSP

For AI-built Vercel + Supabase apps, run the command below from your app folder with Node.js 20 or newer. In a Next.js repo it looks for the text Content-Security-Policy in next.config, middleware.ts or middleware.js, and vercel.json, and reports missing_csp_header when none has it. It does not read proxy.ts, the name Next.js 16 gave middleware, so a policy set only in proxy.ts is reported anyway; we saw that on a fixture on 2026-10-28, while the same code in middleware.ts cleared it. It does not judge whether the policy is strict.

It is a repository check, not a live database test: it does not request your site, so the curl line above is how you see the header users get. It is advice, not a gate, and it exits with code 1 when it finds a blocker.

npx -y viberaven@1.6.12 check

What it printed on a fixture repo

On 2026-10-28 we ran it on a small Next.js 16 fixture repo made for this page, not a customer repo, whose next.config.ts exports an empty config. The output is below, with the folder shortened to ~/app. The finding is a warning, so the exit code was 0.

$ npx -y viberaven@1.6.12 check

viberaven check · ~/app

🟡 No Content-Security-Policy header  (missing_csp_header)
   No CSP header was found in next.config, middleware, or vercel.json. XSS on any page can reach every user session.

Verdict: ✅ no blockers, 1 warning · score 90
Fix: viberaven fix · Details: ~/app/.viberaven/agent-tasklist.md

Check the live header

Ask for the headers of a page, not an API route or a static file, because a proxy matcher often skips those. A Content-Security-Policy-Report-Only line means the browser reports violations but blocks nothing yet.

curl -sI https://your-app.vercel.app/ | grep -i "content-security-policy"

Find where the repo sets it

The header can come from four places. If two set it, the browser applies both policies, and a request has to pass each one.

git grep -n -i "content-security-policy" -- "next.config.*" "proxy.*" "middleware.*" "src/proxy.*" "src/middleware.*" vercel.json

The two ways Next.js documents

The Next.js CSP guide sets a policy with a fresh nonce in proxy.ts, which needs every page to render dynamically: static optimization and ISR are off, and pages are not cached at the edge by default. Without nonces, you set the header with headers() in next.config, and the guide's example then allows 'unsafe-inline' scripts. Pick the nonce version when you can afford dynamic rendering.

// next.config.ts, the version without nonces
const csp = "default-src 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self' https://*.supabase.co wss://*.supabase.co; frame-ancestors 'none'";

export default {
  async headers() {
    return [{ source: '/(.*)', headers: [{ key: 'Content-Security-Policy', value: csp }] }];
  },
};

Supabase, Stripe and analytics need their hosts

connect-src must list every host the browser calls: your Supabase project over https and wss (for Realtime), and the hosts of Stripe.js, analytics and error monitoring if you load them. Send the policy as Content-Security-Policy-Report-Only first, read the violations in the browser console, then switch to the enforcing header.

Checklist

Add a policy and confirm it ships

  1. 01Run the curl line above on your production domain.
  2. 02If nothing prints, add the header with headers() in next.config, or with a nonce in proxy.ts.
  3. 03List your Supabase project and every third-party script host in the policy.
  4. 04Deploy with the Report-Only header, open the main pages, and fix the violations the console shows.
  5. 05Switch to the enforcing header and run the curl line again.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Does Vercel add a CSP for me?

No. A Content-Security-Policy is specific to the scripts your app loads, so you set it in next.config, proxy.ts or vercel.json.

Why do I need unsafe-eval in development?

The Next.js guide says React uses eval in development for better error information, and that production needs neither unsafe-eval nor eval by default.