Content-Security-Policy (CSP) is the most powerful and complex HTTP security header, providing fine-grained control over which resources a browser is permitted to load when rendering a web page. A properly configured CSP can prevent Cross-Site Scripting (XSS) attacks entirely by blocking the execution of injected scripts, one of the most common and impactful web vulnerabilities. The header works by defining a policy consisting of directives, each specifying which sources are trusted for a particular type of resource: scripts, stylesheets, images, fonts, frames, connections, and others.
Key CSP directives include default-src as a fallback for unspecified resource types, script-src for JavaScript, style-src for CSS, img-src for images, connect-src for XHR and fetch requests, font-src for fonts, frame-src for iframes, and object-src for plugins. Source values can be 'none' (block entirely), 'self' (same origin only), specific domain names, or nonce-based values like 'nonce-abc123' that allow specific inline scripts without enabling all inline execution. Avoid 'unsafe-inline' and 'unsafe-eval' as they defeat the XSS protection CSP provides. The report-uri directive sends violation reports to a specified endpoint for monitoring.
default-src 'self'— restrict all unspecified resources to same-origin by defaultscript-src 'self' 'nonce-{random}'— allow only same-origin scripts and inline scripts with a matching nonceobject-src 'none'— block all plugin content including Flash and ActiveXupgrade-insecure-requests— automatically upgrade HTTP resource loads to HTTPSreport-uri /csp-report— send CSP violation reports to your endpoint for monitoring
The HTTP Strict Transport Security (HSTS) header instructs browsers to connect to your website exclusively over HTTPS for a specified duration, preventing SSL stripping attacks where a network-level attacker downgrades a connection from HTTPS to HTTP. Once a browser receives the HSTS header, it will automatically convert any HTTP requests to HTTPS for the specified max-age period, even before sending the initial request — eliminating the window of vulnerability that exists on the very first HTTP connection to a site.
A production HSTS header suitable for the HSTS preload list requires: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload. The max-age=31536000 (one year) is the minimum required for preload submission. The includeSubDomains directive applies HSTS to all subdomains of your domain — only add this after confirming that all subdomains serve HTTPS, as any HTTP-only subdomain will become inaccessible after HSTS is enforced. The preload directive signals consent to be included in browser HSTS preload lists, which hardcode your domain as HTTPS-only in browser distributions, providing protection even on the very first visit before an HSTS response is received.
- Minimum viable header:
Strict-Transport-Security: max-age=31536000 - Add
includeSubDomainsonly after confirming all subdomains serve valid HTTPS - Add
preloadand submit to hstspreload.org to be hardcoded in Chrome, Firefox, and Edge - HSTS preloading is irreversible in the short term — removal from the list takes months to propagate
- Test with a short
max-agefirst (e.g.,max-age=300) before deploying a long-lived value
The X-Content-Type-Options: nosniff header prevents browsers from performing MIME type sniffing — a behavior where browsers attempt to guess the content type of a resource by examining its content rather than trusting the declared Content-Type header. MIME sniffing creates a vulnerability where an attacker can upload a file that the server declares as a harmless type (such as text/plain) but which a browser's sniffing algorithm identifies as executable JavaScript, leading to script execution in the context of the hosting origin. The nosniff directive forces browsers to honor the declared content type exactly.
Clickjacking attacks embed a target site invisibly in an attacker-controlled page's iframe and trick users into clicking on hidden interface elements. The X-Frame-Options header provides protection: the value DENY prevents the page from being embedded in any frame, and SAMEORIGIN allows framing only by pages on the same origin. Note that the ALLOW-FROM value is deprecated and not supported in modern browsers. The modern replacement is the frame-ancestors CSP directive, which provides equivalent functionality with broader browser support and more flexible configuration options. Using both X-Frame-Options: SAMEORIGIN and Content-Security-Policy: frame-ancestors 'self' provides defense-in-depth.
X-Content-Type-Options: nosniff— prevents MIME sniffing attacks on uploaded contentX-Frame-Options: DENY— prevents all framing of the page in any other site's iframeX-Frame-Options: SAMEORIGIN— permits framing only from the same originContent-Security-Policy: frame-ancestors 'none'— modern CSP-based alternative to X-Frame-Options- Use both
X-Frame-Optionsand CSPframe-ancestorsfor defense-in-depth coverage
The Referrer-Policy header controls how much URL information is included in the Referer header when a user navigates from your site to another. By default, browsers include the full URL of the referring page, which can leak sensitive information if the URL contains authentication tokens, user IDs, or search query parameters. The recommended value for most sites is strict-origin-when-cross-origin: for same-origin navigations, the full URL is sent; for cross-origin navigations, only the origin is sent; for downgrade from HTTPS to HTTP, no referrer is sent at all. The no-referrer value suppresses the Referer header entirely for maximum privacy.
The Permissions-Policy header (formerly Feature-Policy) restricts which browser APIs and features your web application is permitted to use and controls whether embedded third-party content in iframes can access these features. Restricting powerful APIs to only the legitimate use cases reduces your attack surface. Common restrictions include camera=() to disable camera access on pages that do not need it, microphone=() to disable microphone, geolocation=() to block location access, and payment=() to disable the Payment Request API. An example header: Permissions-Policy: camera=(), microphone=(), geolocation=(self), payment=().
Referrer-Policy: strict-origin-when-cross-origin— recommended balance of functionality and privacyReferrer-Policy: no-referrer— maximum privacy, suppresses Referer header entirelyPermissions-Policy: camera=()— disable camera access for this page and all embedded contentPermissions-Policy: geolocation=(self)— allow geolocation only from same-origin code- Apply
Permissions-Policyto all pages, especially those embedding third-party scripts or iframes
In Nginx, security headers are added using the add_header directive in the server or location block. A comprehensive set of headers in an Nginx configuration includes: add_header X-Content-Type-Options "nosniff" always;, add_header X-Frame-Options "SAMEORIGIN" always;, add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;, and add_header Referrer-Policy "strict-origin-when-cross-origin" always;. The always flag ensures the header is sent on all response codes including errors, not just 200 OK responses. Place these directives in a dedicated snippet file and include it in every server block.
In Apache, use the Header always set directive inside a VirtualHost or Directory block after enabling the mod_headers module with a2enmod headers. In Next.js, security headers are configured in next.config.js using the headers() async function that returns an array of path-specific header configurations. The Next.js documentation provides a comprehensive security headers example that can be adapted directly. Content-Security-Policy requires the most careful configuration regardless of platform, as an overly restrictive CSP will break legitimate functionality while an overly permissive one provides little protection.
- Nginx:
add_header X-Content-Type-Options "nosniff" always;— thealwaysflag is critical - Apache: enable
mod_headersthen useHeader always set X-Frame-Options "SAMEORIGIN" - Next.js: configure in
next.config.jsusing theheaders()async function - Test CSP in report-only mode first:
Content-Security-Policy-Report-Onlyheader before enforcing - Never apply security headers only to the root path — they must apply to all application paths
SecurityHeaders.com, created by Scott Helme, provides instant security header analysis by scanning any publicly accessible URL and returning a graded report. The grading system assigns letter grades from A+ down to F based on which headers are present, their values, and their strength. The tool specifically checks for the presence and correct configuration of CSP, HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, and Permissions-Policy. The report highlights which headers are missing and provides guidance on recommended values, making it an effective starting point for improving a site's header security posture.
Mozilla Observatory at observatory.mozilla.org performs similar analysis with additional checks including cookie security flags, subdomain security, and redirect behavior. Both tools are valuable but do not evaluate the semantic correctness of your CSP — a CSP that contains 'unsafe-inline' may still receive a passing grade for header presence even though the XSS protection is largely negated. Use the CSP Evaluator tool from Google at csp-evaluator.withgoogle.com for detailed analysis of CSP quality. For command-line verification, curl -I https://example.com displays all response headers including security headers in their raw form.
- SecurityHeaders.com: instant graded security header analysis with specific improvement recommendations
- Mozilla Observatory: additional checks including cookies, redirects, and subdomain security
- Google CSP Evaluator: dedicated analysis of Content-Security-Policy strength and bypass risks
curl -I https://example.com— view raw response headers from the command line- Automate header checks in your CI/CD pipeline to prevent security regressions during deployments