HTTP Security Headers
Assemble Web applies HTTP security headers to every response via the next.config.js file. The headers differ between development and production environments to balance strict security with a comfortable developer experience.
Configuration Location
All security headers are defined in next.config.js, inside the getSecurityHeaders() function, and applied to every route via the headers() async config key:
async headers() {
return [
{
source: '/(.*)',
headers: getSecurityHeaders(),
}
];
}
Provider Arrays
Before the headers are built, four arrays define the allowed external origins. These are the primary lists to update when a new third-party service is integrated.
| Array | CSP directive it feeds | Purpose |
|---|---|---|
providers | connect-src | REST/WebSocket endpoints (APIs, telemetry) |
mediaProviders | media-src | Video/audio asset CDNs |
scriptSrcItems | script-src, script-src-elem | Allowed external script origins |
imageSrcProviders | img-src (production only) | Image proxy / CDN hosts |
// next.config.js
const providers = [
'https://*.accedo.tv',
'wss://*.accedo.tv',
'https://*.boltdns.net',
'https://*.brightcovecdn.com',
'https://bam.nr-data.net', // New Relic telemetry
];
const mediaProviders = ['https://*.boltdns.net', 'https://*.brightcovecdn.com'];
const scriptSrcItems = ['https://js-agent.newrelic.com'];
const imageSrcProviders = [
'https://image-proxy.ps.accedo.tv',
'https://cdn.one.accedo.tv',
];
Headers Reference
Development vs. Production
The function checks process.env.NODE_ENV === 'development' and returns a relaxed policy set in development or a strict one in production.
| Header | Development | Production |
|---|---|---|
Content-Security-Policy | Relaxed (allows unsafe-* for HMR) | Strict (no unsafe-*, extra directives) |
X-Frame-Options | DENY | DENY |
X-Content-Type-Options | nosniff | nosniff |
Referrer-Policy | no-referrer-when-downgrade | strict-origin-when-cross-origin |
Permissions-Policy | (not set) | camera=(), microphone=(), geolocation=() |
Strict-Transport-Security | (not set) | max-age=31536000; includeSubDomains; preload |
Content-Security-Policy Directives
| Directive | Development value | Production value |
|---|---|---|
default-src | 'self' | 'self' |
script-src | 'self' 'unsafe-eval' 'unsafe-inline' <scriptSrcItems> | 'self' <scriptSrcItems> |
script-src-elem | 'self' 'unsafe-eval' 'unsafe-inline' <scriptSrcItems> | 'self' <scriptSrcItems> |
style-src | 'self' 'unsafe-inline' | 'self' |
img-src | 'self' data: https: | 'self' data: <imageSrcProviders> |
font-src | 'self' data: | 'self' data: |
connect-src | 'self' <providers> | 'self' <providers> |
media-src | 'self' blob: <mediaProviders> | 'self' blob: <mediaProviders> |
frame-ancestors | 'none' | 'none' |
base-uri | (not set) | 'self' |
form-action | (not set) | 'self' |
upgrade-insecure-requests | (not set) | (set — no value required) |
unsafe-eval and unsafe-inline in development?Next.js hot module replacement (HMR) injects inline scripts and uses eval to apply code updates at runtime. These keyword allowances are required for a smooth development experience and are never present in production builds.
In production, style-src is set to 'self' with no 'unsafe-inline'. Any dynamically injected <style> tags (e.g., from third-party libraries) will be blocked by the browser. Audit new dependencies for inline style usage before adding them.
How to Update for a New Project
1. Adding a new API or WebSocket origin
Add the URL to the providers array:
const providers = [
// … existing entries …
'https://api.my-new-service.com',
'wss://realtime.my-new-service.com',
];
2. Adding a new video/audio CDN
Add the CDN origin to mediaProviders:
const mediaProviders = [
// … existing entries …
'https://cdn.my-video-provider.com',
];
3. Adding an external script (e.g., analytics or monitoring)
Add the script origin to scriptSrcItems:
const scriptSrcItems = [
// … existing entries …
'https://cdn.analytics-provider.com',
];
4. Adding an image CDN
Add the hostname to imageSrcProviders and to the images.remotePatterns array in nextConfig so that the Next.js <Image> component can load from it:
const imageSrcProviders = [
// … existing entries …
'https://images.my-cdn.com',
];
// Also update the Next.js images config:
images: {
remotePatterns: [
{ hostname: 'image-proxy.ps.accedo.tv' },
{ hostname: 'cdn.one.accedo.tv' },
{ hostname: 'images.my-cdn.com' }, // ← add here too
],
};
Keep providers, mediaProviders, scriptSrcItems, and imageSrcProviders as specific as possible. Wildcard subdomains (*.example.com) are acceptable when the subdomain space is controlled by a trusted party, but avoid overly broad patterns like https: or *.
Verifying Headers in the Browser
- Open DevTools → Network tab.
- Reload the page and select the main document request.
- Check the Response Headers panel for the headers listed above.