Skip to content
Back to Blog
Web SecurityMarch 22, 2026·13 min read

HTTP Security Headers: The Complete Guide to Securing Your Website

Security headers protect against XSS, clickjacking, and data injection. Learn every header, why it matters, and how to implement them.

Code on laptop representing website security headers

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.

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;
DirectiveControlsRecommended Value
default-srcFallback for all resource types'self'
script-srcJavaScript sources'self' + specific CDNs
style-srcCSS sources'self' (avoid 'unsafe-inline')
img-srcImage sources'self' data: https:
frame-ancestorsWho can embed your page'none'
base-uriRestricts <base> 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.

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 <iframe>, <object>, or <embed> elements on other sites — blocking clickjacking attacks.

X-Frame-Options: DENY
ValueBehavior
DENYNever allow framing (most secure)
SAMEORIGINAllow 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

Referrer-Policy

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

Referrer-Policy: strict-origin-when-cross-origin
PolicySame-OriginCross-Origin (HTTPS→HTTPS)Downgrade (HTTPS→HTTP)
no-referrerNoneNoneNone
strict-originFull URLOrigin onlyNone
strict-origin-when-cross-originFull URLOrigin onlyNone
no-referrer-when-downgradeFull URLFull URLNone

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=()

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

HeaderValuePurpose
Cross-Origin-Opener-Policysame-originIsolates browsing context, enables SharedArrayBuffer
Cross-Origin-Embedder-Policyrequire-corpEnsures all embedded resources opt-in to being loaded
Cross-Origin-Resource-Policysame-originPrevents other origins from loading your resources
X-DNS-Prefetch-ControloffPrevents 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;

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=()"

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

MistakeImpactFix
Using 'unsafe-inline' + 'unsafe-eval' in CSPNullifies XSS protectionUse nonces or hashes instead
Missing always keyword in NginxHeaders absent on error pagesAdd always to every add_header
HSTS without full HTTPS coverageSubdomains become unreachableEnsure all subdomains have valid TLS certs first
Forgetting frame-ancestors in CSPClickjacking still possibleAdd frame-ancestors 'none' to CSP
Setting Referrer-Policy: unsafe-urlFull URL leaked to third partiesUse strict-origin-when-cross-origin

Tools

Scan your website's security headers:


References

🚀 Free ToolZilla tools used in this article

All client-side, no signup, no upload — open them in a new tab while you read:


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.

Continue Reading

Related Articles

Free & Private

Explore Our Free Tools

40+ browser-based utilities — fast, private, and always free. No sign-up required.

Browse All Tools