Security Headers Explained: CSP, HSTS, X-Frame-Options https://vulnify.app/blog/security-headers-explained-csp-hsts-x-frame-options Learn how CSP, HSTS, and X-Frame-Options protect your site from XSS, downgrade attacks, and clickjacking. Use a security headers checker to spot missing or weak headers and apply practical fixes fast. Security headers are HTTP response headers that harden browsers against common attacks like XSS, clickjacking, downgrade attacks, and data leakage. They are quick wins that reduce risk without changing application code in many cases. Table of Contents Quick checklist Content-Security-Policy (CSP) Strict-Transport-Security (HSTS) X-Frame-Options (and frame-ancestors) Other high-value security headers Common mistakes and how to avoid them Security headers checker: what it does and what to look for Practical fixes (by platform) FAQs Quick checklist Content-Security-Policy : present, specific, minimal allowlists, no unnecessary unsafe-inline or unsafe-eval Strict-Transport-Security : enabled on HTTPS, long max-age, includeSubDomains if safe, preload if ready X-Frame-Options : set to DENY or SAMEORIGIN (or use CSP frame-ancestors) Referrer-Policy : limits referrer leakage Permissions-Policy : disables unused browser features Cross-Origin-Opener-Policy / Cross-Origin-Resource-Policy : reduces cross-origin data exposure Cache-Control (for sensitive pages): prevents storing private content Content-Security-Policy (CSP) What it protects against: CSP reduces the impact of cross-site scripting (XSS), malicious third-party scripts, and unsafe content loading by restricting where resources can be loaded from. How it works: You define a policy that browsers enforce. The policy describes allowed sources for scripts, styles, images, fonts, frames, and more. Good starting policy (example): Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests Key directives to understand: default-src : fallback for other resource types script-src : controls JavaScript sources (most important for XSS) style-src : controls CSS sources img-src , font-src , connect-src : images, fonts, and XHR/WebSocket destinations object-src : set to 'none' to block plugins base-uri : prevents hostile base URL injection frame-ancestors : clickjacking defense (preferred over X-Frame-Options when available) upgrade-insecure-requests : upgrades HTTP subresources to HTTPS Common CSP pitfalls: unsafe-inline in script-src : weakens XSS defenses. Prefer nonces or hashes. unsafe-eval : enables dangerous JS evaluation patterns. Overly broad allowlists like * or entire CDNs without need. Breaking the site by enforcing too early. Use Content-Security-Policy-Report-Only first to collect violations. Report-only mode (recommended rollout): Content-Security-Policy-Report-Only: default-src 'self'; ...; report-to csp-endpoint Strict-Transport-Security (HSTS) What it protects against: HSTS blocks protocol downgrade attacks and cookie hijacking by forcing browsers to use HTTPS for your domain after the first secure visit. How it works: When a browser sees HSTS, it caches the rule and refuses to use HTTP for that domain until the max-age expires. Recommended header (example): Strict-Transport-Security: max-age=31536000; includeSubDomains Preload option (only if you meet requirements): Strict-Transport-Security: max-age=31536000; includeSubDomains; preload HSTS deployment warnings: Do not enable includeSubDomains unless every subdomain is on HTTPS. Be careful with long max-age on environments that are not ready, like legacy subdomains or staging. Preload is hard to roll back. Only do it when you are confident. X-Frame-Options (and frame-ancestors) What it protects against: clickjacking, where an attacker frames your site and tricks users into clicking UI elements. X-Frame-Options values: DENY : no framing allowed anywhere SAMEORIGIN : allow framing only on the same origin Recommended modern approach: Prefer CSP frame-ancestors because it is more flexible and widely supported in modern browsers. Examples: X-Frame-Options: DENY Content-Security-Policy: frame-ancestors 'none' Content-Security-Policy: frame-ancestors 'self' https://trusted.example Note: If you set both X-Frame-Options and CSP frame-ancestors, ensure they do not conflict. Other high-value security headers Referrer-Policy : strict-origin-when-cross-origin (good default) Permissions-Policy : disable unused features (example: geolocation=(), microphone=(), camera=() ) X-Content-Type-Options : nosniff to prevent MIME sniffing Cross-Origin-Opener-Policy (COOP) : same-origin to isolate browsing context Cross-Origin-Resource-Policy (CORP) : same-origin or same-site depending on needs Cross-Origin-Embedder-Policy (COEP) : powerful isolation, but can break third-party embeds Cache-Control (for sensitive pages): no-store where appropriate About X-XSS-Protection: This is legacy and not recommended as a security control for modern browsers. Common mistakes and how to avoid them CSP is present but ineffective : policies that allow everything (wildcards, unsafe-inline) do not help much. HSTS on some pages only : HSTS must be sent consistently on HTTPS responses for the host. Conflicting clickjacking controls : X-Frame-Options says DENY but CSP frame-ancestors allows framing, or vice versa. Setting headers on the app but missing the CDN or reverse proxy : ensure the edge layer is not stripping or overriding headers. Applying strict policies to every path : some pages need different rules (for example, an embedded widget). Use per-route config where needed. Security headers checker: what it does and what to look for A security headers checker fetches your site and analyzes response headers to identify missing protections, weak configurations, and inconsistencies across endpoints. What a good checker should verify: Presence and correctness of core headers: CSP, HSTS, X-Frame-Options (or frame-ancestors), Referrer-Policy, Permissions-Policy, X-Content-Type-Options Strength checks, not just presence (for example, CSP without unsafe-inline, HSTS max-age length) Redirect chain behavior (HTTP to HTTPS, and whether headers appear on final response) Consistency across key paths (/, login, admin, API endpoints) Edge layer overrides (CDN, WAF, load balancer) that add or remove headers What you should do with results: Treat missing as priority fixes, especially HSTS, clickjacking protections, and nosniff. Treat weak as hardening tasks, especially CSP and Permissions-Policy. Re-test multiple endpoints after changes to confirm the header is actually served. Practical fixes (by platform) Nginx : add headers at the server or location block, and use the always flag where appropriate so they apply to error responses too. Apache : set headers in VirtualHost or .htaccess (prefer server config for consistency). CDN / Reverse proxy : apply at the edge for uniform coverage, but ensure application-level headers do not conflict. Framework middleware : good for route-specific CSP, but verify the final response after proxies. Rule of thumb: Set broad, safe defaults at the edge, then tighten CSP per app route if needed. FAQs Should I use both X-Frame-Options and CSP frame-ancestors? Yes, you can, but keep them aligned. If you already use frame-ancestors, X-Frame-Options becomes a compatibility layer for older clients. Will CSP break my site? It can if deployed in enforce mode without testing. Start with Report-Only, fix violations, then enforce. Is HSTS safe to enable immediately? Only if all content and subdomains you plan to include are available over HTTPS. Start without includeSubDomains if you are unsure. Do security headers replace secure coding? No. They reduce browser-side attack surface and impact, but they do not eliminate server-side vulnerabilities.