Security

Complete Guide to Security Headers: Protecting Your Website in 2025

Master HTTP security headers including CSP, HSTS, and modern security policies. Learn implementation strategies, common pitfalls, and how to maximize protection without breaking functionality.

A
Andrew - Admin
9 months ago
6 min read

Why Security Headers Matter

HTTP security headers are your first line of defense against many common web attacks. These response headers instruct browsers how to behave when handling your site's content, providing protection against cross-site scripting (XSS), clickjacking, code injection, and information leakage.

Despite their importance, research shows that over 95% of websites fail to implement even basic security headers correctly. This guide provides a comprehensive overview of essential security headers and how to implement them effectively.

Content Security Policy (CSP)

Content Security Policy is the most powerful—and most complex—security header available. CSP defines approved sources of content that browsers can load on your pages, preventing attackers from injecting malicious scripts even if they find an XSS vulnerability.

CSP Directives

A comprehensive CSP policy includes multiple directives:

Content-Security-Policy: default-src 'self'; 
  script-src 'self' https://cdn.example.com; 
  style-src 'self' 'unsafe-inline'; 
  img-src 'self' data: https:; 
  font-src 'self' https://fonts.gstatic.com; 
  connect-src 'self' https://api.example.com; 
  frame-ancestors 'none'; 
  base-uri 'self'; 
  form-action 'self';

Key CSP directives explained:

  • default-src: Fallback for other directives. Set to 'self' to only allow resources from your domain
  • script-src: Controls which scripts can execute. Avoid 'unsafe-inline' and 'unsafe-eval' when possible
  • style-src: Controls stylesheet sources. Many sites need 'unsafe-inline' for third-party widgets
  • img-src: Controls image sources. Use https: to allow any HTTPS image
  • connect-src: Controls fetch(), XMLHttpRequest, WebSocket, and EventSource connections
  • frame-ancestors: Replaces X-Frame-Options, controls who can embed your page in frames
  • base-uri: Restricts the URLs that can be used in a page's <base> element
  • form-action: Restricts the URLs that forms can submit to

Implementing CSP Without Breaking Your Site

CSP implementation should be gradual:

  1. Report-Only Mode: Deploy CSP in report-only mode first:
    Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
  2. Collect Violations: Monitor violation reports for 1-2 weeks to identify legitimate resources being blocked
  3. Refine Policy: Adjust your policy to allow necessary resources while maintaining security
  4. Enforce Policy: Switch from Report-Only to enforcement mode
  5. Continuous Monitoring: Keep monitoring reports to catch new violations

CSP Level 3 and Nonces

Modern CSP implementations use nonces (cryptographic tokens) instead of 'unsafe-inline':

Content-Security-Policy: script-src 'nonce-{RANDOM}';

<script nonce="{RANDOM}">
  // Your inline script
</script>

The nonce must be randomly generated for each page load and match between the header and script tag. This allows specific inline scripts while blocking injected ones.

HTTP Strict Transport Security (HSTS)

HSTS forces browsers to only connect to your site over HTTPS, preventing protocol downgrade attacks and cookie hijacking.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Parameters explained:

  • max-age: Duration (seconds) the browser should remember to only use HTTPS. 31536000 = 1 year
  • includeSubDomains: Applies policy to all subdomains. Only use if all subdomains support HTTPS
  • preload: Allows inclusion in browser HSTS preload lists for first-visit protection

HSTS Preload List

The HSTS preload list is a hardcoded list of domains built into browsers. To be included:

  1. Serve a valid certificate
  2. Redirect all HTTP traffic to HTTPS
  3. Serve HSTS header on all HTTPS responses with max-age of at least 31536000 seconds
  4. Include the 'preload' directive
  5. Submit your domain to hstspreload.org

Warning: Preload list inclusion is difficult to reverse. Ensure all subdomains support HTTPS before submission.

X-Content-Type-Options

Prevents MIME type sniffing, where browsers try to detect content type by analyzing file contents rather than trusting the Content-Type header.

X-Content-Type-Options: nosniff

Without this header, an attacker could upload a malicious file disguised as an image, and the browser might execute it as JavaScript. Always include this header.

X-Frame-Options and Frame-Ancestors

Protects against clickjacking attacks where your site is embedded in an invisible iframe to trick users into clicking malicious elements.

X-Frame-Options: DENY

Options:

  • DENY: Page cannot be displayed in a frame under any circumstances
  • SAMEORIGIN: Page can be framed only by pages on the same origin

Modern sites should use CSP's frame-ancestors directive instead, which provides more granular control:

Content-Security-Policy: frame-ancestors 'none';

Referrer-Policy

Controls how much referrer information is sent when navigating away from your site.

Referrer-Policy: strict-origin-when-cross-origin

Policy options (from least to most restrictive):

  • unsafe-url: Full URL sent in all cases (not recommended)
  • origin-when-cross-origin: Full URL for same-origin, origin only for cross-origin
  • strict-origin-when-cross-origin: Full URL for same-origin HTTPS, origin only for cross-origin HTTPS, nothing for HTTPS→HTTP (recommended)
  • no-referrer: Never send referrer information (breaks some analytics)

Permissions-Policy (formerly Feature-Policy)

Controls which browser features and APIs can be used by your site and embedded content.

Permissions-Policy: geolocation=(), microphone=(), camera=()

Common features to restrict:

  • geolocation: GPS location access
  • microphone: Microphone access
  • camera: Camera access
  • payment: Payment Request API
  • usb: WebUSB API
  • magnetometer: Magnetometer sensor

Syntax allows allowlists: Permissions-Policy: geolocation=(self "https://maps.example.com")

Cross-Origin-Embedder-Policy and Cross-Origin-Opener-Policy

These headers enable 'cross-origin isolation', required for powerful features like SharedArrayBuffer:

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

Warning: These headers can break embedded content and pop-ups. Test thoroughly before deployment.

Implementation Across Platforms

Apache (.htaccess)

<IfModule mod_headers.c>
  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 Permissions-Policy "geolocation=(), microphone=(), camera=()"
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
  Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'"
</IfModule>

Nginx

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'" always;

WordPress (functions.php)

function add_security_headers() {
  header('X-Frame-Options: SAMEORIGIN');
  header('X-Content-Type-Options: nosniff');
  header('Referrer-Policy: strict-origin-when-cross-origin');
  header('Permissions-Policy: geolocation=(), microphone=(), camera=()');
}
add_action('send_headers', 'add_security_headers');

Testing Your Security Headers

Use these tools to validate your implementation:

  • securityheaders.com: Comprehensive header analysis with letter grades
  • Mozilla Observatory: Detailed security recommendations
  • Browser DevTools: Network tab shows all response headers
  • csp-evaluator.withgoogle.com: Specifically tests CSP policies

Common Pitfalls and Solutions

CSP Breaking Third-Party Widgets

Many third-party services (analytics, ads, chat widgets) require 'unsafe-inline' or 'unsafe-eval'. Solutions:

  • Use nonces for your own inline scripts
  • Load third-party scripts from their CDN domains (add to script-src)
  • Consider switching to privacy-respecting alternatives that support strict CSP

HSTS Locking Out Users

If you deploy HSTS then lose your SSL certificate, users cannot access your site. Always:

  • Test with low max-age values first (300 seconds)
  • Ensure your certificate renewal process is automated
  • Have monitoring in place to alert on certificate expiration

Breaking Mobile Apps

Mobile apps that embed webviews may be affected by strict security headers. Test all native apps after header deployment.

Monitoring and Maintenance

Security headers require ongoing maintenance:

  • Monitor CSP violation reports weekly
  • Review headers after adding new third-party services
  • Test headers after platform upgrades
  • Keep policies updated as browser features evolve
  • Audit header effectiveness quarterly

Conclusion

Security headers are essential for modern web security. While implementation can be challenging, particularly for CSP, the protection they provide is worth the effort. Start with basic headers (X-Frame-Options, X-Content-Type-Options, HSTS) and gradually implement more advanced policies like CSP and Permissions-Policy.

Remember: security headers are defense in depth—they protect against exploitation even when vulnerabilities exist in your code. They should be part of every website's security strategy.

Frequently Asked Questions

1What is the most important security header to implement first?

Start with X-Content-Type-Options (nosniff) and X-Frame-Options as they're simple to implement and provide immediate protection. Then add HSTS if you have HTTPS fully deployed. Content Security Policy should be implemented last as it requires the most testing and refinement.

2Will security headers slow down my website?

No, security headers add negligible overhead (typically less than 1KB per response) and do not impact page load performance. In fact, some headers like CSP can improve performance by blocking unnecessary third-party scripts and resources.

3Can I implement security headers if I use a CDN?

Yes, most CDNs support custom headers. You can configure headers in your CDN dashboard (Cloudflare, AWS CloudFront, Fastly, etc.) or at your origin server. CDN-level configuration is often preferred as it applies headers to all responses including static assets.

4How do I fix Content Security Policy violations?

First, deploy CSP in report-only mode and collect violation reports. Review reports to identify legitimate resources being blocked. Update your CSP policy to allow these resources while maintaining security. Common fixes include adding trusted CDN domains to script-src or using nonces for inline scripts.

5What happens if I set HSTS with a long max-age and then lose my SSL certificate?

Users will be unable to access your site until you restore a valid SSL certificate. This is why you should always start with a short max-age (like 300 seconds) for testing, gradually increase it, and ensure you have automated certificate renewal and monitoring in place before using long max-age values.

Protect Your Website Today

Run a comprehensive security scan to detect malware, vulnerabilities, and security issues before they impact your business.