HTTP Security Headers: An Implementation Guide for 2026

HTTP security headers are one of the most effective, low-effort security controls available to web developers. Set them once in your server configuration, and the browser enforces the policy on every subsequent request-blocking entire classes of attacks before your application code even runs.

This guide provides a technically accurate, implementation-focused reference for HTTP security headers, verified against current browser support and best practices as of August 2026.

TL;DR

  • Start with CSP in Report-Only mode. Use default-src 'self', nonces for inline scripts, and frame-ancestors 'self'. Keep reporting enabled during and after rollout.
  • HSTS preload is nearly irreversible. As of April 2026, approximately 120,000 domains are in the Chrome preload list. Only submit when every subdomain serves HTTPS and you have documented business approval.
  • The report-to directive is now Baseline 2026. Since March 2026, report-to works across latest browsers. Support for the older Report-To header is declining; migrate to Reporting-Endpoints + report-to.
  • Stop using HPKP, X-XSS-Protection, and Expect-CT. These headers are deprecated or removed. Use HSTS, CSP, and rely on Certificate Transparency instead.
  • Set X-Content-Type-Options: nosniff on every response. No exceptions. Low risk, material security improvement.
  • Lock cookies with Secure; HttpOnly; SameSite=Lax. Use SameSite=None; Secure only for third-party contexts.

What Security Headers Do For You

HeaderAttack Vector Mitigated
Content-Security-Policy (CSP)XSS, data injection, clickjacking
HSTSProtocol downgrade, SSL stripping
X-Content-Type-OptionsMIME sniffing attacks
Referrer-PolicyReferrer data leakage
Permissions-PolicyUnauthorized browser feature access
COOP / COEP / CORPCross-origin data leaks, Spectre-type attacks
CORSUnauthorized cross-origin API access
Fetch MetadataCSRF and cross-site request abuse
Secure CookiesSession hijacking, CSRF
Subresource IntegrityCDN asset tampering

How to Work With Security Headers

  1. Treat headers as configuration as code. Keep them in version control alongside your application.
  2. Roll out with report-only first. Use Content-Security-Policy-Report-Only and monitor violations before enforcing.
  3. Validate in CI. Use curl checks and automated scanners in your pipeline.
  4. Use DAST in staging and production. Confirm real-world behavior.
  5. Revisit policies when you add third-party scripts, new APIs, or new features.

Major Headers: Secure Defaults and Examples

Content-Security-Policy (CSP)

CSP is the most powerful security header. It restricts where your page can load scripts, styles, images, and other resources, mitigating XSS and data injection attacks.

Safe Starting Point (Report-Only)

Content-Security-Policy-Report-Only: default-src 'self'; base-uri 'self'; object-src 'none'; script-src 'self' 'nonce-{RANDOM}'; style-src 'self'; img-src 'self' data:; frame-ancestors 'self'; upgrade-insecure-requests; report-to csp-endpoint

Enforcing Policy

Content-Security-Policy: default-src 'self'; base-uri 'self'; object-src 'none'; script-src 'self' 'nonce-{RANDOM}' 'strict-dynamic'; style-src 'self' 'nonce-{RANDOM}'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; form-action 'self'; upgrade-insecure-requests

Reporting Setup (Modern)

The report-to directive is now Baseline 2026-available across latest browsers since March 2026. Use Reporting-Endpoints to define endpoints:

Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports"
Content-Security-Policy: ... report-to csp-endpoint

Important: Browsers that support report-to ignore the older report-uri directive. Include both during transition, but plan to migrate fully to Reporting-Endpoints + report-to.

Key CSP Directives

DirectivePurposeRecommendation
default-srcFallback for other fetch directives'self'
script-srcControls script execution'self' 'nonce-{RANDOM}' 'strict-dynamic'
style-srcControls stylesheet loading'self' 'nonce-{RANDOM}'
frame-ancestorsControls framing (clickjacking)'none' or 'self'
object-srcBlocks plugins (Flash, etc.)'none'
base-uriPrevents base tag hijacking'self'
upgrade-insecure-requestsUpgrades HTTP to HTTPSInclude

CSP Comparison

NamePurposeCritical DirectivesBrowser SupportBreakage Risk
CSPClient-side content isolationdefault-src, script-src with nonce, frame-ancestorsChrome 25+, Firefox 23+, Safari 7+, Edge 12+Medium-High. Inline scripts without nonce break; third-party hosts must be declared

HTTP Strict Transport Security (HSTS)

HSTS forces browsers to use HTTPS for all future requests to your domain, preventing protocol downgrade and SSL stripping attacks.

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

Preload Readiness

To be eligible for the browser preload list, you need:

  • max-age ≥ 31,536,000 (one year)
  • includeSubDomains
  • preload directive
  • An HTTP-to-HTTPS redirect on the apex domain

Warning: The preload list is nearly irreversible. Removal takes several months and only affects future browser versions. Only submit when every subdomain serves HTTPS reliably and you have documented business approval.

As of April 2026, approximately 120,000 domains are in the Chrome preload list, and only 35.7% of sites that ship HSTS have enabled the preload directive.

HSTS Comparison

NamePurposeCritical DirectivesBrowser SupportBreakage Risk
HSTSTransport security, HTTPS enforcementmax-age, includeSubDomains, preloadChrome 4+, Firefox 4+, Safari 7+, Edge 12+High if any subdomain still serves HTTP

Referrer-Policy

Controls how much referrer information is sent in the Referer header.

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

This sends the full URL on same-origin requests, only the origin on cross-origin HTTPS-to-HTTPS, and nothing on HTTPS-to-HTTP downgrades.

For privacy-sensitive sites:

Referrer-Policy: no-referrer

Permissions-Policy

Controls access to browser features like geolocation, camera, and microphone.

Note: Permissions-Policy is experimental and not yet Baseline. Check browser compatibility before deploying in production.

Tight Default

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

Audit Steps

  1. Search your code for feature APIs (geolocation, camera, microphone, payment, usb, fullscreen).
  2. Use DevTools to see prompts and blocked features.
  3. Open policies for routes that need them.

X-Content-Type-Options

Prevents MIME sniffing-the browser’s dangerous habit of guessing content types.

Value

X-Content-Type-Options: nosniff

Set this on every response. No exceptions. Low risk, material security improvement.

COOP, COEP, CORP

These headers enable cross-origin isolation, protecting against cross-site leaks and enabling advanced features like SharedArrayBuffer.

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

Browser Support

HeaderBrowser Support
COOPChrome 83+, Firefox 79+, Safari 15.2+, Edge 83+
COEPChrome 83+, Edge 83+, Opera 69+; Firefox and Safari support vary
CORPChrome 73+, Edge 79+, Firefox 74+

When to Use Cross-Origin Isolation

  • You need SharedArrayBuffer or performance APIs that require isolation
  • You want stronger protections against cross-site leaks and popup communication

Troubleshooting

  • Images, fonts, or WASM failing to load under COEP usually lack Cross-Origin-Resource-Policy. Add CORP: same-origin on your assets.
  • Third-party widgets may not embed under COEP. Host them first-party or use credentialless.

Cross-Origin Resource Sharing (CORS)

CORS governs which origins, methods, and headers may access protected resources.

Safe Example (Preflight)

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 600
Vary: Origin

Safe Example (Simple Response)

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
Vary: Origin

Critical Rules

  • Never use Access-Control-Allow-Origin: * with credentials. The spec forbids this, and browsers reject it.
  • Always set Vary: Origin when responses differ by origin.
  • Allow only necessary methods and headers. Avoid Access-Control-Allow-Headers: *.
  • Keep Access-Control-Max-Age modest (e.g., 600 seconds) so policy changes take effect quickly.

Fetch Metadata

Fetch Metadata headers (Sec-Fetch-Site, Sec-Fetch-Mode, Sec-Fetch-Dest, Sec-Fetch-User) tell the server about the request context. Use them to block cross-site abuse.

Server-Side Check (Pseudocode)

site = get_header("Sec-Fetch-Site")
mode = get_header("Sec-Fetch-Mode")
is_unsafe_method = (method in [POST, PUT, PATCH, DELETE])
is_cross_site = (site == "cross-site")
is_navigational = (mode == "navigate")
if (is_cross_site && is_unsafe_method && !is_navigational) {
 reject()
}

Express Middleware Example

function fetchMetadataGuard(req, res, next) {
 const site = req.get("Sec-Fetch-Site") || "";
 const mode = req.get("Sec-Fetch-Mode") || "";
 const dest = req.get("Sec-Fetch-Dest") || "";
 const unsafe = ["POST", "PUT", "PATCH", "DELETE"].includes(req.method);
 const allowedSameSite = site === "" || site === "same-origin" || site === "same-site";
 const isNavigation = mode === "navigate" && dest === "document";
 if (unsafe && !allowedSameSite && !isNavigation) return res.sendStatus(403);
 next();
}

Browser Support

HeaderBrowser Support
Sec-Fetch-SiteChrome 76+, Edge 79+, Opera 63+
Sec-Fetch-ModeChrome 76+, Edge 79+

Subresource Integrity (SRI)

SRI ensures that CDN resources have not been tampered with by verifying a cryptographic hash.

Example

<script src="https://cdn.example.com/app.js"
 integrity="sha384-BASE64HASH"
 crossorigin="anonymous"></script>

Best Practices for 2026

  • SHA-384 is the recommended choice-a good balance between security and hash length. SHA-512 is also acceptable.
  • Always include the crossorigin attribute. Browsers silently ignore SRI without it.
  • Automate hash updates using build tools. Broken hashes break your site.
  • Pin to immutable versioned URLs. SRI cannot protect content that is designed to change.

Generate a Hash

openssl dgst -sha384 -binary app.js | openssl base64 -A

Secure Cookies

Protect session cookies with the right flags.

Set-Cookie: session=abc...; Path=/; Secure; HttpOnly; SameSite=Lax
  • Set Path=/ only when needed. Narrower paths reduce exposure.
  • Scope Domain to the fewest hosts possible.
  • Rotate session IDs on login and privilege changes.
  • Use short expirations for session cookies.
  • Use SameSite=None; Secure only for third-party cookies.

Obsolete or Discouraged Headers

HeaderStatusReasonReplacement
HTTP Public-Key-Pins (HPKP)Removed (2018)High risk of self-inflicted lockoutHSTS preload + Certificate Transparency
X-XSS-ProtectionDeprecated (2019)Ineffective; can create XSS vulnerabilitiesCSP with no inline scripts
Expect-CTDeprecated (2022)CT now enforced by browsers nativelyRely on Certificate Transparency
X-Frame-OptionsSupersededLimited syntax and controlCSP frame-ancestors

Configuration Recipes

Nginx

# /etc/nginx/conf.d/security.conf
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
location / {
 set_by_lua_block $csp_nonce {
 local rand = require("resty.random").bytes(16, true)
 return ngx.encode_base64(rand)
 }
 add_header Content-Security-Policy
 "default-src 'self'; base-uri 'self'; object-src 'none'; \
 script-src 'self' 'nonce-$csp_nonce'; style-src 'self'; img-src 'self' data:; \
 frame-ancestors 'self'; upgrade-insecure-requests" always;
}

Apache httpd

# In <VirtualHost> or .htaccess
Header always set Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; script-src 'self' 'nonce-%{CSP_NONCE}e'; style-src 'self'; img-src 'self' data:; frame-ancestors 'self'; upgrade-insecure-requests"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Content-Type-Options "nosniff"

Node.js Express

import express from "express";
import crypto from "crypto";
const app = express();
app.use((req, res, next) => {
 res.locals.cspNonce = crypto.randomBytes(16).toString("base64");
 next();
});
app.use((req, res, next) => {
 const nonce = res.locals.cspNonce;
 res.setHeader("Content-Security-Policy",
 "default-src 'self'; base-uri 'self'; object-src 'none'; " +
 "script-src 'self' 'nonce-" + nonce + "'; style-src 'self'; img-src 'self' data:; " +
 "frame-ancestors 'self'; upgrade-insecure-requests");
 res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
 res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
 res.setHeader("X-Content-Type-Options", "nosniff");
 next();
});

Verification

Command Line

# Show all response headers
curl -s -D - https://example.com/ -o /dev/null
# Check HSTS
curl -s -D - https://example.com/ -o /dev/null | grep -i strict-transport-security
# Check CORS preflight
curl -s -D - -X OPTIONS https://api.example.com/endpoint \
 -H "Origin: https://example.com" \
 -H "Access-Control-Request-Method: POST" -o /dev/null

Browser DevTools

  • Chrome: Network tab → select document → Response Headers
  • Firefox: Storage panel → Cookies → check Secure, HttpOnly, SameSite flags
  • Console: Reload to see CSP violations while in Report-Only

Online Scanners

  • securityheaders.com
  • observatory.mozilla.org

Rollout and Monitoring Plan

PhaseTimelineActivities
Inventory & BaselineWeek 1Map all domains/subdomains; document current header state; identify all third-party dependencies
CSP Report-OnlyWeeks 2-3Deploy CSP in report-only mode; monitor violations for 14+ days; fix legitimate violations
Basic HeadersWeek 4Deploy X-Content-Type-Options: nosniff; Referrer-Policy; Permissions-Policy with safe defaults
HSTS GradualWeeks 5-8Start with max-age=300; gradually increase; add includeSubDomains only after all subdomains verified
CSP EnforcementWeek 9+Switch CSP to enforce mode; keep reporting enabled; monitor for new violations
Advanced IsolationOptionalDeploy COOP/COEP/CORP only if needed; test cross-origin isolation requirements

Common Mistakes and Quick Fixes

MistakeFix
Access-Control-Allow-Origin: * with credentialsUse an allowlist and Vary: Origin
Only sending X-Frame-OptionsUse frame-ancestors in CSP
Forgetting X-Content-Type-Options: nosniffSet it on every response
Not setting cookie flagsUse Secure; HttpOnly; SameSite
Preloading HSTS before readyOnly submit when every subdomain serves HTTPS
CSP allows 'unsafe-inline'Use nonces or hashes; remove inline handlers
Missing frame-ancestors in CSPAdd it even if you also send X-Frame-Options
COEP require-corp without CORP on assetsAdd Cross-Origin-Resource-Policy on images, fonts, WASM

Key Takeaways

  1. CSP is the most powerful header. Start in Report-Only, use nonces for inline scripts, and keep reporting enabled. Since March 2026, report-to is Baseline-migrate from report-uri.
  2. HSTS preload is nearly irreversible. As of April 2026, only 35.7% of sites that ship HSTS use the preload directive. Submit only when you are certain.
  3. X-Content-Type-Options: nosniff goes on every response. No exceptions.
  4. CORS with credentials: never use *. Always set Vary: Origin.
  5. Test your headers. Use curl, browser DevTools, securityheaders.com, and observatory.mozilla.org.
  6. Roll out gradually. Start with report-only, monitor, then enforce. HSTS should be ramped up over weeks.
  7. Permissions-Policy is experimental. Check browser compatibility before deploying in production.

Conclusion

HTTP security headers are one of the highest-leverage security controls available to web developers. A few lines of configuration can block XSS, clickjacking, protocol downgrade, and data leakage attacks-all before your application code runs.

Start with the basics: X-Content-Type-Options: nosniff, Referrer-Policy, and HSTS. Add CSP in Report-Only mode and monitor violations. Gradually tighten policies as you validate their impact. Test with curl and online scanners. Document your headers as configuration as code.

Security headers are not a silver bullet-they complement input validation, output encoding, and authentication. But they are a critical layer of defense that every production web application should have.

Resources

Need help securing your web applications? Playful Sparkle has been engineering digital products since 2004, offering Web Development, App Development, and UI/UX & Web Design services. Contact us to discuss how we can help harden your application security.

Was this helpful - Post
Zsolt Oroszlány

Zsolt Oroszlány

Founder & Chief Creative Officer of Playful Sparkle since 2004, combining business leadership, digital strategy, design, and software engineering to help organizations build effective digital solutions. Regularly publishes insights on web development, SEO, design, and emerging technologies.