DEV Community

Cover image for HTTP Security Headers: The Complete Guide to Securing Your Website
toolbox-poster
toolbox-poster

Posted on • Originally published at toolbox.starnomina.tn

HTTP Security Headers: The Complete Guide to Securing Your Website

TL;DR
HTTP security headers are your first line of defense against cross-site scripting (XSS), clickjacking,
MIME sniffing, and data injection attacks. Despite being simple response headers, a 2024 scan of the
top 1 million websites found that fewer than 12% deploy a Content Security Policy. This guide
covers every critical security header with production-ready Nginx and Apache configurations.

📑 Table of Contents

  • Why Security Headers Matter
  • Content Security Policy (CSP)
  • Strict-Transport-Security (HSTS)
  • X-Frame-Options
  • X-Content-Type-Options
  • Referrer-Policy
  • Permissions-Policy
  • Additional Useful Headers
  • Nginx & Apache Configuration
  • Best Practices
  • Common Mistakes
  • Tools
  • References

Why Security Headers Matter

Security headers instruct browsers on how to handle your content — which scripts can run,
whether your page can be framed, and what information is leaked in referrers. They cost nothing to deploy
and defend against entire categories of attacks identified in the OWASP Top 10.

📖 Definition — HTTP security headers are response headers sent by the server that activate browser-side security mechanisms, restricting behavior that could be exploited by attackers.

Content Security Policy (CSP)

CSP is the most powerful security header. It defines an allowlist of content sources, effectively neutralizing
XSS, data injection, and unauthorized inline scripts.

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';
  upgrade-insecure-requests;
Enter fullscreen mode Exit fullscreen mode
Directive Controls Recommended Value
default-src Fallback for all resource types 'self'
script-src JavaScript sources 'self' + specific CDNs
style-src CSS sources 'self' (avoid 'unsafe-inline')
img-src Image sources 'self' data: https:
frame-ancestors Who can embed your page 'none'
base-uri Restricts `` element 'self'

🎯 Start with Content-Security-Policy-Report-Only to log violations without blocking. Use the report-uri or report-to directive to collect reports, then tighten the policy iteratively.

🚫 Never use 'unsafe-eval' in production CSP. It re-enables eval(), completely undermining XSS protection. Refactor code that calls eval(), new Function(), or inline event handlers.

Strict-Transport-Security (HSTS)

Forces browsers to connect over HTTPS only, preventing protocol downgrade attacks and SSL stripping.

`http
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
`

⚠️ Once HSTS is deployed with includeSubDomains, every subdomain must have a valid TLS certificate. Rolling this out without full HTTPS coverage will break subdomains.

X-Frame-Options

Prevents your page from being embedded in , , or
`` elements on other sites — blocking clickjacking attacks.

X-Frame-Options: DENY
Enter fullscreen mode Exit fullscreen mode
Value Behavior
DENY Never allow framing (most secure)
SAMEORIGIN Allow framing only from same origin

💡 CSP's frame-ancestors directive is the modern replacement for X-Frame-Options, offering more granular control. Deploy both for backward compatibility.

X-Content-Type-Options

Prevents browsers from MIME-sniffing a response away from the declared Content-Type.
Blocks attacks that disguise executable content as harmless file types.

X-Content-Type-Options: nosniff
Enter fullscreen mode Exit fullscreen mode

Referrer-Policy

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

Referrer-Policy: strict-origin-when-cross-origin
Enter fullscreen mode Exit fullscreen mode
Policy Same-Origin Cross-Origin (HTTPS→HTTPS) Downgrade (HTTPS→HTTP)
no-referrer None None None
strict-origin Full URL Origin only None
strict-origin-when-cross-origin Full URL Origin only None
no-referrer-when-downgrade Full URL Full URL None

Permissions-Policy

Controls which browser features (camera, microphone, geolocation, etc.) your site and embedded iframes can use.
Formerly known as Feature-Policy.

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(self), usb=()
Enter fullscreen mode Exit fullscreen mode

Pro Tip: Set unused features to () (empty allowlist) to explicitly disable them. This prevents embedded third-party scripts from silently accessing sensitive APIs like the camera or microphone.

Additional Useful Headers

Header Value Purpose
Cross-Origin-Opener-Policy same-origin Isolates browsing context, enables SharedArrayBuffer
Cross-Origin-Embedder-Policy require-corp Ensures all embedded resources opt-in to being loaded
Cross-Origin-Resource-Policy same-origin Prevents other origins from loading your resources
X-DNS-Prefetch-Control off Prevents speculative DNS lookups (privacy)

Nginx & Apache Configuration

Nginx

# /etc/nginx/snippets/security-headers.conf
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;

# Include in server block:
# include snippets/security-headers.conf;
Enter fullscreen mode Exit fullscreen mode

Apache

# .htaccess or httpd.conf
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; frame-ancestors 'none';"
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Enter fullscreen mode Exit fullscreen mode

Best Practices

Deploy CSP in report-only mode first, analyze violations, then enforce.

Use nonce-based CSP ('nonce-{random}') instead of 'unsafe-inline' for inline scripts.

Add the always keyword in Nginx to send headers on all response codes (including 4xx/5xx).

Test headers in staging before production — overly strict CSP can break legitimate functionality.

Audit headers regularly with automated scanners as your site's dependencies evolve.

Common Mistakes

Mistake Impact Fix
Using 'unsafe-inline' + 'unsafe-eval' in CSP Nullifies XSS protection Use nonces or hashes instead
Missing always keyword in Nginx Headers absent on error pages Add always to every add_header
HSTS without full HTTPS coverage Subdomains become unreachable Ensure all subdomains have valid TLS certs first
Forgetting frame-ancestors in CSP Clickjacking still possible Add frame-ancestors 'none' to CSP
Setting Referrer-Policy: unsafe-url Full URL leaked to third parties Use strict-origin-when-cross-origin

Tools

Scan your website's security headers:

  • 🔧 Security Header Scanner — Analyze all security headers and get an actionable report with grades.

References

  • 📄 MDN — Content Security Policy (CSP)

  • 📄 MDN — Strict-Transport-Security

  • 📄 MDN — Permissions-Policy

  • 📄 OWASP Secure Headers Project

  • 📄 OWASP HTTP Headers Cheat Sheet

  • 📄 MDN — Referrer-Policy

🎯 Key Takeaway: Security headers are free, high-impact defenses. At minimum, deploy CSP, HSTS, X-Frame-Options,
X-Content-Type-Options, Referrer-Policy, and Permissions-Policy. Start CSP in report-only mode,
iterate based on real violation reports, then enforce. Combine with a regular scanning cadence
to catch regressions as third-party dependencies change.


Originally published on StarNomina ToolBox. Try our free online tools — no signup required.

Top comments (0)