HTTP security headers are the fastest, most impactful security controls a web server can deploy. They cost nothing, require no code changes, and defend against entire classes of attacks — yet they are absent from the majority of websites. This guide explains what each header does, the attack it prevents, and how to configure it correctly.
What it does: HSTS instructs browsers to connect to your domain exclusively over HTTPS for a defined period, and to refuse plain HTTP connections entirely — even if the user types http:// or clicks an HTTP link. The browser enforces HTTPS internally without making a network request, so a network attacker has no opportunity to intercept and downgrade the connection.
The attack it prevents: Without HSTS, an attacker on the same network (public Wi-Fi, corporate network, ISP) can perform an SSL strip attack: intercept the initial HTTP request before it redirects to HTTPS and serve the victim a plain HTTP version of the site indefinitely. The user never sees an HTTPS connection and has no warning that their session is being intercepted.
Correct configuration:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age=31536000 — One year in seconds. Browsers remember and enforce HTTPS for this duration. Values below 31536000 are considered weak.includeSubDomains — Applies the policy to all subdomains, preventing SSL stripping attacks that redirect through an insecure subdomain.preload — Permits inclusion in browser preload lists (chromium.org/hsts). Preloaded domains get HSTS enforced even on the first ever visit, before the server has had a chance to send the header.Common mistake: Setting max-age=0 or a short value like max-age=300 for testing and forgetting to increase it. Browsers cache the HSTS policy, so a short max-age means no real protection.
What it does: CSP is an allowlist that tells the browser which sources of scripts, styles, images, fonts, and frames are permitted to load on a page. Any resource from a source not on the list is blocked before it executes. CSP is the primary browser-level defense against Cross-Site Scripting (XSS) and data injection attacks.
The attack it prevents: An XSS vulnerability allows an attacker to inject malicious JavaScript into a page viewed by victims. Without CSP, injected scripts execute with full access to the page — reading cookies, stealing session tokens, logging keystrokes, or exfiltrating form data to an attacker-controlled server. CSP blocks the injected script if it comes from an unlisted source, and blocks exfiltration if the destination is unlisted.
A strong starting configuration:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self'
default-src 'self' — Catch-all for any directive not explicitly specified. Restricts everything to same-origin by default.script-src — Controls JavaScript sources. This is the most critical directive for XSS prevention. Never use 'unsafe-inline' or 'unsafe-eval' in production — these directives completely neutralize CSP’s XSS protection.frame-ancestors 'none' — Prevents the page from being embedded in any frame. This is a stronger alternative to X-Frame-Options and can replace it entirely when present.base-uri 'self' — Prevents base tag injection, which attackers use to redirect all relative URLs to an attacker-controlled domain.form-action 'self' — Restricts where forms can submit, blocking exfiltration of form data via injected <form action="https://attacker.com"> tags.Common mistakes: Using script-src * (wildcard), which allows scripts from any domain and provides no XSS protection. Using 'unsafe-inline' to avoid adding nonces to inline scripts — this is the most frequent misconfiguration found in SPAT scans.
What it does: Tells the browser whether the page is permitted to be loaded inside a <frame>, <iframe>, or <object> element. Prevents other websites from embedding your pages in invisible frames overlaid on their own content.
The attack it prevents: Clickjacking. An attacker places your site in a transparent iframe over a fake UI, tricking users into clicking your buttons without realizing it. Classic targets include "transfer funds" or "delete account" buttons, social media like buttons, and any action that can be triggered with a single click.
Correct configuration:
X-Frame-Options: SAMEORIGIN
Use DENY if the page should never appear in a frame (login pages, payment pages, admin panels). Use SAMEORIGIN if legitimate embedding from your own domain is needed.
Note: If you have frame-ancestors in your CSP, it takes precedence over X-Frame-Options in modern browsers. Setting both provides backward compatibility with older browsers that don’t support CSP.
What it does: Instructs the browser not to infer ("sniff") the MIME type of a response and to treat it only as the declared Content-Type. The only valid value is nosniff.
The attack it prevents: MIME confusion attacks. If a server hosts user-uploaded content (profile pictures, documents) and responds with an incorrect or missing Content-Type, some browsers will try to detect the content type by inspecting the file. An attacker can upload a file containing HTML or JavaScript with a filename like photo.jpg. Without nosniff, the browser may execute it as a script even though the server said it was an image.
Correct configuration:
X-Content-Type-Options: nosniff
This is a single-value header with no configuration choices. Every server should serve it on every response.
What it does: Controls what URL information is included in the Referer header when a user navigates from your site to another. By default, browsers send the full URL of the page the user came from, including any query parameters, to the destination server.
Why it matters: Query parameters in URLs frequently carry sensitive data: password reset tokens (/reset?token=abc123), session identifiers, search terms that reveal user intent, or email addresses. Without a restrictive Referrer-Policy, these values are sent to every third-party script, analytics service, and external link your site includes.
Recommended configuration:
Referrer-Policy: strict-origin-when-cross-origin
https://yoursite.com), not the path or query string.For high-security pages (admin, payment, password reset): use no-referrer to send nothing to any external destination.
What it does: Controls access to browser APIs and hardware features — camera, microphone, geolocation, payment handler, USB, accelerometer — for the page and any embedded third-party frames. Formerly called Feature-Policy.
Why it matters: Third-party scripts included via advertising networks, analytics, or social media widgets run with the same browser API access as your own code. Without Permissions-Policy, an XSS vulnerability or a compromised third-party script can silently activate the user’s microphone or camera. Permissions-Policy creates a browser-enforced boundary that constrains what any script on the page can access, regardless of where it came from.
A privacy-protective configuration:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Empty parentheses () mean "no origin is permitted to use this feature." Add self inside to permit your own origin: camera=(self). Only list origins that genuinely need the capability.
Strict-Transport-Security: max-age=31536000; includeSubDomains on all HTTPS responsesContent-Security-Policy with no unsafe-inline, no unsafe-eval, no wildcard * in script-srcX-Frame-Options: SAMEORIGIN or frame-ancestors 'self' in CSPX-Content-Type-Options: nosniff on every responseReferrer-Policy: strict-origin-when-cross-origin or stricterPermissions-Policy disabling unused browser APIsUse SPAT’s free scanner to check your domain’s headers now — results in under 60 seconds.