HTTP Security Headers: CSP, HSTS, and Beyond
Which HTTP security headers your site needs and why. Covers Content-Security-Policy, HSTS, X-Frame-Options, and Permissions-Policy.
What are HTTP security headers?
Every time a browser requests a page, the server responds with content and a set of HTTP headers. Most headers are functional — Content-Type specifies the format, Cache-Control manages caching. Security headers add a policy layer that restricts what the browser can do with the content.
The value of security headers is that they mitigate attacks at the browser level, even when the application has vulnerabilities. A Content-Security-Policy header can prevent a cross-site scripting (XSS) vulnerability from being exploited, even if the XSS flaw exists in the code. An HSTS header prevents protocol downgrade attacks regardless of how the application handles HTTP requests.
According to the HTTP Archive’s 2024 Web Almanac, only 14% of the top one million websites had a Content-Security-Policy header, and only 27% set Strict-Transport-Security. The gap between available mitigations and actual deployment represents a significant opportunity for attackers.
Which security headers does every website need?
The essential security headers, in order of impact:
| Header | Purpose | Recommended Value |
|---|---|---|
| Content-Security-Policy | Restricts script, style, and resource sources | default-src 'self'; script-src 'self' (adapt to your needs) |
| Strict-Transport-Security | Forces HTTPS connections | max-age=31536000; includeSubDomains; preload |
| X-Content-Type-Options | Prevents MIME type sniffing | nosniff |
| X-Frame-Options | Prevents clickjacking via framing | DENY or SAMEORIGIN |
| Referrer-Policy | Controls referrer information leakage | strict-origin-when-cross-origin |
| Permissions-Policy | Restricts browser feature access | camera=(), microphone=(), geolocation=() |
These six headers cover the most common browser-level attack vectors. They can be implemented at the web server level (Nginx, Apache, Caddy), at the CDN level (Cloudflare, Fastly, AWS CloudFront), or in application middleware.
What is Content-Security-Policy and how does it prevent XSS?
Cross-site scripting (XSS) is consistently one of the most common web vulnerabilities. In the OWASP Top Ten (2021), injection vulnerabilities including XSS are ranked A03 — the third most critical category of web application security risks. CSP is the most effective browser-level mitigation.
A CSP header tells the browser: “Only load scripts from these specific sources. Ignore everything else.” If an attacker injects a <script> tag into the page through an XSS vulnerability, the browser refuses to execute it because the injected script does not come from an allowed source.
A basic CSP policy:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'
This policy allows scripts only from the site’s own origin and cdn.example.com, allows inline styles (common but not ideal), allows images from the same origin and data URIs, and allows fonts from the same origin. Everything else — including inline scripts injected via XSS — is blocked.
CSP deployment challenges. CSP is powerful but requires careful rollout:
- Audit existing scripts first. CSP will break any script not in the allowlist. Catalogue all scripts, inline handlers, and third-party integrations before deploying.
- Start in report-only mode. Use
Content-Security-Policy-Report-Onlyto detect violations without blocking content. Collect reports, fix violations, then switch to enforcement. - Avoid
unsafe-inlinefor scripts. Allowing inline scripts ('unsafe-inline') significantly weakens CSP because most XSS payloads are inline. Use nonces or hashes instead.
How SurfaceLoop handles this
SurfaceLoop checks HTTP security headers across all your domains and subdomains. It identifies missing headers, weak configurations, and common mistakes like CSP policies that allow unsafe-inline for scripts. Each finding includes the specific header value needed to fix the issue.
What is HSTS and why should every site use it?
Without HSTS, the first request to a site may happen over HTTP — when a user types example.com in the address bar without https://, or follows an HTTP link. An attacker positioned between the user and the server (on public Wi-Fi, for example) can intercept this initial HTTP request and prevent the redirect to HTTPS, keeping the entire session on unencrypted HTTP.
HSTS eliminates this window. After a browser receives the HSTS header once over a valid HTTPS connection, all subsequent connections to that domain use HTTPS automatically — even if the user types http:// or follows an HTTP link.
The recommended HSTS configuration:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age=31536000— enforce for one year (in seconds)includeSubDomains— apply to all subdomains, not just the main domainpreload— eligible for inclusion in browser HSTS preload lists, which enforce HTTPS from the very first visit
HSTS preloading. The preload directive signals that you want your domain added to browsers’ built-in HSTS preload lists. Once preloaded, browsers enforce HTTPS for your domain before any network request is made — eliminating even the first-visit vulnerability. Submit your domain at hstspreload.org after confirming all subdomains support HTTPS.
How do you check which security headers your site has?
Several approaches, from manual to automated:
Browser developer tools. Open DevTools (F12), go to the Network tab, select any request, and inspect the Response Headers section. This shows exactly what headers the server is sending. Useful for spot checks but does not scale.
Command-line inspection. Use curl to retrieve headers:
curl -I https://example.com
The -I flag returns only headers. Check for the presence and values of security headers in the output.
Online scanners. SecurityHeaders.com provides a quick grade and detailed breakdown of which headers are present, which are missing, and what values are recommended. It is useful for initial assessment and benchmarking.
Continuous EASM scanning. For organisations with multiple domains and subdomains, manual checking is insufficient. EASM platforms scan all endpoints continuously, flagging missing or weak headers as they are discovered. This catches new services deployed without proper header configuration and detects configuration drift after server or CDN changes.
What is the difference between X-Frame-Options and CSP frame-ancestors?
Clickjacking is an attack where a malicious site embeds your page in an invisible frame and tricks users into clicking on it — performing actions on your site without their knowledge. Both headers prevent this, but they differ in capability:
| Feature | X-Frame-Options | CSP frame-ancestors |
|---|---|---|
| Granularity | DENY or SAMEORIGIN only | Any list of specific origins |
| Browser support | Universal (legacy and modern) | Modern browsers only |
| Standard status | Informally deprecated in favour of CSP | Current standard |
| Precedence | Overridden by frame-ancestors | Takes priority |
For maximum compatibility, set both:
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
If you need to allow framing from specific origins (e.g., embedding your content on a partner site), use frame-ancestors with explicit origins and keep X-Frame-Options as a fallback for older browsers.
Frequently asked questions
- What are HTTP security headers? +
- HTTP security headers are response headers that web servers send to browsers with instructions about how to handle the page content. They control security behaviours like script execution, framing, protocol enforcement, and feature access -- preventing entire classes of attacks without changes to application code.
- Which security headers does every website need? +
- At minimum, every website should set Content-Security-Policy (prevents XSS), Strict-Transport-Security (enforces HTTPS), X-Content-Type-Options (prevents MIME sniffing), X-Frame-Options or CSP frame-ancestors (prevents clickjacking), and Referrer-Policy (controls information leakage).
- What is Content-Security-Policy? +
- Content-Security-Policy (CSP) is an HTTP header that tells the browser which sources of content (scripts, styles, images, fonts, frames) are permitted on a page. By restricting script sources, CSP prevents cross-site scripting (XSS) attacks even when a vulnerability exists in the application code.
- What is HSTS and why is it important? +
- HTTP Strict Transport Security (HSTS) tells browsers to only connect to your site over HTTPS, even if the user types http:// or follows an HTTP link. This prevents SSL stripping attacks where an attacker downgrades the connection to unencrypted HTTP to intercept traffic.
- How do I check which security headers my site has? +
- You can inspect response headers in browser developer tools (Network tab), use online scanners like SecurityHeaders.com, or use an EASM platform like SurfaceLoop that checks security headers across all your domains and subdomains automatically.
- Do security headers affect SEO? +
- Security headers do not directly affect search rankings, but they prevent attacks that can harm SEO -- such as XSS-based content injection, clickjacking that damages user trust, and HTTP downgrade attacks that trigger browser warnings. HSTS in particular signals to search engines that the site is HTTPS-only.
Get SurfaceLoop security briefings
No spam, just findings that matter. Fortnightly.