Configure Apache security headers and Content Security Policy for enhanced web application protection

Intermediate 25 min Jun 02, 2026 108 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Configure essential security headers including HSTS, CSRF protection, and Content Security Policy (CSP) in Apache to protect your web applications from common attacks like XSS, clickjacking, and data injection vulnerabilities.

Prerequisites

  • Apache web server installed and running
  • Root or sudo access
  • Basic understanding of web security concepts
  • SSL certificates configured (recommended)

What this solves

Web applications face constant threats from cross-site scripting (XSS), clickjacking, and data injection attacks. HTTP security headers provide your first line of defense by instructing browsers how to handle your content safely. This tutorial configures Apache with essential security headers including Content Security Policy (CSP), HSTS, and anti-clickjacking protections to significantly reduce your attack surface.

Step-by-step configuration

Enable required Apache modules

Apache needs the headers module to add security headers and the security module for advanced CSP features. Enable both modules to get started.

sudo a2enmod headers
sudo a2enmod security2
sudo httpd -M | grep -E "(headers|security)"

If not loaded, check /etc/httpd/conf.modules.d/00-base.conf

sudo systemctl reload httpd

Install ModSecurity for CSP enforcement

ModSecurity provides advanced Content Security Policy features and violation reporting. Install it to enable comprehensive CSP protection.

sudo apt update
sudo apt install -y libapache2-mod-security2
sudo a2enmod security2
sudo dnf install -y epel-release
sudo dnf install -y mod_security

Create security headers configuration

Create a dedicated configuration file for security headers. This keeps your security settings organized and makes them easy to manage across virtual hosts.

# Security Headers Configuration

Enable security headers for all responses

# HTTP Strict Transport Security (HSTS) # Forces HTTPS connections for 1 year, includes subdomains Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" # Prevent MIME type sniffing Header always set X-Content-Type-Options "nosniff" # Enable XSS protection in browsers Header always set X-XSS-Protection "1; mode=block" # Prevent clickjacking attacks Header always set X-Frame-Options "SAMEORIGIN" # Control referrer information Header always set Referrer-Policy "strict-origin-when-cross-origin" # Remove server information Header always unset Server Header always unset X-Powered-By # Permissions Policy (formerly Feature Policy) Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" IfModule>

Content Security Policy Configuration

# Basic CSP - adjust sources based on your application needs Header always set Content-Security-Policy "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'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'" # CSP Report URI for violation reporting (optional) # Header always set Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report" IfModule>

Configure ModSecurity for CSP reporting

Set up ModSecurity to handle CSP violation reports. This helps you identify and fix CSP issues in your applications.

# Enable ModSecurity engine
SecRuleEngine On

Configure audit logging

SecAuditEngine RelevantOnly SecAuditLog /var/log/apache2/modsec_audit.log

CSP violation logging

SecRule REQUEST_URI "@streq /csp-report" \ "id:1001,phase:1,pass,nolog,ctl:ruleEngine=Off"

Log CSP violations

SecRule REQUEST_METHOD "@streq POST" \ "chain,id:1002,phase:2,msg:'CSP Violation Report',logdata:'%{REQUEST_BODY}'" SecRule REQUEST_URI "@streq /csp-report" \ "t:none"
# Enable ModSecurity engine
SecRuleEngine On

Configure audit logging

SecAuditEngine RelevantOnly SecAuditLog /var/log/httpd/modsec_audit.log

CSP violation logging

SecRule REQUEST_URI "@streq /csp-report" \ "id:1001,phase:1,pass,nolog,ctl:ruleEngine=Off"

Log CSP violations

SecRule REQUEST_METHOD "@streq POST" \ "chain,id:1002,phase:2,msg:'CSP Violation Report',logdata:'%{REQUEST_BODY}'" SecRule REQUEST_URI "@streq /csp-report" \ "t:none"

Create CSP violation report handler

Create a simple endpoint to receive and log CSP violation reports. This helps you monitor policy violations and adjust your CSP as needed.

 'Method not allowed']));
}

// Get the raw POST data
$input = file_get_contents('php://input');
$data = json_decode($input, true);

if ($data && isset($data['csp-report'])) {
    $report = $data['csp-report'];
    
    // Log the violation
    $logEntry = [
        'timestamp' => date('c'),
        'blocked-uri' => $report['blocked-uri'] ?? 'unknown',
        'document-uri' => $report['document-uri'] ?? 'unknown',
        'violated-directive' => $report['violated-directive'] ?? 'unknown',
        'source-file' => $report['source-file'] ?? 'unknown',
        'line-number' => $report['line-number'] ?? 'unknown'
    ];
    
    // Write to log file
    file_put_contents(
        '/var/log/apache2/csp-violations.log',
        json_encode($logEntry) . "\n",
        FILE_APPEND | LOCK_EX
    );
    
    http_response_code(204); // No content
} else {
    http_response_code(400);
    exit(json_encode(['error' => 'Invalid report format']));
}
?>

Enable security headers configuration

Enable the security headers configuration and restart Apache to apply the changes.

sudo a2enconf security-headers
sudo apache2ctl configtest
sudo systemctl restart apache2
sudo httpd -t
sudo systemctl restart httpd

Configure application-specific CSP

Customize the Content Security Policy for your specific application. This example shows a more restrictive policy for a typical web application.


    ServerName example.com
    DocumentRoot /var/www/example.com
    
    # SSL Configuration
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/example.com.crt
    SSLCertificateKeyFile /etc/ssl/private/example.com.key
    
    # Include global security headers
    Include conf-available/security-headers.conf
    
    # Application-specific CSP
    
        # Strict CSP for this application
        Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://js.stripe.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https://images.example.com; connect-src 'self' https://api.example.com; frame-src https://js.stripe.com; report-uri /csp-report"
        
        # Additional security for sensitive applications
        Header always set Cross-Origin-Opener-Policy "same-origin"
        Header always set Cross-Origin-Embedder-Policy "require-corp"
        Header always set Cross-Origin-Resource-Policy "same-origin"
    IfModule>
    
    # Security configurations
    
        Options -Indexes -Includes -ExecCGI
        AllowOverride None
        Require all granted
    
    
    # Disable server-info and server-status
    
        Require all denied
    
    
    
        Require all denied
    
VirtualHost>

Set up log rotation for security logs

Configure log rotation for CSP violation logs and ModSecurity audit logs to prevent disk space issues.

/var/log/apache2/csp-violations.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    create 640 www-data adm
    postrotate
        systemctl reload apache2 > /dev/null 2>&1 || true
    endscript
}

/var/log/apache2/modsec_audit.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 640 www-data adm
    postrotate
        systemctl reload apache2 > /dev/null 2>&1 || true
    endscript
}

Verify your setup

Test your security headers configuration to ensure everything is working correctly.

# Check Apache configuration
sudo apache2ctl configtest

Test security headers with curl

curl -I https://example.com

Specifically check for security headers

curl -s -D- https://example.com | grep -E "(Strict-Transport|Content-Security|X-Frame|X-Content-Type)"

Test CSP violation reporting

curl -X POST https://example.com/csp-report \ -H "Content-Type: application/json" \ -d '{"csp-report":{"blocked-uri":"https://evil.com/script.js","document-uri":"https://example.com/test","violated-directive":"script-src"}}'

Check CSP violation logs

sudo tail -f /var/log/apache2/csp-violations.log
Note: Use browser developer tools to test CSP in practice. The Console tab will show CSP violations in real-time, which is invaluable for debugging policy issues.

Common issues

SymptomCauseFix
Scripts not loadingCSP blocks inline or external scriptsAdd script sources to script-src directive or use nonces
Styles not applyingCSP blocks inline stylesUse external stylesheets or add 'unsafe-inline' to style-src
Images not displayingMissing image sources in CSPAdd image domains to img-src directive
HSTS not workingSite accessed over HTTPEnsure redirects to HTTPS and headers only sent over HTTPS
CSP reports not loggingIncorrect report-uri or permissionsCheck PHP file permissions and log directory writability
Headers module not loadedModule not enabledRun sudo a2enmod headers and restart Apache

Advanced CSP configuration

For production applications, consider these advanced CSP strategies for better security without breaking functionality.

Use nonces for inline scripts

Generate unique nonces for each request to allow specific inline scripts while blocking others. This provides better security than 'unsafe-inline'.