{"id":3361,"date":"2026-06-25T10:15:00","date_gmt":"2026-06-25T10:15:00","guid":{"rendered":"https:\/\/getdarkscout.com\/blog\/?p=3361"},"modified":"2026-06-25T07:10:29","modified_gmt":"2026-06-25T07:10:29","slug":"http-security-headers-checklist","status":"publish","type":"post","link":"https:\/\/getdarkscout.com\/blog\/http-security-headers-checklist\/","title":{"rendered":"HTTP Security Headers Checklist: Every Header You Need in 2026"},"content":{"rendered":"\n<p>Fewer than 10% of websites have all four critical security headers correctly configured.<\/p>\n\n\n\n<p>Not missing. Not slightly wrong. Completely absent.<\/p>\n\n\n\n<p>Security headers are free to implement. They require no code changes to your application logic. They protect against attacks that have been documented for over two decades. And the vast majority of production websites still don&#8217;t have them.<\/p>\n\n\n\n<p>This checklist covers every HTTP security header that matters in 2026: what each one protects against, what the correct configuration looks like, which ones are quick to implement and which require careful tuning, and which deprecated headers you should actively remove because they can make security worse.<\/p>\n\n\n\n<p>Work through it section by section. By the end of this HTTP Security Headers Checklist, you&#8217;ll have a complete picture of your current configuration and exactly what needs to change.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"what-are-http-security-headers-and-why-do-they-matter\"><\/span>What Are HTTP Security Headers and Why Do They Matter?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img fetchpriority=\"high\" decoding=\"async\" width=\"850\" height=\"494\" src=\"https:\/\/getdarkscout.com\/blog\/wp-content\/uploads\/2026\/06\/HTTP-Security-Headers.webp\" alt=\"\" class=\"wp-image-3362\" srcset=\"https:\/\/getdarkscout.com\/blog\/wp-content\/uploads\/2026\/06\/HTTP-Security-Headers.webp 850w, https:\/\/getdarkscout.com\/blog\/wp-content\/uploads\/2026\/06\/HTTP-Security-Headers-300x174.webp 300w, https:\/\/getdarkscout.com\/blog\/wp-content\/uploads\/2026\/06\/HTTP-Security-Headers-768x446.webp 768w\" sizes=\"(max-width: 850px) 100vw, 850px\" \/><\/figure>\n\n\n\n<p>A security header for HTTP is a piece of information that is sent as part of the response headers that your web server sends back with any page you request. It gives an instruction to the browser on how to treat your content.<\/p>\n\n\n\n<p>Without them, browsers use their most permissive defaults. That means they&#8217;ll load scripts from any source, allow your pages to be embedded in iframes on other sites, accept any content type regardless of what you intended to serve, and connect over HTTP even if you&#8217;ve configured HTTPS.<\/p>\n\n\n\n<p>Security headers change those defaults. They tell the browser to only load scripts from sources you&#8217;ve explicitly approved, refuse to render your pages inside iframes, enforce HTTPS even if a user types HTTP, and reject content that doesn&#8217;t match its declared type.<\/p>\n\n\n\n<p>The attacks each header prevents aren&#8217;t theoretical. <a href=\"https:\/\/portswigger.net\/web-security\/cross-site-scripting\" target=\"_blank\" rel=\"noopener\">Cross-site scripting (XSS)<\/a> remains one of the most common web vulnerabilities. Clickjacking attacks steal credentials through invisible iframe overlays. Protocol downgrade attacks strip HTTPS from connections on shared networks. MIME-sniffing executes uploaded files as scripts. These are documented attack patterns with known defenses, and security headers are those defenses.<\/p>\n\n\n\n<p>What makes the 95% failure rate remarkable is the cost: implementing these headers requires no changes to your application code. It&#8217;s the server configuration. It takes hours for most of them, minutes for several. The protection is immediate. The only explanation for the widespread absence is that developers deprioritize it until an audit or a breach forces attention.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"how-to-check-your-current-headers\"><\/span>How to Check Your Current Headers<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Before working through the checklist, run a baseline audit to see where you currently stand.<\/p>\n\n\n\n<p><strong>Mozilla Observatory<\/strong> <code>observatory.mozilla.org<\/code> Enter your domain and get a letter grade with specific findings for each header. Grade A or higher is the target. Takes under 30 seconds.<\/p>\n\n\n\n<p><strong>securityheaders.com<\/strong> <code>securityheaders.com<\/code> Focused specifically on security headers. Shows which are present, which are missing, and flags common misconfiguration issues. Quick and readable output.<\/p>\n\n\n\n<p><strong>curl from the command line<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -I https:\/\/yourdomain.com<\/code><\/pre>\n\n\n\n<p>Returns all response headers directly. Handy to check quickly without the browser or an external tool. Notice the security headers there.<\/p>\n\n\n\n<p>Open developer tools in the browser (F12) and select Network. Reload the page and then, in a pop-up window, select the first request and then the Headers tab. Response Headers section will show all headers your server sends..<\/p>\n\n\n\n<p>For a broader website security scan covering headers alongside other vulnerabilities, <a href=\"https:\/\/getdarkscout.com\/services\/scan-website\/\">website scanner<\/a> tools provide a more comprehensive audit in a single pass.<\/p>\n\n\n\n<p>Run the audit first. Note which headers are missing and which are present but misconfigured. Then work through the priority sections below.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"priority-1-implement-today-quick-wins\"><\/span>Priority 1: Implement Today (Quick Wins)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>These headers are one or two lines of server configuration each. They protect against specific, well-documented attacks and have virtually no risk of breaking your site. Implement all of these before anything else.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"> X-Content-Type-Options<\/h3>\n\n\n\n<p>What it defends from:<a href=\"https:\/\/coalfire.com\/the-coalfire-blog\/mime-sniffing-in-browsers-and-the-security\" target=\"_blank\" rel=\"noopener\"> MIME-sniffing attacks<\/a>. If you do not set this header, a browser can possibly recognize an uploaded text file as a JavaScript file if the file contents look executable, no matter what the file&#8217;s original MIME type was. It is one of the defenses for a specific attack, where an attacker manages to upload a malicious file that later gets executed as a script.<\/p>\n\n\n\n<p><strong>Correct configuration:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>X-Content-Type-Options: nosniff<\/code><\/pre>\n\n\n\n<p>That&#8217;s it. There&#8217;s one value, and it&#8217;s always <code>nosniff<\/code>. No configuration decisions required. This is a single line that closes an entire category of upload-based attacks. It should be on every website without exception.<\/p>\n\n\n\n<p><strong>Risk of implementation:<\/strong> Zero. This header tells the browser to respect the MIME type you declare. It doesn&#8217;t change behavior for correctly served content.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">X-Frame-Options<\/h3>\n\n\n\n<p>What it blocks: clickjacking. A hacker makes a malicious page, loads your site into an imperceptible iframe that is layered over the attacker\u2019s page and then tricks visitors into clicking buttons on that page, unknowingly completing actions on your website such as authorizing a payment or setting a new account password.<\/p>\n\n\n\n<p><strong>Correct configuration:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>X-Frame-Options: DENY<\/code><\/pre>\n\n\n\n<p>Use <code>DENY<\/code> if your site should never be embedded in a frame anywhere. Use <code>SAMEORIGIN<\/code> it if you need your own pages to embed other pages from your own domain.<\/p>\n\n\n\n<p><strong>Note on CSP frame-ancestors:<\/strong> The Content Security Policy <code>frame-ancestors<\/code> Directive is the modern replacement for X-Frame-Options and takes precedence in browsers that support CSP. However, X-Frame-Options provides backward compatibility for older browsers. Set both.<\/p>\n\n\n\n<p><strong>Risk of implementation:<\/strong> Low. Only affects whether your pages can be embedded in frames. If you don&#8217;t embed your own pages in iframes across different domains, <code>DENY<\/code> is safe.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Referrer-Policy<\/h3>\n\n\n\n<p>What it protects against: Leakage of information through the Referer header. If your visitors access your site, then click out to another site, the browser will include the URL they were at before in the Referer header. This can include session ID, userid, search terms, or any number of other things you don&#8217;t want spilling over to third parties in the query parameters.<\/p>\n\n\n\n<p><strong>Correct configuration:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Referrer-Policy: strict-origin-when-cross-origin<\/code><\/pre>\n\n\n\n<p>Tweets the full URL for same-origin requests (which can be useful for tracking on your own analytics solutions), only the origin (ie. Https:\/\/yoursite.com) for cross-origin (HTTPS) requests and nothing for cross-origin (HTTP).<\/p>\n\n\n\n<p><strong>Risk of implementation:<\/strong> Low to none for most sites. Your internal analytics still works. External third parties lose the path and query string details they didn&#8217;t need anyway.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">X-DNS-Prefetch-Control<\/h3>\n\n\n\n<p><strong>What it protects against:<\/strong> Unintended DNS lookups. Browsers prefetch DNS for links on a page to speed up future navigation. This can leak browsing behavior information to DNS servers for links your users haven&#8217;t actually clicked.<\/p>\n\n\n\n<p><strong>Correct configuration:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>X-DNS-Prefetch-Control: off<\/code><\/pre>\n\n\n\n<p>In most sites where we are more interested in performance than privacy, one is fine. But if we are serious about user privacy, the browsing behavior should be off.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"priority-2-implement-this-week-moderate-effort\"><\/span>Priority 2: Implement This Week (Moderate Effort)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>These headings will require you to put a little more thought into your exact setup, but they are very simple to get right.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">HTTP Strict Transport Security (HSTS)<\/h3>\n\n\n\n<p>What it defends you from: protocol downgrade attacks. Without HSTS, someone on a communal network, whether you are using public Wi-Fi, a hotel network, or a coffee shop, can run tools like sslstrip, which will see the very first request in your browser, the request for your domain, and could remove the redirect to a secure https:\/\/ website and deliver the resulting page unencrypted.<\/p>\n\n\n\n<p>With HSTS, once a browser has seen your HSTS header, it enforces HTTPS internally for the specified duration. The HTTP request never leaves the browser. The window for SSL stripping disappears.<\/p>\n\n\n\n<p><strong>Correct configuration:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Strict-Transport-Security: max-age=63072000; includeSubDomains; preload<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>max-age=63072000<\/code> is two years in seconds. OWASP recommends a minimum of one year.<\/li>\n\n\n\n<li><code>includeSubDomains<\/code> extends the policy to all subdomains.<\/li>\n\n\n\n<li><code>preload<\/code> allows your domain to be submitted to browser preload lists for protection on the very first visit.<\/li>\n<\/ul>\n\n\n\n<p><strong>Important warnings before implementing:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Your site must be fully functional over HTTPS before enabling HSTS. Once a browser caches an HSTS policy, it will refuse HTTP connections for the entire <code>max-age<\/code> period, even if you remove the header later.<\/li>\n\n\n\n<li><code>includeSubDomains<\/code> means every subdomain of your domain must serve HTTPS. If any subdomain only has HTTP, those subdomains will break for users who have cached your HSTS policy.<\/li>\n\n\n\n<li>Only add <code>preload<\/code> If every subdomain supports HTTPS, once in the preload list, removal takes weeks. The full implications are covered in the <a href=\"https:\/\/getdarkscout.com\/blog\/ssl-tls-website-security-check\/\">SSL\/TLS website security check<\/a> guide.<\/li>\n<\/ol>\n\n\n\n<p>Ramp-up approach: begin with max-age=300 (5 min, for test). Ensure that your website actually functions correctly for all users. Set max-age=86400 (1 day). Wait a week to make sure there are no problems, and then increase to max-age=63072000.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Permissions-Policy<\/h3>\n\n\n\n<p>What it guards against: This prevents rogue pages or third-party scripts that you include from accessing sensitive browser APIs. Without this header, any script loaded into your page can access the camera, microphone, geolocation, payment APIs, and a dozen other sensitive parts of your browser.<\/p>\n\n\n\n<p><strong>Correct configuration (restrictive baseline):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(self), usb=(), fullscreen=(self)<\/code><\/pre>\n\n\n\n<p>The <code>()<\/code> syntax disables the feature entirely. <code>(self)<\/code> allows it for your own origin only. List only the features your site actively uses. Everything unlisted defaults to off in modern implementations.<\/p>\n\n\n\n<p><strong>Risk of implementation:<\/strong> Medium. If your site legitimately uses these features (a mapping site needs geolocation, a video conferencing app needs camera and microphone), you need to allowlist the appropriate origins. Test thoroughly before deploying.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cross-Origin-Resource-Policy (CORP)<\/h3>\n\n\n\n<p>What it defends you from: Cross-origin information hijacking. Without CORP, other sites can embed your resources (images, data, scripts) on their pages via an img or script tags. This allows them to run side-channel attacks where they make your user load through an attacker&#8217;s page to learn information about them.<\/p>\n\n\n\n<p><strong>Correct configuration for most sites:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cross-Origin-Resource-Policy: same-origin<\/code><\/pre>\n\n\n\n<p>This will stop your resources from being loaded by other origins. Use same-site if you want resources served by your domain, but accessible sub-domain wide and cross-origin only if you want resources we serve to anyone.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"priority-3-implement-carefully-complex-headers\"><\/span>Priority 3: Implement Carefully (Complex Headers)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>These headers provide powerful protection but require careful configuration. Mistakes can break legitimate site functionality. Read the guidance fully before implementing.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Content Security Policy (CSP)<\/h3>\n\n\n\n<p><strong>What it protects against:<\/strong> Cross-site scripting (XSS) attacks. CSP defines an explicit allowlist of sources from which scripts, styles, images, fonts, and other resources can load. Any resource not on the list is blocked by the browser. A properly configured CSP prevents injected malicious scripts from executing even when an XSS vulnerability exists in your application.<\/p>\n\n\n\n<p>CSP is the most powerful security header available, and when correctly configured, it can prevent most XSS exploitation even when vulnerabilities exist in the application.<\/p>\n\n\n\n<p>CSP is also the most complex to configure correctly. This is why it&#8217;s Priority 3 despite its importance. A misconfigured CSP can break your site, block legitimate content, or provide much weaker protection than intended.<\/p>\n\n\n\n<p><strong>Starting configuration:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'<\/code><\/pre>\n\n\n\n<p>Note <code>Content-Security-Policy-Report-Only<\/code>: This logs violations without blocking anything. Use this in production first to see what your policy would block, tune it until violations stop, then switch to enforcing <code>Content-Security-Policy<\/code>.<\/p>\n\n\n\n<p><strong>Critical directives explained:<\/strong><\/p>\n\n\n\n<p><code>default-src 'self'<\/code> is the fallback for any directive not explicitly listed. It restricts all content to your own origin by default.<\/p>\n\n\n\n<p><code>script-src 'self'<\/code> restricts JavaScript to your own domain. If you use inline scripts (<code>&lt;script&gt;<\/code> tags in your HTML), this will break them. Inline scripts require either <code>'unsafe-inline'<\/code> (which weakens your CSP significantly) or nonce-based or hash-based approaches that allow specific inline scripts explicitly.<\/p>\n\n\n\n<p><code>frame-ancestors 'none'<\/code> is the modern replacement for <code>X-Frame-Options: DENY<\/code>. Set both for compatibility.<\/p>\n\n\n\n<p><code>upgrade-insecure-requests<\/code> automatically upgrades HTTP resource requests to HTTPS, addressing mixed content issues without manually updating every URL. Detailed guidance on <a href=\"https:\/\/getdarkscout.com\/blog\/what-is-mixed-content-and-how-do-you-fix-it\/\">finding and fixing mixed content<\/a> covers the full mixed content remediation process.<\/p>\n\n\n\n<p><code>connect-src<\/code> controls which URLs your JavaScript can connect to via <code>fetch<\/code>, <code>XMLHttpRequest<\/code>, and WebSocket. If your site makes API calls to external services, those origins need to be listed here.<\/p>\n\n\n\n<p><strong>The biggest CSP mistake:<\/strong> Setting <code>default-src *<\/code> or adding <code>'unsafe-inline'<\/code> broadly to stop violations. These configurations are effectively no CSP at all. They&#8217;re worse than no header because they suggest protection exists when it doesn&#8217;t.<\/p>\n\n\n\n<p><strong>Practical approach:<\/strong> Implement in report-only mode for a week. Review violations in your browser console or via a CSP reporting endpoint. Add legitimate sources to the allowlist. Switch to enforcement once violations stop.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cross-Origin-Opener-Policy (COOP)<\/h3>\n\n\n\n<p>What it blocks: Side-channel attacks like Spectre that let a compromised page in one tab of your browser perform timing attacks against your memory. COOP prevents other origin pages from sharing the same process in the first place.<\/p>\n\n\n\n<p><strong>Correct configuration:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cross-Origin-Opener-Policy: same-origin<\/code><\/pre>\n\n\n\n<p>Note: COOP depends on COEP, which is also needed for SharedArrayBuffer and high-resolution timers. By default, these are disabled in most browsers as a Spectre mitigation. Even if you do not use those APIs, publishing COOP gives you isolation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cross-Origin-Embedder-Policy (COEP)<\/h3>\n\n\n\n<p>What it addresses: Cross-origin resource leakage in a common process. Coupled with COOP, it is necessary to prevent cross-origin sharing.<\/p>\n\n\n\n<p><strong>Correct configuration:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cross-Origin-Embedder-Policy: require-corp<\/code><\/pre>\n\n\n\n<p>Important: require-corp will result in any cross-origin resource your page loads having to present a Cross-Origin-Resource-Policy header, which permits the embed. This may break external images, fonts, and headers to remove: The Deprecated List {#deprecated}<\/p>\n\n\n\n<p>This section doesn&#8217;t get covered by most guides. It&#8217;s as important as adding correct headers.<\/p>\n\n\n\n<p>Some security headers were once recommended but are now deprecated, broken, or actively harmful. Leaving them in your configuration signals outdated security practices and in one specific case can actually decrease security.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"headers-to-remove-the-deprecated-list\"><\/span>Headers to Remove: The Deprecated List<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>This section doesn&#8217;t get covered by most guides. It&#8217;s as important as adding correct headers.<\/p>\n\n\n\n<p>Some security headers were once recommended but are now deprecated, broken, or actively harmful. Leaving them in your configuration signals outdated security practices and, in one specific case, can actually decrease security.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">X-XSS-Protection: 1; mode=block<\/h3>\n\n\n\n<p>Remove this immediately.<\/p>\n\n\n\n<p>The XSS Auditor, which controlled this header, was a browser-built-in feature that attempted to detect and block reflected XSS attacks. It was removed from Chrome in version 78 (2019) and from most other browsers shortly after, specifically because it introduced new security vulnerabilities. The filter could be abused to selectively block legitimate scripts, enabling a different class of attacks.<\/p>\n\n\n\n<p>Setting X-XSS-Protection: 1; mode=block on a modern browser has no effect on the browser&#8217;s XSS protection but may produce confusing behavior in some edge cases. Setting X-XSS-Protection: 0 explicitly disables the legacy filter to prevent any residual interaction.<\/p>\n\n\n\n<p>If you see this header in a security audit recommendation, the recommendation is outdated. Replace it with a properly configured CSP, which provides genuine XSS protection.<\/p>\n\n\n\n<p>This is one of the most common website security mistakes developers inherit from old configuration templates without questioning.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Expect-CT<\/h3>\n\n\n\n<p>Remove this header from your configuration.<\/p>\n\n\n\n<p>The Expect-CT header was used to opt in to Certificate Transparency (CT) requirements, allowing sites to specify a report-only or enforcement mode for CT compliance. It was formally deprecated by the IETF in RFC 9163 in 2022 because CT compliance became mandatory across all certificates issued by trusted CAs. The header is now redundant, and both Mozilla and Chrome have deprecated support for it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Public-Key-Pins (HPKP)<\/h3>\n\n\n\n<p>If you still have this configured, remove it.<\/p>\n\n\n\n<p>HPKP allowed sites to specify which public keys browsers should accept for HTTPS connections. It was intended to prevent certificate misissuance from being silently trusted. In practice, it was dangerous: a misconfiguration or a lost key could make your site permanently inaccessible to users who had cached the pin. Chrome removed support in Chrome 72. Firefox removed it in Firefox 72. It provides no protection on modern browsers and the risk of catastrophic misconfiguration was severe.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"implementation-copy-ready-config-for-apache-and-nginx\"><\/span>Implementation: Copy-Ready Config for Apache and Nginx <span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Apache (.htaccess or virtual host configuration):<\/strong><\/h3>\n\n\n\n<p>apache<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;IfModule mod_headers.c&gt;\n    # Priority 1: Quick wins\n    Header always set X-Content-Type-Options \"nosniff\"\n    Header always set X-Frame-Options \"DENY\"\n    Header always set Referrer-Policy \"strict-origin-when-cross-origin\"\n    Header always set X-DNS-Prefetch-Control \"off\"\n    \n    # Priority 2: Moderate effort\n    Header always set Strict-Transport-Security \"max-age=63072000; includeSubDomains; preload\"\n    Header always set Permissions-Policy \"camera=(), microphone=(), geolocation=(), payment=(self)\"\n    Header always set Cross-Origin-Resource-Policy \"same-origin\"\n    \n    # Priority 3: After tuning\n    Header always set Content-Security-Policy \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests\"\n    Header always set Cross-Origin-Opener-Policy \"same-origin\"\n    Header always set Cross-Origin-Embedder-Policy \"require-corp\"\n    \n    # Remove deprecated headers\n    Header unset X-XSS-Protection\n    Header unset Expect-CT\n    Header unset Public-Key-Pins\n&lt;\/IfModule&gt;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Nginx (server or location block):<\/strong><\/h3>\n\n\n\n<p>nginx<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Priority 1: Quick wins\nadd_header X-Content-Type-Options \"nosniff\" always;\nadd_header X-Frame-Options \"DENY\" always;\nadd_header Referrer-Policy \"strict-origin-when-cross-origin\" always;\nadd_header X-DNS-Prefetch-Control \"off\" always;\n\n# Priority 2: Moderate effort\nadd_header Strict-Transport-Security \"max-age=63072000; includeSubDomains; preload\" always;\nadd_header Permissions-Policy \"camera=(), microphone=(), geolocation=(), payment=(self)\" always;\nadd_header Cross-Origin-Resource-Policy \"same-origin\" always;\n\n# Priority 3: After tuning\nadd_header Content-Security-Policy \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests\" always;\nadd_header Cross-Origin-Opener-Policy \"same-origin\" always;\nadd_header Cross-Origin-Embedder-Policy \"require-corp\" always;\n\n# Remove deprecated headers\nmore_clear_headers X-XSS-Protection;\nmore_clear_headers Expect-CT;\nmore_clear_headers Public-Key-Pins;<\/code><\/pre>\n\n\n\n<p><strong>Note for Nginx:<\/strong> Removing headers in Nginx requires the <code>ngx_http_headers_more_filter_module<\/code>. If this module isn&#8217;t available, omit those lines since unset deprecated headers cause no harm. The security risk is setting them incorrectly, not leaving them absent.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"cookie-security-the-often-forgotten-extension\"><\/span>Cookie Security: The Often-Forgotten Extension<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Security headers protect the response your server sends. Cookie attributes protect the session tokens that the browser stores and sends back. Both matter, and they work together.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Secure flag<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Set-Cookie: session=token; Secure<\/code><\/pre>\n\n\n\n<p>Prevents the browser from sending the cookie over HTTP connections. Only sent over HTTPS. Should be on every cookie containing session tokens.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>HttpOnly flag<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Set-Cookie: session=token; HttpOnly<\/code><\/pre>\n\n\n\n<p>Secures the cookie from being accessible to JavaScript through document.cookie. This is mighty useful. Imagine you get XSS injected on your page and then a java script attack can be rolled up on that injected code to steal session tokens. Not so easy if the HttpOnly flag is set on your session tokens.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>SameSite attribute<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Set-Cookie: session=token; SameSite=Strict<\/code><\/pre>\n\n\n\n<p>Specifies whether the user agent should send cookies in cross-site requests. SameSite=Strict prevents the browser from sending the cookie in all cross-site browsing contexts (like following a link from another site) except when the user is following a link from your site. This will defeat any attempts to perform a cross-site request forgery (CSRF), with the attack page triggering a fully valid request to your site, while the browser sends along the cookie.<\/p>\n\n\n\n<p>A complete session cookie line combining all three:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Set-Cookie: session=token; Secure; HttpOnly; SameSite=Strict; Path=\/<\/code><\/pre>\n\n\n\n<p>Specifically, these are programmed into your application code (wherever you set cookies). In a framework, see if there&#8217;s middleware or helper functions that do it around always, without regard for the individual response.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"how-to-test-after-implementation\"><\/span>How to Test After Implementation<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>After making changes, verify they&#8217;re working before considering the task complete.<\/p>\n\n\n\n<p>Run securityheaders.com again. Enter your domain and compare the results to your baseline audit. Every header you added should now appear with a green checkmark. Misconfigured values will be flagged specifically.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Check with curl<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -I https:\/\/yourdomain.com<\/code><\/pre>\n\n\n\n<p>Verify each header appears in the output with the correct value.<\/p>\n\n\n\n<p>Test CSP in report-only mode first. If you implemented CSP in enforcement mode, check your browser console immediately after. Any blocked resources appear as console errors with the specific CSP directive that blocked them. Switch to <code>Content-Security-Policy-Report-Only<\/code> if you need to tune without breaking the site.<\/p>\n\n\n\n<p>Check that your HSTS isn&#8217;t causing any issues. Head to your site via HTTP and make sure it redirects to HTTPS. If you used includeSubDomains, then confirm that subdomains still work.<\/p>\n\n\n\n<p>Testing cross-browser COOP, COEP, and some CSP directives can have different effects across browsers. Test in Chrome and Firefox at a minimum after implementing Priority 3 headers.<\/p>\n\n\n\n<p>Check <a href=\"https:\/\/getdarkscout.com\/blog\/how-to-check-if-a-website-is-secure\/\">how to check if a website is secure<\/a>. Running a full website check after implementation confirms that the security posture improvement shows up across every test dimension, not just the headers specifically.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"the-compliance-dimension\"><\/span>The Compliance Dimension<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Security headers are no longer purely a technical recommendation. They show up in compliance frameworks.<\/p>\n\n\n\n<p>In PCI DSS 4.0, we see some direct mention of transport security and access controls via security headers that enforce it. The absence of headers is also emerging as a category in PCI QSA assessments. We see HSTS addressing the encrypted transmission of cardholder data.<\/p>\n\n\n\n<p>HIPAA technical safeguards mandate &#8220;use of technical security measures to guard against unauthorized access to electronic protected health information.&#8221; Security headers fulfill this by blocking credential stealing via XSS and protocol downgrade attacks.<\/p>\n\n\n\n<p>SOC 2 Common Criteria (CC6.1 to CC6.8) mandates logical access controls and protective mechanisms. Security header configuration is a common SOC 2 TypeII audit finding.<\/p>\n\n\n\n<p>GDPR technical measures require appropriate technical security for personal data. Security headers protecting against data theft attacks are relevant to Article 25 (data protection by design) and Article 32 (security of processing).<\/p>\n\n\n\n<p>Missing security headers are increasingly the kind of finding that produces audit failures, not just recommendations. They&#8217;re also the kind of finding that looks embarrassing in a penetration test report precisely because they&#8217;re so easy to fix.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"the-quick-reference-http-security-headers-checklist\"><\/span>The Quick-Reference HTTP Security Headers Checklist<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Use this for rapid auditing. Check each item against your current configuration.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Priority 1: Implement Today<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>X-Content-Type-Options: nosniff<\/code><\/li>\n\n\n\n<li><code>X-Frame-Options: DENY<\/code> (or SAMEORIGIN if you embed your own pages)<\/li>\n\n\n\n<li><code>Referrer-Policy: strict-origin-when-cross-origin<\/code><\/li>\n\n\n\n<li><code>X-DNS-Prefetch-Control: off<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Priority 2: Implement This Week<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>Strict-Transport-Security: max-age=63072000; includeSubDomains; preload<\/code><\/li>\n\n\n\n<li><code>Permissions-Policy<\/code> with only features you actually use<\/li>\n\n\n\n<li><code>Cross-Origin-Resource-Policy: same-origin<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Priority 3: Implement Carefully<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>Content-Security-Policy<\/code> (start in report-only mode, tune, then enforce)<\/li>\n\n\n\n<li><code>Cross-Origin-Opener-Policy: same-origin<\/code><\/li>\n\n\n\n<li><code>Cross-Origin-Embedder-Policy: require-corp<\/code> or <code>credentialless<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Remove These<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>X-XSS-Protection<\/code> removed (or set to 0 if removing entirely isn&#8217;t possible)<\/li>\n\n\n\n<li><code>Expect-CT<\/code> removed<\/li>\n\n\n\n<li><code>Public-Key-Pins<\/code> removed<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Cookie Attributes<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>Secure<\/code> flag on all session cookies<\/li>\n\n\n\n<li><code>HttpOnly<\/code> flag on all session cookies<\/li>\n\n\n\n<li><code>SameSite=Strict<\/code> on session cookies (or <code>Lax<\/code> where Strict breaks legitimate cross-site navigation)<\/li>\n<\/ul>\n\n\n\n<p>For the full website security context, this checklist sits alongside the broader <a href=\"https:\/\/getdarkscout.com\/blog\/website-security-checklist-small-business\/\">website security checklist for small businesses<\/a> which covers every security area beyond headers.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Fewer than 10% of websites have all four critical security headers correctly configured. Not missing. Not slightly wrong. Completely absent. Security headers are free to implement. They require no code changes to your application logic. They protect against attacks that have been documented for over two decades. And the vast majority of production websites still don&#8217;t have them. This checklist covers every HTTP security header that matters in 2026: what each one protects against, what the correct configuration looks like, which ones are quick to implement and which require careful tuning, and which deprecated headers you should actively remove because they can make security worse. Work through it section by section. By the end of this HTTP Security Headers Checklist, you&#8217;ll have a complete picture of your current configuration and exactly what needs to change. What Are HTTP Security Headers and Why Do They Matter? A security header for HTTP is a piece of information that is sent as part of the response headers that your web server sends back with any page you request. It gives an instruction to the browser on how to treat your content. Without them, browsers use their most permissive defaults. That means they&#8217;ll load scripts from any source, allow your pages to be embedded in iframes on other sites, accept any content type regardless of what you intended to serve, and connect over HTTP even if you&#8217;ve configured HTTPS. Security headers change those defaults. They tell the browser to only load scripts from sources you&#8217;ve explicitly approved, refuse to render your pages inside iframes, enforce HTTPS even if a user types HTTP, and reject content that doesn&#8217;t match its declared type. The attacks each header prevents aren&#8217;t theoretical. Cross-site scripting (XSS) remains one of the most common web vulnerabilities. Clickjacking attacks steal credentials through invisible iframe overlays. Protocol downgrade attacks strip HTTPS from connections on shared networks. MIME-sniffing executes uploaded files as scripts. These are documented attack patterns with known defenses, and security headers are those defenses. What makes the 95% failure rate remarkable is the cost: implementing these headers requires no changes to your application code. It&#8217;s the server configuration. It takes hours for most of them, minutes for several. The protection is immediate. The only explanation for the widespread absence is that developers deprioritize it until an audit or a breach forces attention. How to Check Your Current Headers Before working through the checklist, run a baseline audit to see where you currently stand. Mozilla Observatory observatory.mozilla.org Enter your domain and get a letter grade with specific findings for each header. Grade A or higher is the target. Takes under 30 seconds. securityheaders.com securityheaders.com Focused specifically on security headers. Shows which are present, which are missing, and flags common misconfiguration issues. Quick and readable output. curl from the command line Returns all response headers directly. Handy to check quickly without the browser or an external tool. Notice the security headers there. Open developer tools in the browser (F12) and select Network. Reload the page and then, in a pop-up window, select the first request and then the Headers tab. Response Headers section will show all headers your server sends.. For a broader website security scan covering headers alongside other vulnerabilities, website scanner tools provide a more comprehensive audit in a single pass. Run the audit first. Note which headers are missing and which are present but misconfigured. Then work through the priority sections below. Priority 1: Implement Today (Quick Wins) These headers are one or two lines of server configuration each. They protect against specific, well-documented attacks and have virtually no risk of breaking your site. Implement all of these before anything else. X-Content-Type-Options What it defends from: MIME-sniffing attacks. If you do not set this header, a browser can possibly recognize an uploaded text file as a JavaScript file if the file contents look executable, no matter what the file&#8217;s original MIME type was. It is one of the defenses for a specific attack, where an attacker manages to upload a malicious file that later gets executed as a script. Correct configuration: That&#8217;s it. There&#8217;s one value, and it&#8217;s always nosniff. No configuration decisions required. This is a single line that closes an entire category of upload-based attacks. It should be on every website without exception. Risk of implementation: Zero. This header tells the browser to respect the MIME type you declare. It doesn&#8217;t change behavior for correctly served content. X-Frame-Options What it blocks: clickjacking. A hacker makes a malicious page, loads your site into an imperceptible iframe that is layered over the attacker\u2019s page and then tricks visitors into clicking buttons on that page, unknowingly completing actions on your website such as authorizing a payment or setting a new account password. Correct configuration: Use DENY if your site should never be embedded in a frame anywhere. Use SAMEORIGIN it if you need your own pages to embed other pages from your own domain. Note on CSP frame-ancestors: The Content Security Policy frame-ancestors Directive is the modern replacement for X-Frame-Options and takes precedence in browsers that support CSP. However, X-Frame-Options provides backward compatibility for older browsers. Set both. Risk of implementation: Low. Only affects whether your pages can be embedded in frames. If you don&#8217;t embed your own pages in iframes across different domains, DENY is safe. Referrer-Policy What it protects against: Leakage of information through the Referer header. If your visitors access your site, then click out to another site, the browser will include the URL they were at before in the Referer header. This can include session ID, userid, search terms, or any number of other things you don&#8217;t want spilling over to third parties in the query parameters. Correct configuration: Tweets the full URL for same-origin requests (which can be useful for tracking on your own analytics solutions), only the origin (ie. Https:\/\/yoursite.com) for cross-origin (HTTPS) requests and nothing for cross-origin (HTTP). Risk of implementation: Low to none for most sites. Your internal analytics still works. External<\/p>\n","protected":false},"author":9,"featured_media":3363,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[22,18],"tags":[51],"class_list":["post-3361","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cybersecurity","category-information","tag-website-security"],"_links":{"self":[{"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/posts\/3361","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/comments?post=3361"}],"version-history":[{"count":1,"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/posts\/3361\/revisions"}],"predecessor-version":[{"id":3364,"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/posts\/3361\/revisions\/3364"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/media\/3363"}],"wp:attachment":[{"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/media?parent=3361"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/categories?post=3361"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/getdarkscout.com\/blog\/wp-json\/wp\/v2\/tags?post=3361"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}