Why HTTP Security Headers Matter

HTTP security headers are response headers that instruct the browser how to behave when handling your site's content. They are one of the highest-value, lowest-effort security controls available β€” most take under five minutes to implement and protect against entire vulnerability classes including XSS, clickjacking, protocol downgrade attacks, and MIME-type confusion.

Despite this, they are consistently absent. A scan of the Alexa top 1 million sites finds that over 95% are missing at least one critical security header. Scanner tools like securityheaders.com give most production applications a grade of D or F.

Where to set them: Security headers can be set in your web server config (nginx, Apache), CDN (Cloudflare, Fastly), load balancer, or application framework middleware. The framework approach is easiest to version-control and review.

Content-Security-Policy (CSP)

CSP is the most powerful β€” and most complex β€” security header. It defines which sources of content (scripts, styles, images, fonts, frames) the browser is permitted to load. A properly configured CSP eliminates the impact of most XSS attacks by preventing injected scripts from executing.

nginx.conf nginx
# Strict CSP β€” adjust sources to match your actual CDN/fonts
add_header Content-Security-Policy
  "default-src 'self';
   script-src 'self' 'nonce-{RANDOM}';
   style-src 'self' 'nonce-{RANDOM}' fonts.googleapis.com;
   font-src 'self' fonts.gstatic.com;
   img-src 'self' data: cdn.yoursite.com;
   connect-src 'self' api.yoursite.com;
   frame-ancestors 'none';
   base-uri 'self';
   form-action 'self';
   upgrade-insecure-requests";

Start with report-only mode

CSP is notoriously easy to break in production. Start with Content-Security-Policy-Report-Only and a report-uri endpoint. Run it for two weeks, fix all the legitimate violations, then switch to enforcement mode.

Never use unsafe-inline or unsafe-eval: These directives defeat the entire purpose of CSP. If your codebase relies on inline scripts, use nonces β€” a per-request cryptographic token injected into each allowed script tag.

Strict-Transport-Security (HSTS)

HSTS tells browsers to only ever connect to your domain over HTTPS β€” even if the user types http:// or follows an HTTP link. Once a browser has seen an HSTS header, it will internally redirect to HTTPS before making any network request for the specified duration.

nginx.conf nginx
# Two-year max-age, include subdomains, opt into preload list
add_header Strict-Transport-Security
  "max-age=63072000; includeSubDomains; preload" always;

The preload directive allows your domain to be added to Chrome's HSTS preload list β€” a hardcoded list of domains that Chrome connects to over HTTPS before ever seeing the header. Submit at hstspreload.org.

Important: Only add preload if every subdomain of your domain supports HTTPS. HSTS with includeSubDomains; preload on a domain that has HTTP-only subdomains will break those subdomains for all Chrome users β€” and it's very difficult to reverse.

X-Frame-Options

X-Frame-Options prevents your pages from being embedded in iframes on other sites, mitigating clickjacking attacks. Clickjacking places an invisible iframe of your site over a deceptive UI to trick users into clicking on your buttons.

nginx.conf nginx
add_header X-Frame-Options "DENY" always;
# Or SAMEORIGIN if you need to iframe your own pages:
# add_header X-Frame-Options "SAMEORIGIN" always;

CSP supersedes X-Frame-Options: The frame-ancestors CSP directive is more flexible and overrides X-Frame-Options in modern browsers. Set both for maximum compatibility with older browsers.

X-Content-Type-Options

Without this header, browsers perform MIME-type sniffing β€” inferring the content type from the content itself rather than the Content-Type header. An attacker can upload a file that looks like an image but contains JavaScript, and the browser might execute it.

nginx.conf nginx
add_header X-Content-Type-Options "nosniff" always;

This is the simplest header to set and has essentially zero risk of breaking anything. There is no legitimate reason not to have it.

Referrer-Policy

When a user clicks a link from your site to an external site, the browser sends a Referer header containing the full URL of the page they were on. This can leak sensitive path information β€” user IDs, session tokens in URLs, or internal path structure.

nginx.conf nginx
# Send origin only for same-origin, nothing for cross-origin
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
  • no-referrer β€” never send the Referer header (breaks some analytics)
  • strict-origin-when-cross-origin β€” send full URL same-origin, just the origin cross-origin (recommended)
  • same-origin β€” only send Referer for same-origin requests

Permissions-Policy

Formerly Feature-Policy, this header controls which browser features and APIs your page (and embedded iframes) can use β€” camera, microphone, geolocation, payment, and more. Restricting unused APIs reduces the attack surface for malicious third-party scripts.

nginx.conf nginx
# Disable everything you don't use
add_header Permissions-Policy
  "camera=(), microphone=(), geolocation=(), payment=(),
   usb=(), bluetooth=(), interest-cohort=()" always;

Remove the Server Header

By default, web servers advertise their name and version in the Server header (Server: nginx/1.18.0 or Server: Apache/2.4.51). This helps attackers identify unpatched software versions instantly.

nginx.conf nginx
server_tokens off;  # nginx: removes version from Server header
# Apache: ServerTokens Prod + ServerSignature Off

Also remove or sanitize X-Powered-By (often set by frameworks to e.g. PHP/8.1.2). In Express.js: app.disable('x-powered-by').

Testing Your Security Headers

After deployment, verify your header configuration:

terminal bash
# Check headers with curl
curl -I https://yoursite.com

# Check specific header
curl -s -I https://yoursite.com | grep -i "content-security-policy"

# Full header audit β€” securityheaders.com API
curl "https://securityheaders.com/?q=yoursite.com&followRedirects=on"
  • securityheaders.com β€” grades your header configuration, explains each finding
  • Mozilla Observatory β€” comprehensive header and TLS analysis
  • OWASP ZAP β€” includes passive header checks in every scan
  • AquilaX DAST β€” checks headers as part of automated security scanning in CI/CD

Security Headers Checklist

  1. Content-Security-Policy β€” deployed in report-only, then enforcement; no unsafe-inline
  2. Strict-Transport-Security β€” max-age β‰₯ 1 year, includeSubDomains if safe
  3. X-Frame-Options: DENY (or SAMEORIGIN) β€” plus frame-ancestors in CSP
  4. X-Content-Type-Options: nosniff β€” zero risk, always set
  5. Referrer-Policy: strict-origin-when-cross-origin β€” stops URL leakage
  6. Permissions-Policy β€” disable camera, mic, geolocation, payment if not used
  7. Remove or sanitize Server and X-Powered-By headers
  8. Verify with securityheaders.com and Mozilla Observatory after each deployment

Scan Your App for Missing Security Headers

AquilaX DAST checks your deployed application for every missing or misconfigured security header and explains how to fix each one.

Start Free Scan