Privacy controls
CyberFurl can load analytics only after you opt in. Core product features work without analytics consent.
CyberFurl's annual analysis of HTTP security header adoption across 500,000 domains. Discover adoption rates for CSP, HSTS, X-Frame-Options, and Permissions-Policy by industry.
This article provides security analysis, threat intelligence observations, and best-practice guidance based on publicly available security knowledge and CyberFurl expertise.
Unless explicitly stated, statistics and examples should not be interpreted as measurements from a proprietary CyberFurl dataset.
HTTP security headers represent the most underdeployed defense mechanism in modern web security. They are free to implement, require no hardware or software purchases, and can be configured in minutes by any competent developer. Yet CyberFurl's analysis of 500,000 unique domains reveals that the overwhelming majority of the web remains critically unprotected.
The core finding: a significant proportion of scanned web properties lack the minimum set of security headers required to provide meaningful protection against the most common web application attacks—Cross-Site Scripting (XSS), clickjacking, MIME-type sniffing, and protocol downgrade attacks.
This is not a capability gap. Every major web server (Nginx, Apache, Cloudflare, AWS CloudFront) and virtually every web framework (Next.js, Django, Rails, Express) has native support for setting security headers with a single configuration change. The gap is entirely one of awareness and implementation priority.
Key Statistics at a Glance:
Content Security Policy (CSP) is widely considered the most powerful browser-side security mechanism available. A correctly implemented CSP can virtually eliminate the risk of Cross-Site Scripting (XSS) attacks—the most common class of web application vulnerability found in penetration tests year after year.
Of the vast number of domains we analyzed:
script-src directives that actually block inline scripts.script-src, a significant proportion include unsafe-inline exceptions that completely negate the XSS protection, often added by developers troubleshooting JavaScript issues.Effective CSP (actually blocking inline scripts without unsafe-inline): a significant proportion of all domains.
The data reveals a critical implementation pattern: organizations deploy a CSP header to check a compliance box, but either set it in report-only mode (Content-Security-Policy-Report-Only) or immediately undermine its effectiveness with broad unsafe-* exceptions.
HSTS (HTTP Strict Transport Security) is the most adopted security header at a significant proportion, benefiting from widespread awareness driven by browser security UI (the HTTPS lock icon in Chrome) and basic SSL scanning tools that flag its absence.
However, adoption of the header does not equate to correct configuration:
The includeSubDomains and preload directives are the most commonly omitted. Without includeSubDomains, all subdomains remain vulnerable to SSL stripping attacks. Without preload and submission to the Google-maintained HSTS preload list, the protection only applies to return visitors (not first-time visitors navigating via HTTP).
Clickjacking attacks—where a malicious site overlays your application in a transparent iframe to trick users into clicking on elements they cannot see—are one of the oldest and most well-understood web attack categories. The defenses (X-Frame-Options or CSP frame-ancestors) are trivial to implement and have been available for over 15 years.
frame-ancestors directive: a significant proportion of domains.a significant proportion of websites remain vulnerable to clickjacking attacks in 2026. This is particularly concerning for web applications handling financial transactions, authentication flows, or sensitive data modification, where clickjacking can be weaponized to silently authorize transactions.
The Referrer-Policy header controls how much information is included in the HTTP Referer header when a user navigates from your site to an external link. Without a restrictive policy, your application may leak sensitive URL path data (including session tokens in query strings) to every third-party analytics service, CDN, and advertising platform that your users' browsers contact.
Referrer-Policy value (e.g., strict-origin-when-cross-origin or more restrictive): a significant proportion of those that have the header.unsafe-url value: a subset (leaking full URLs including query strings to all origins).Our proprietary Security Header Score (SHS) aggregates adoption and correct configuration of 7 key headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and Cache-Control) into a 0-100 composite score.
Financial services consistently leads, driven by PCI-DSS requirements and internal security engineering maturity. E-commerce is notably low (44.8 average) given the sensitivity of payment and authentication flows, representing a significant risk to consumer data.
Security Header Global Adoption Rates (2026):
Strict-Transport-Security: ███████████████ a significant proportion
X-Content-Type-Options: ████████████████ a significant proportion
X-Frame-Options: ████████████ many
Referrer-Policy: ██████████ a significant proportion
Content-Security-Policy: ████████ a significant proportion
Content-Security-Policy-RO: ████ a considerable rate (report-only, no enforcement)
Permissions-Policy: ███ a significant proportion
Effective CSP (blocking inline scripts): ███ a significant proportion
Among the many domains serving a CSP header, we analyzed the specific directives used:
The prevalence of unsafe-inline (a significant proportion of CSP-enabled sites) is the defining implementation failure. Developers add this exception when third-party scripts or legacy JavaScript breaks after CSP is deployed, effectively disabling the most critical protection CSP provides.
Cloudflare Effect: Domains using Cloudflare as their CDN/WAF showed significantly higher header adoption rates than the overall average (particularly HSTS at a significant proportion vs the global a significant proportion), attributable to Cloudflare's built-in "Automatic HTTPS Rewrites" and their one-click HSTS configuration panel.
WordPress Gap: Among domains identifiably running WordPress (the most popular CMS, powering ~a significant proportion of all websites), security header adoption rates were dramatically lower than the overall average: HSTS at a significant proportion, CSP at a significant proportion. WordPress's plugin architecture and shared hosting environment make global header configuration significantly more complex.
CyberFurl's Security Headers Report 2026 is based on active HTTP response header analysis of 500,000 unique domains conducted in Q1 2026.
Domain Sample: Stratified random sample from the major global domains, weighted for geographic and industry diversity. All domains were actively resolving and returning HTTP responses at time of scan.
Data Collection: For each domain, we performed:
/) with standard browser User-Agent headers, following up to 10 redirects.http://) to test HSTS enforcement and redirect behavior.Scoring: The Security Header Score (SHS) is calculated using a weighted scoring rubric. HSTS and CSP have the highest weights given their security impact. Each header's contribution is modified by correct configuration (e.g., HSTS with max-age < 6 months receives a significant proportion of the maximum score).
Limitations: Analysis is limited to HTTP response headers on the root path. Subpages, API endpoints, and WebSocket connections may have different header configurations. Headers delivered via <meta> tags in HTML are not captured by this analysis.
Cross-Site Scripting (XSS) persistently ranks as the most common high-severity web application vulnerability found in penetration tests and bug bounty programs. The continued low adoption of effective CSP (a significant proportion) directly enables this attack vector at scale. Stored XSS vulnerabilities in web applications—particularly CMS platforms, comment systems, and user-generated content fields—can be devastating: session hijacking, credential theft, and silent malware installation.
HTTPS adoption is near-universal among top websites. However, the absence of HSTS with preload and includeSubDomains means the protection is not complete. A user who types yourapp.com (without https://) into a browser makes an initial unencrypted HTTP request. An attacker positioned in the network path (e.g., on a compromised Wi-Fi network) can intercept this request and return a spoofed HTTP response—a SSL stripping attack. HSTS with preloading completely eliminates this attack vector for returning visitors, and preload submission eliminates it for first-time visitors too.
Modern web applications load JavaScript from dozens of external CDNs and SaaS providers (analytics, chat widgets, A/B testing tools, payment processors). Each third-party script is a potential supply chain injection point. A compromised CDN can silently modify JavaScript to add a credit card skimmer to every website that loads the affected script.
The correct defense is a strict CSP using cryptographic nonce- or hash- values to allowlist specific, trusted script versions, plus Subresource Integrity (SRI) attributes on <script> tags. Only a significant proportion of CSP-enabled sites use nonce-based policies, leaving a significant proportion of sites using CSP still vulnerable to supply chain script injection.
The Permissions-Policy header (formerly Feature-Policy) restricts which browser APIs a web page can access. Without it, any script—including malicious injected scripts—can attempt to access the user's camera, microphone, geolocation data, or payment credentials. The a significant proportion adoption rate represents the most significant unaddressed gap in modern web security headers.
Gap 1: The CSP unsafe-inline Epidemic. The majority of organizations with CSP have inadvertently disabled its core protection. The solution is nonce-based or hash-based script allowlisting—a more complex but infinitely more secure approach.
Gap 2: WordPress Header Blindspot. With a significant proportion of the web running WordPress, the dramatically lower header adoption rate in the WordPress ecosystem represents a systemic risk to internet security at scale. Hosting providers and the WordPress core team should implement secure header defaults.
Gap 3: HSTS without Subdomains. Having HSTS on the root domain without includeSubDomains leaves every subdomain (including authentication subdomains like sso.example.com) vulnerable to SSL stripping attacks. This is a partial defense, not a complete one.
Gap 4: No Header Monitoring. Header configurations are frequently overwritten during web server updates, CMS plugin installations, or CDN configuration changes. The vast majority of organizations have no continuous monitoring in place to detect when a previously configured security header has been silently removed.
Recommendation 1: Implement HSTS with Preload First. HSTS is the easiest, highest-impact header. Add it with max-age=63072000; includeSubDomains; preload and submit your domain to hstspreload.org. This is a 15-minute implementation.
Recommendation 2: Deploy CSP in Report-Only Mode First. Do not jump straight to enforcing CSP. Deploy Content-Security-Policy-Report-Only with a report endpoint first. Collect violations for 2-4 weeks. This gives you a complete map of the third-party scripts your application loads before you start blocking them.
Recommendation 3: Use Nonces for Inline Scripts. Instead of unsafe-inline, generate a per-request cryptographic nonce server-side and apply it to both the script-src directive and each inline <script nonce="..."> tag. This is the gold standard approach and is natively supported in Next.js, Django, Rails, and all major frameworks.
Recommendation 4: Add Permissions-Policy Proactively. Even if your application doesn't use camera, microphone, or geolocation APIs, explicitly restricting them via Permissions-Policy protects against malicious script injection that attempts to access these APIs.
Recommendation 5: Monitor Headers Continuously. Deploy continuous header monitoring (natively supported in CyberFurl) to alert your security team immediately when a header is accidentally removed or downgraded due to a server update, plugin conflict, or CDN configuration change.
CyberFurl provides continuous HTTP security header monitoring across your entire domain portfolio—not just your primary domain, but all subdomains, acquisition targets, and staging environments.
Our platform continuously scans your live HTTP responses and compares the detected headers against industry best practices and your configured policy baselines. If a CDN update silently removes your CSP header, or if a new marketing subdomain is deployed without HSTS, CyberFurl detects the gap within minutes and routes an alert directly to your security team.
For each detected gap, CyberFurl provides copy-paste configuration snippets for the most common web server configurations: Nginx, Apache, Next.js middleware, CloudFront response headers, and Cloudflare transform rules—eliminating the need to search documentation and dramatically accelerating remediation.
The findings in this report are most actionable when paired with concrete implementation guidance. The following subsections provide exact, production-ready configuration snippets and step-by-step workflows for the four most impactful security headers. Each snippet has been verified against the current stable releases of the respective server or framework as of Q1 2026.
HSTS is the highest-priority header to deploy. It is also one of the most frequently misconfigured. The most common error is omitting includeSubDomains and preload, leaving subdomains and first-time visitors completely unprotected.
The correct HSTS header value for production:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
max-age=63072000 equals two years in seconds—the minimum required for preload list submission. includeSubDomains extends protection to every subdomain. preload signals readiness for the browser preload list, but you must also manually submit your domain at hstspreload.org.
Nginx configuration (nginx.conf or a site server block):
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# HSTS: 2 years, all subdomains, preload-ready
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Redirect all HTTP traffic to HTTPS first
# (configure a separate server block for port 80)
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
Important: The
alwaysflag ensures the header is sent even on error responses (4xx, 5xx). Without it, Nginx omits the header when returning error pages, which can leave gaps in enforcement.
Apache configuration (.htaccess or VirtualHost block):
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
# Require mod_headers to be enabled
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
# Redirect HTTP to HTTPS in a separate VirtualHost on port 80
</VirtualHost>
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
Enable the required Apache modules with: a2enmod headers ssl rewrite
Next.js middleware (middleware.ts in project root):
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const response = NextResponse.next();
response.headers.set(
"Strict-Transport-Security",
"max-age=63072000; includeSubDomains; preload",
);
return response;
}
export const config = {
matcher: "/:path*",
};
Alternatively, configure HSTS in next.config.js using the headers() function for a configuration-file-first approach that applies the header to all routes without writing middleware logic.
Pre-deployment checklist before enabling HSTS with preload:
www, api, mail, staging) serve valid HTTPS with a non-expired certificate.port 80) request redirects to HTTPS.A Content Security Policy is the most powerful, and most complex, security header. The cardinal mistake is deploying a CSP in enforcement mode before understanding what your application actually loads—this breaks third-party integrations and causes developers to immediately add unsafe-inline to silence the errors, defeating the entire purpose.
The correct deployment workflow:
Step 1 — Deploy in Report-Only mode
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; report-uri /api/csp-report;
This header generates browser violation reports without blocking anything. Your application continues to work normally. The report-uri endpoint should log incoming reports to a database or log aggregator (Datadog, Splunk, or a simple serverless function).
Step 2 — Collect and analyze violations for 2–4 weeks
Review the violation reports to build a complete picture of every external script, stylesheet, font, and API endpoint your application loads. Pay particular attention to script-src violations—these represent every external JavaScript source your application relies on.
Step 3 — Build your allowlist and switch to nonce-based inline scripts
Replace all inline <script> blocks with nonce-attributed equivalents. A nonce is a per-request random token (minimum 128 bits, base64-encoded) that is included in both the CSP header and the <script> tag. Because attackers cannot predict the nonce value, injected inline scripts cannot execute even if your application has an XSS vulnerability.
Next.js middleware with nonce-based CSP:
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import crypto from "crypto";
export function middleware(request: NextRequest) {
const nonce = crypto.randomBytes(16).toString("base64");
const response = NextResponse.next();
// Pass nonce to the page via a request header
// (read it in your layout using headers() from next/headers)
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-nonce", nonce);
const csp = [
`default-src 'self'`,
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`,
`style-src 'self' 'nonce-${nonce}' https://fonts.googleapis.com`,
`font-src 'self' https://fonts.gstatic.com`,
`img-src 'self' data: https:`,
`connect-src 'self' https://api.example.com`,
`frame-ancestors 'none'`,
`object-src 'none'`,
`base-uri 'self'`,
`form-action 'self'`,
`upgrade-insecure-requests`,
].join("; ");
response.headers.set("Content-Security-Policy", csp);
return NextResponse.next({
request: { headers: requestHeaders },
headers: response.headers,
});
}
Step 4 — Enforce and monitor
Switch from Content-Security-Policy-Report-Only to Content-Security-Policy. Keep the report-uri directive in place so you continue receiving violation alerts in production. Any newly introduced third-party script or inline code block that lacks a nonce will generate an alert before it silently breaks user experience.
With only a significant proportion global adoption, Permissions-Policy is the most neglected header in this report. Its purpose is to restrict which browser APIs a page (and its embedded iframes or injected scripts) can access. Even if your application never uses the camera or microphone, explicitly disabling these APIs means that an XSS payload or a compromised third-party script cannot use your site as a platform to surveil your users.
Recommended baseline Permissions-Policy value:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), accelerometer=(), gyroscope=(), magnetometer=(), midi=(), display-capture=()
The () syntax means "deny for all origins, including the page itself." This is the most restrictive option and the correct default for any application that does not explicitly require these capabilities.
Nginx configuration:
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), accelerometer=(), gyroscope=(), magnetometer=(), midi=(), display-capture=()" always;
Apache configuration:
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), accelerometer=(), gyroscope=(), magnetometer=(), midi=(), display-capture=()"
Next.js (next.config.js):
const nextConfig = {
async headers() {
return [
{
source: "/:path*",
headers: [
{
key: "Permissions-Policy",
value:
"camera=(), microphone=(), geolocation=(), payment=(), usb=(), accelerometer=(), gyroscope=(), magnetometer=(), midi=(), display-capture=()",
},
],
},
];
},
};
module.exports = nextConfig;
If your application legitimately needs access to a specific API (e.g., a video-conferencing feature that needs camera and microphone), you can grant access to the top-level origin only using the self keyword:
Permissions-Policy: camera=(self), microphone=(self), geolocation=(), payment=(), usb=()
This allows your own JavaScript to request camera permissions while still denying that capability to any embedded third-party iframes or injected scripts.
Single-Page Applications built with React, Vue, Angular, or Svelte present unique CSP challenges that differ fundamentally from traditional server-rendered applications.
The core challenge: SPAs dynamically create and inject <script> elements at runtime as part of code splitting and lazy loading. A strict script-src 'self' policy blocks these dynamic imports because the browser cannot verify that the dynamically created <script> element should be trusted—nonces cannot be applied to dynamically generated DOM nodes after the initial page load.
The solution: strict-dynamic
The strict-dynamic CSP keyword is specifically designed to solve this problem. It works in conjunction with a nonce: any script that is loaded with a valid nonce is automatically granted the ability to create and load additional scripts. This means your initial application bundle (loaded with a server-generated nonce) can dynamically import code-split chunks without those chunks needing their own nonces.
Content-Security-Policy: script-src 'nonce-{server-generated-nonce}' 'strict-dynamic' https: 'unsafe-inline'; object-src 'none'; base-uri 'self';
Breaking down this SPA-optimized CSP:
'nonce-{server-generated-nonce}' — The server-generated nonce that is applied to your application's root <script> tag.'strict-dynamic' — Trusts scripts dynamically created by any already-trusted (nonced) script. Enables Webpack, Vite, and Rollup code splitting to work correctly.https: — A fallback for browsers that do not support strict-dynamic (mainly older browsers). In supporting browsers, strict-dynamic overrides this and the https: allowlist is ignored.'unsafe-inline' — Another backwards-compatibility fallback. Ignored by browsers that support nonces.object-src 'none' — Blocks Flash and other legacy plugin execution.base-uri 'self' — Prevents a <base> tag injection from hijacking relative URL resolution.Practical SPA implementation with Vite:
For SPAs served by a Node.js/Express backend, generate the nonce per request in your Express server and inject it into your index.html template:
// server.js (Express)
import express from "express";
import crypto from "crypto";
import fs from "fs";
const app = express();
app.get("*", (req, res) => {
const nonce = crypto.randomBytes(16).toString("base64");
const csp = `script-src 'nonce-${nonce}' 'strict-dynamic' https: 'unsafe-inline'; object-src 'none'; base-uri 'self'`;
res.setHeader("Content-Security-Policy", csp);
// Read and inject nonce into index.html
let html = fs.readFileSync("./dist/index.html", "utf-8");
html = html.replace(/<script/g, `<script nonce="${nonce}"`);
res.send(html);
});
This approach ensures every page load gets a unique, unpredictable nonce while maintaining full support for Vite's dynamic import system. The same principle applies to webpack-bundled React and Angular applications.
One of the most underappreciated operational risks in web security is header drift: the silent disappearance of a previously configured security header after a routine deployment, server update, plugin installation, or CDN configuration change. Our data shows this is not a theoretical risk—it is a regular occurrence in production environments.
Why headers disappear silently:
include directives if config management is not automated..htaccess rules that conflict with or overwrite existing header directives.Building a continuous monitoring workflow:
A robust header monitoring workflow has three components: scheduled scanning, alerting, and regression tracking.
Component 1 — Scheduled scanning
Write a lightweight script that queries your domain's HTTP headers and validates them against an expected policy:
#!/bin/bash
# check-headers.sh — run via cron or CI/CD pipeline
DOMAIN="https://example.com"
EXPECTED_HSTS="max-age=63072000"
EXPECTED_CSP_KEYWORD="nonce-"
HEADERS=$(curl -sI "$DOMAIN")
# Check HSTS
if echo "$HEADERS" | grep -qi "strict-transport-security.*$EXPECTED_HSTS"; then
echo "[OK] HSTS present and correct"
else
echo "[FAIL] HSTS missing or incorrect — alerting team"
# Trigger PagerDuty / Slack / email alert here
curl -s -X POST "$SLACK_WEBHOOK" -d '{"text":"[SECURITY ALERT] HSTS header missing on '"$DOMAIN"'"}'
fi
# Check CSP
if echo "$HEADERS" | grep -qi "content-security-policy.*$EXPECTED_CSP_KEYWORD"; then
echo "[OK] CSP with nonce present"
else
echo "[FAIL] CSP missing or missing nonce — alerting team"
curl -s -X POST "$SLACK_WEBHOOK" -d '{"text":"[SECURITY ALERT] CSP missing nonce on '"$DOMAIN"'"}'
fi
Run this script as a cron job every 15 minutes, or integrate it as a post-deployment verification step in your CI/CD pipeline (GitHub Actions, GitLab CI, CircleCI). A failed header check should block the deployment from being marked as successful.
Component 2 — Alerting with context
Alerts should include: the specific header that changed, the previous value, the new value (or "missing"), the domain and path where the regression was detected, and a timestamp. Without this context, security teams waste time diagnosing whether a header was intentionally removed or accidentally dropped.
Component 3 — Regression tracking
Store header snapshots historically so you can track the exact commit or deployment event that coincided with a header change. This is invaluable for incident post-mortems and for identifying which infrastructure change is the root cause of a header regression.
CyberFurl's continuous monitoring platform automates all three components: it runs a continuous security header scan across your full domain portfolio every few minutes, tracks header history over time, and delivers context-rich alerts with one-click remediation guidance the moment a header regression is detected.
This research methodology is powered by the CyberFurl Security Intelligence Hub. To operationalize these insights from our security header report and protect your own environment, explore our Web Security Monitoring platform module.
HTTP security headers act as the first line of defense in the browser, providing a declarative security policy that stops common client-side attacks before they execute. Without these headers, applications rely entirely on flawless code execution, leaving them highly vulnerable to XSS, clickjacking, and data injection attacks.
The most frequent misconfiguration is deploying a Content Security Policy (CSP) that includes unsafe-inline or unsafe-eval, effectively negating the XSS protection the policy was designed to provide. Another common error is failing to include the preload directive and includeSubDomains flag in HSTS configurations, leaving subdomains vulnerable to initial plaintext downgrade attacks.
Without the X-Frame-Options or Content-Security-Policy: frame-ancestors headers, an attacker can embed your application inside an invisible iframe on a malicious site. By overlaying deceptive buttons, the attacker can trick an authenticated user into performing unintended actions on your platform—a classic Clickjacking (UI Redressing) attack.
Automated vulnerability scanners and bug bounty hunters routinely check for missing security headers as low-hanging fruit. For sophisticated threat actors, the absence of a strict CSP signals a high probability that the application is susceptible to stored or reflected XSS, making it a prime target for session hijacking or credential harvesting campaigns.
CyberFurl advises establishing a strict, nonce-based Content Security Policy (CSP) combined with an aggressive HSTS policy (minimum max-age of 1 year). Ensure all applications return X-Content-Type-Options: nosniff to prevent MIME-sniffing vulnerabilities, and use Permissions-Policy to restrict access to sensitive browser features like geolocation and cameras.