PropChain enforces a strict Content Security Policy to prevent XSS attacks. The policy is applied via Next.js middleware.
default-src 'self'- Only same-origin resources by defaultimg-src 'self' data: ipfs:- Images from self, data URIs, and IPFSscript-src 'self' 'nonce-...'- Only same-origin scripts with valid noncestyle-src 'self' 'unsafe-inline'- Styles from self (inline allowed for Tailwind)font-src 'self' data:- Fonts from self and data URIsconnect-src 'self'- API connections only to same originframe-src 'self'- Frames only from same originbase-uri 'self'- Base URIs restricted to same originform-action 'self'- Form submissions only to same originframe-ancestors 'self'- Framing only by same origin
Whether the enforcing header is sent is controlled by CSP_ENFORCE — see the table below. Since issue #1107, the secure default enforces the CSP in production and staging without any configuration.
CSP violations are reported to POST /api/csp-report. In development mode, reports are logged to the console.
The middleware uses the environment variable CSP_ENFORCE to toggle between enforcement and report-only modes. As of issue #1107 it has a secure, environment-aware default:
CSP_ENFORCE |
Environment | Header Sent | Behaviour |
|---|---|---|---|
"true" |
Any | Content-Security-Policy |
Violations are blocked by the browser |
"false" |
Any | No CSP header | CSP is disabled entirely (warns at boot outside development) |
| unset / empty | production, staging | Content-Security-Policy |
Secure default: enforced |
| unset / empty | development | No CSP header | Dev convenience: no nonce bookkeeping while coding |
Default decision (#1107): CSP is enforced in production and staging by default — no configuration required. Developers only need to set
CSP_ENFORCE=trueexplicitly when they want to test the policy locally. An explicit"false"in production logs a warning at boot and atnpm run validate:env.
Note: In development (
NODE_ENV=development), thescript-srcdirective includes'unsafe-eval'to support hot reload. This is never included in production builds.
# .env.local (development — CSP disabled by default for easier debugging)
# CSP_ENFORCE=true # uncomment to test CSP enforcement locally
# .env.production (optional — already enforced by the secure default)
# CSP_ENFORCE=trueAfter deploying, confirm the policy is live:
# 1. Enforcing header present (secure default in production/staging):
curl -sI https://your-domain.example.com/ | grep -i content-security-policy
# → content-security-policy: default-src 'self'; script-src 'self' 'nonce-...'; ...
# 2. The nonce is unique per request:
curl -sI https://your-domain.example.com/ | grep -oiP "nonce-\K[A-Za-z0-9+/=]+"
curl -sI https://your-domain.example.com/ | grep -oiP "nonce-\K[A-Za-z0-9+/=]+"
# → two different values = per-request nonce working
# 3. Exclusions (no CSP header expected):
curl -sI https://your-domain.example.com/api/health | grep -i content-security-policy
# → (no output)If you get no CSP header in production, check that the middleware matcher isn't excluding the route and that CSP_ENFORCE isn't explicitly set to "false" — the boot log and npm run validate:env both warn about it.
To add new directives or allow additional origins:
- Edit
src/middleware.ts→buildCspHeader(). - Add the new directive to the
directivesarray. - Ensure nonce-based scripts are properly handled (the
x-noncerequest header is forwarded). - Test in development first by setting
CSP_ENFORCE=truelocally and checking the browser console for violation reports. - Violations are automatically posted to
POST /api/csp-reportfor monitoring.
The following paths are excluded from CSP:
/api/*- API routes/sw.js- Service Worker script/_next/static/*- Next.js static assets/_next/image/*- Next.js image optimization/favicon.ico,/sitemap.xml,/robots.txt