Server Configuration for CSP Headers
Server Configuration for CSP Headers
Different server environments require different approaches for implementing CSP headers. Here's how to configure CSP for popular web servers and frameworks.
Apache Configuration
For Apache servers, CSP headers can be set using .htaccess
files or server configuration:
# .htaccess file for CSP implementation
<IfModule mod_headers.c>
# Development/Testing Phase - Report Only
Header set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com; report-uri /csp-violation-report-endpoint"
# Production Phase - Enforcing
# Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-%{UNIQUE_ID}e' https://cdnjs.cloudflare.com; style-src 'self' 'nonce-%{UNIQUE_ID}e' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com"
# Security Headers Bundle
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "0"
Header set Referrer-Policy "strict-origin-when-cross-origin"
Header set Permissions-Policy "geolocation=(), microphone=(), camera=()"
</IfModule>
# Enable mod_headers if not already enabled
# sudo a2enmod headers
# sudo systemctl restart apache2
Nginx Configuration
Nginx offers flexible CSP header configuration through server blocks:
# nginx.conf or site configuration
server {
listen 443 ssl http2;
server_name example.com;
# Generate nonce for each request
set $csp_nonce $request_id;
# CSP Header Configuration
set $csp_default "default-src 'self'";
set $csp_script "script-src 'self' 'nonce-$csp_nonce' https://cdnjs.cloudflare.com";
set $csp_style "style-src 'self' 'nonce-$csp_nonce' https://fonts.googleapis.com";
set $csp_font "font-src 'self' https://fonts.gstatic.com";
set $csp_img "img-src 'self' data: https:";
set $csp_connect "connect-src 'self' https://api.example.com wss://realtime.example.com";
set $csp_frame "frame-ancestors 'self'";
set $csp_report "report-uri /csp-violation-report-endpoint";
# Combine CSP directives
add_header Content-Security-Policy "$csp_default; $csp_script; $csp_style; $csp_font; $csp_img; $csp_connect; $csp_frame; $csp_report" always;
# Additional security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "0" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Pass nonce to application
location / {
proxy_pass http://backend;
proxy_set_header X-CSP-Nonce $csp_nonce;
}
# CSP violation reporting endpoint
location /csp-violation-report-endpoint {
proxy_pass http://backend/csp-reports;
proxy_set_header Content-Type "application/csp-report";
}
}
Node.js/Express Implementation
For Node.js applications, implement CSP using middleware:
// csp-middleware.js
const crypto = require('crypto');
function generateNonce() {
return crypto.randomBytes(16).toString('base64');
}
function cspMiddleware(options = {}) {
return (req, res, next) => {
// Generate nonce for this request
res.locals.nonce = generateNonce();
// Build CSP policy
const directives = {
'default-src': ["'self'"],
'script-src': ["'self'", `'nonce-${res.locals.nonce}'`],
'style-src': ["'self'", `'nonce-${res.locals.nonce}'`],
'img-src': ["'self'", 'data:', 'https:'],
'font-src': ["'self'"],
'connect-src': ["'self'"],
'frame-ancestors': ["'self'"],
'base-uri': ["'self'"],
'form-action': ["'self'"],
...options.directives
};
// Add report-uri if in report-only mode
if (options.reportUri) {
directives['report-uri'] = [options.reportUri];
}
// Format policy string
const policy = Object.entries(directives)
.map(([key, values]) => `${key} ${values.join(' ')}`)
.join('; ');
// Set appropriate header
const headerName = options.reportOnly
? 'Content-Security-Policy-Report-Only'
: 'Content-Security-Policy';
res.setHeader(headerName, policy);
// Make nonce available to templates
res.locals.cspNonce = res.locals.nonce;
next();
};
}
// Usage in Express app
const express = require('express');
const app = express();
// Development configuration
app.use(cspMiddleware({
reportOnly: true,
reportUri: '/csp-violation-report-endpoint',
directives: {
'script-src': ["'self'", "'unsafe-inline'", 'https://cdnjs.cloudflare.com'],
'style-src': ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
'font-src': ["'self'", 'https://fonts.gstatic.com']
}
}));
// Production configuration
/*
app.use(cspMiddleware({
reportOnly: false,
reportUri: '/csp-violation-report-endpoint',
directives: {
'script-src': ["'self'", 'https://cdnjs.cloudflare.com'],
'style-src': ["'self'", 'https://fonts.googleapis.com'],
'font-src': ["'self'", 'https://fonts.gstatic.com'],
'upgrade-insecure-requests': ['']
}
}));
*/
// CSP violation reporting endpoint
app.post('/csp-violation-report-endpoint',
express.json({ type: 'application/csp-report' }),
(req, res) => {
console.log('CSP Violation:', req.body);
// Log to your monitoring system
res.status(204).end();
}
);