Web Security

Security Headers Implementation Checklist: Hardening Your Web Application

HTTP security headers are your first line of defense against XSS, clickjacking, and data injection attacks. Here is a practical implementation checklist with correct configurations.

Yukti Singhal
Security Researcher
5 min read

HTTP security headers are one of the highest-impact, lowest-effort security improvements you can make to a web application. They instruct the browser to enforce security policies that prevent entire classes of attacks. Yet a surprising number of production applications ship with missing or misconfigured security headers.

The reason is not that headers are complex -- they are straightforward once you understand them. The problem is that there is no single authoritative reference, default server configurations ship without them, and framework documentation often skips them. This checklist covers every security header you should implement, in order of priority.

Content-Security-Policy (CSP)

CSP is the most powerful security header and the most complex to configure. It defines which sources the browser is allowed to load content from, effectively neutralizing XSS attacks that rely on inline scripts or external script injection.

A minimal production CSP:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'

Start with report-only mode. Deploy CSP in report-only mode first to identify what would break. Collect reports for a week, adjust the policy, then enforce.

Avoid unsafe-eval. The 'unsafe-eval' directive allows eval() and similar functions, which is exactly what XSS attacks use. If your application requires it (some template engines and older libraries do), plan to eliminate the dependency.

Use nonces for inline scripts. Instead of allowing all inline scripts ('unsafe-inline'), use per-request nonces that allow only your inline scripts. This maintains CSP protection while supporting inline script patterns.

Strict-Transport-Security (HSTS)

HSTS tells browsers to only connect to your site over HTTPS, preventing SSL stripping attacks.

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

Set max-age to at least one year (31536000 seconds). Shorter values leave a window for downgrade attacks.

Include subdomains. The includeSubDomains directive protects all subdomains. Only omit this if some subdomains legitimately need HTTP access (which is increasingly rare).

Submit for preloading. The preload directive and submission to the browser preload list ensures HSTS is enforced on the very first visit, not just subsequent visits.

X-Content-Type-Options

Prevents MIME type sniffing, which can turn non-executable responses into executable content.

X-Content-Type-Options: nosniff

This is a single-value header with no configuration options. Always include it. It prevents attacks where an attacker uploads a file with a misleading extension, and the browser executes it based on content sniffing rather than the declared content type.

X-Frame-Options

Prevents your pages from being embedded in iframes, defending against clickjacking attacks.

X-Frame-Options: DENY

Use DENY unless your application specifically needs to be framed. If it does, use SAMEORIGIN to allow framing only by your own origin. Note that CSP's frame-ancestors directive supersedes X-Frame-Options in modern browsers, but including both provides defense-in-depth.

Referrer-Policy

Controls how much referrer information the browser includes when navigating away from your site.

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

This sends the full referrer for same-origin requests (useful for analytics), the origin only for cross-origin HTTPS requests, and no referrer for HTTPS-to-HTTP downgrades. It prevents leaking sensitive URL paths to third-party sites.

Permissions-Policy

Controls which browser features your application can use. This limits the attack surface by disabling features your application does not need.

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

Disable every feature your application does not use. This prevents compromised scripts from accessing sensitive capabilities.

Cross-Origin Headers

Modern browsers support cross-origin isolation headers that prevent cross-origin attacks:

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

These headers enable cross-origin isolation, which provides stronger security boundaries but may break legitimate cross-origin integrations. Test thoroughly before deploying.

Implementation Approaches

Web server configuration. The most reliable approach is configuring headers at the web server level (Nginx, Apache, Caddy). This ensures headers are present on every response, including error pages and static files.

Application middleware. Framework middleware (Helmet.js for Express, django-csp for Django, secure-headers for Ruby) provides programmatic header management. This allows dynamic headers (like CSP nonces) but requires framework-level configuration.

CDN/reverse proxy. If you use a CDN (Cloudflare, Fastly, Akamai), configure security headers at the CDN edge. This adds headers to all responses without modifying your application.

Verification

After implementation, verify headers using securityheaders.com, Mozilla Observatory, or browser developer tools. Check every response type: HTML pages, API responses, static assets, and error pages. A single response without proper headers can be exploited.

Run verification as part of your CI/CD pipeline. Tools like check-headers can be integrated into automated testing to prevent header regression.

How Safeguard.sh Helps

Safeguard.sh monitors your web application's security posture, including HTTP security header configuration. The platform identifies missing or misconfigured headers across your application portfolio and tracks compliance with security header policies over time. Combined with supply chain monitoring, Safeguard.sh ensures that both your application's code dependencies and its runtime security configuration meet your organization's security standards.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.