Apache Security Headers Explained (and Why You Need Them)
Your site can be perfectly coded and still be vulnerable if the browser isn't told how to protect its users. HTTP security headers are short instructions your server sends with every response that tell the browser to enable built-in defenses against cross-site scripting, clickjacking, protocol downgrade attacks and data leaks.
They're cheap to add, they don't change your application code, and they meaningfully raise your security posture. Here's what each one does and how to set them in Apache.
Enable mod_headers first
sudo a2enmod headers
sudo systemctl restart apache2Strict-Transport-Security (HSTS)
Forces browsers to use HTTPS for your domain, even if a user types http:// or clicks an old link. This defeats SSL-stripping attacks where an attacker downgrades the connection.
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"max-age is in seconds (one year here). Only add preload once you're sure every subdomain is HTTPS — it's hard to undo.
Content-Security-Policy (CSP)
The most powerful — and most involved — header. CSP whitelists exactly where scripts, styles, images and other resources may load from, which shuts down most cross-site scripting (XSS) attacks. Start restrictive and loosen as needed:
Header always set Content-Security-Policy "default-src 'self'; \
script-src 'self'; \
style-src 'self' 'unsafe-inline'; \
img-src 'self' data:; \
object-src 'none'; \
base-uri 'self'; \
frame-ancestors 'self'"Because CSP can break things, roll it out with Content-Security-Policy-Report-Only first, watch the browser console for violations, then switch to enforcing.
X-Frame-Options
Stops other sites from embedding yours in an <iframe>, which prevents clickjacking (tricking users into clicking hidden buttons). Modern browsers also honour CSP's frame-ancestors, but keep this for older ones.
Header always set X-Frame-Options "SAMEORIGIN"X-Content-Type-Options
Tells the browser not to "sniff" and second-guess the declared content type — which can turn an uploaded image into executable script.
Header always set X-Content-Type-Options "nosniff"Referrer-Policy & Permissions-Policy
Referrer-Policy controls how much of your URL is sent to other sites (avoid leaking query strings). Permissions-Policy disables browser features you don't use, like camera or geolocation.
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"Putting it together in a VirtualHost
<VirtualHost *:443>
ServerName example.com
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'; object-src 'none'"
# ... your SSL and document root config
</VirtualHost>Use always so headers are set even on error responses (like 404s), and reload with sudo apachectl configtest && sudo systemctl reload apache2.