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();
    }
);