What Are HTTP Security Headers and Why They Matter
Seven HTTP response headers that block XSS, clickjacking, MIME sniffing, and data leaks. How to configure each one, with copy-paste examples for Nginx, Apache, and Vercel.
Key Takeaway
Most websites ship zero security headers. Adding seven response headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and COOP) blocks entire classes of attacks with no code changes. It takes 15 minutes to configure.
What Are Security Headers?
Security headers are HTTP response headers that instruct the browser to enable (or restrict) specific security behaviors. They are not application code. They are server configuration. You set them in Nginx, Apache, Vercel, Cloudflare, or your framework's middleware, and the browser enforces them on every page load.
Without them, browsers fall back to permissive defaults: any script can run, any site can frame your page, MIME types can be guessed, and referrer data leaks to third parties. Attackers know this. The first thing a pentest checks is which headers are missing. For the full security picture, see our Complete Guide to Web Security.
How common is the problem? We scanned 100 YC startups in April 2026 and found that 94% were missing two or more of these headers, and 91% shipped without any Content-Security-Policy at all. The full YC audit has the per-header breakdown and raw data.
Content-Security-Policy (CSP)
CSP is the most powerful security header. It defines a whitelist of sources the browser is allowed to load resources from. If an attacker injects a <script> tag pointing to their server, CSP blocks it because that origin is not in the policy.
A starter CSP that blocks most XSS attacks:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
form-action 'self'Key rules: avoid 'unsafe-inline' for script-src— use nonces ('nonce-abc123') instead. Set object-src 'none' to kill plugin-based attacks. Use report-uri or report-to to get violation reports in production without blocking anything (Content-Security-Policy-Report-Only). Full reference: MDN: CSP.
Strict-Transport-Security (HSTS)
HSTS tells the browser to never load your site over HTTP. After seeing the header once, the browser automatically upgrades every request to HTTPS — no server-side redirect needed for subsequent visits.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadStart with a short max-age (e.g., 300 seconds) during testing. Once confirmed, bump to two years (63072000). Add preload and submit to hstspreload.org so browsers enforce HTTPS even on the first visit. Defined in RFC 6797.
X-Frame-Options
Prevents your page from being loaded in a <frame>, <iframe>, or <object>. This stops clickjacking: an attacker embeds your site in an invisible iframe and tricks users into clicking buttons they cannot see.
X-Frame-Options: DENYDENY blocks all framing. SAMEORIGIN allows your own domain. The modern replacement is CSP frame-ancestors, but X-Frame-Options is still needed for IE11 and older browsers. Spec: MDN: X-Frame-Options.
X-Content-Type-Options
Browsers sometimes guess the MIME type of a response by inspecting its content (MIME sniffing). An attacker can upload a file with a .jpg extension that contains JavaScript. Without this header, the browser might execute it.
X-Content-Type-Options: nosniffOne value, one line, blocks an entire attack class. Always set it. See MDN: X-Content-Type-Options.
Referrer-Policy
By default, browsers send the full URL in the Referer header when navigating to another page. If your URLs contain tokens, session IDs, or personally identifiable information, those leak to every external service you link to.
Referrer-Policy: strict-origin-when-cross-originThis sends origin-only for cross-origin requests and full URL for same-origin. For maximum privacy, use no-referrer. Reference: MDN: Referrer-Policy.
Permissions-Policy
Controls access to browser features like camera, microphone, geolocation, and payment. If your site does not use a feature, disable it. This way, even if malicious code runs on your page, it cannot access the hardware.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=()Empty parentheses means “disabled for all origins.” You can also allow specific origins: camera=(self "https://meet.example.com"). Full list: MDN: Permissions-Policy.
Cross-Origin-Opener-Policy (COOP)
When your site opens a popup or is opened by another site via window.open, the opener retains a reference to the opened window (and vice versa). An attacker can use this to navigate the opener to a phishing page. COOP severs this reference.
Cross-Origin-Opener-Policy: same-originAlso required for cross-origin isolation (enabling SharedArrayBuffer). See MDN: COOP.
Full Example Configurations
Copy-paste configurations for common setups:
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'" 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;Apache (.htaccess)
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; 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=()"
Header always set Cross-Origin-Opener-Policy "same-origin"Vercel (vercel.json)
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" },
{ "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains; preload" },
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }
]
}
]
}Test Your Headers
Our scanner checks all seven headers and flags missing or misconfigured ones with specific fix instructions. Takes 60 seconds.
Check your website right now
110 security checks in 60 seconds. Free, no signup required.
Scan My Website (Free)ismycodesafe.com Security Team
We run automated security scans on thousands of websites daily, combining static analysis, SSL/TLS inspection, header auditing, and CVE lookups. Our team tracks OWASP, NIST, and evolving compliance requirements (GDPR, NIS2, PCI DSS) to keep these guides accurate and practical.
Related Articles
94% of YC startups ship with missing security headers (2026 audit)
Original research: we scanned 100 Y Combinator companies. Full per-header breakdown and raw CSV.
The Complete Guide to Web Security in 2026
The pillar guide covering TLS, headers, CORS, cookies, and HTTPS enforcement.
SSL/TLS Certificates Explained: Setup, Errors, and Best Practices
Certificate types, Let's Encrypt, common errors, and TLS 1.3 migration.
Cross-Site Scripting (XSS): How Attackers Steal User Data
How XSS works, the three attack types, and why CSP is your strongest defense.