<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Content-Security-Policy | Toolbox Guides &amp; Articles</title><link>https://blog.toolbox.vishnudigital.com/tags/content-security-policy/</link><atom:link href="https://blog.toolbox.vishnudigital.com/tags/content-security-policy/index.xml" rel="self" type="application/rss+xml"/><description>Content-Security-Policy</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Sun, 06 Sep 2026 00:00:00 +0000</lastBuildDate><image><url>https://blog.toolbox.vishnudigital.com/media/icon.svg</url><title>Content-Security-Policy</title><link>https://blog.toolbox.vishnudigital.com/tags/content-security-policy/</link></image><item><title>HTTP Security Headers: A Practical Hardening Guide for CSP, HSTS, and Cross-Origin Isolation</title><link>https://blog.toolbox.vishnudigital.com/http-security-headers-hardening-guide/</link><pubDate>Sun, 06 Sep 2026 00:00:00 +0000</pubDate><guid>https://blog.toolbox.vishnudigital.com/http-security-headers-hardening-guide/</guid><description>
&lt;aside class="comic-bubble not-prose my-8 p-6 rounded-2xl border-2 border-[#2c221a] dark:border-[#ded4c3] bg-[#fdfbf7] dark:bg-[#28201a] shadow-[4px_4px_0px_#2c221a] dark:shadow-[4px_4px_0px_#14100d]" aria-label="AEO Quick Answer &amp; Specification Dossier"&gt;
&lt;div class="flex items-center justify-between gap-3 mb-3"&gt;
&lt;div class="flex items-center gap-2"&gt;
&lt;span class="comic-stamp bg-amber-600 text-white px-2.5 py-0.5 rounded text-[11px] font-black tracking-wider"&gt;QUICK ANSWER&lt;/span&gt;
&lt;span class="text-xs font-mono font-bold text-[#2c221a]/70 dark:text-[#ede6db]/70"&gt;RFC SPECIFICATION SUMMARY&lt;/span&gt;
&lt;/div&gt;
&lt;span class="hidden sm:inline-flex items-center gap-1.5 text-[11px] font-bold font-mono px-2 py-0.5 rounded border border-emerald-700 dark:border-emerald-500 bg-emerald-100 dark:bg-emerald-950 text-emerald-900 dark:text-emerald-200"&gt;
&lt;span class="inline-block w-1.5 h-1.5 rounded-full bg-emerald-600 dark:bg-emerald-400"&gt;&lt;/span&gt; 100% Client-Side
&lt;/span&gt;
&lt;/div&gt;
&lt;p class="text-base sm:text-lg text-[#2c221a] dark:text-[#ede6db] leading-relaxed font-semibold mb-4"&gt;
A hardened HTTP response sends a strict Content-Security-Policy that names object-src none, base-uri self, and either a per-response script nonce or strict-dynamic; Strict-Transport-Security with a max-age of at least one year and includeSubDomains; X-Content-Type-Options nosniff; frame-ancestors none or X-Frame-Options DENY; Referrer-Policy strict-origin-when-cross-origin; and a Permissions-Policy that denies unused device APIs. It also removes Server and X-Powered-By version disclosure and drops the deprecated X-XSS-Protection header entirely.
&lt;/p&gt;
&lt;div class="flex flex-wrap gap-2 text-xs font-mono font-bold mt-4 pt-3 border-t border-[#ded4c3] dark:border-[#3b332c]"&gt;
&lt;span class="px-2.5 py-1 rounded bg-[#ede6db] dark:bg-[#382e26] text-[#2c221a] dark:text-[#ede6db] border border-[#ded4c3] dark:border-[#4a3e35]"&gt;Difficulty: Intermediate&lt;/span&gt;
&lt;span class="px-2.5 py-1 rounded bg-[#ede6db] dark:bg-[#382e26] text-[#2c221a] dark:text-[#ede6db] border border-[#ded4c3] dark:border-[#4a3e35]"&gt;Time: 9 Mins&lt;/span&gt;
&lt;span class="px-2.5 py-1 rounded bg-[#ede6db] dark:bg-[#382e26] text-[#2c221a] dark:text-[#ede6db] border border-[#ded4c3] dark:border-[#4a3e35]"&gt;Privacy: 100% Client-Side RAM&lt;/span&gt;
&lt;span class="px-2.5 py-1 rounded bg-[#ede6db] dark:bg-[#382e26] text-[#2c221a] dark:text-[#ede6db] border border-[#ded4c3] dark:border-[#4a3e35]"&gt;Standard: OWASP Secure Headers / RFC 6797 / CSP Level 3&lt;/span&gt;
&lt;/div&gt;
&lt;/aside&gt;
&lt;div class="tool-embed-container comic-panel not-prose my-10 rounded-2xl border-2 border-[#2c221a] dark:border-[#ded4c3] bg-[#fdfbf7] dark:bg-[#28201a] shadow-[4px_4px_0px_#2c221a] dark:shadow-[4px_4px_0px_#14100d] overflow-hidden" id="embed-http-headers-1788851897573846000-box" role="region" aria-label="Interactive Tool Workbench"&gt;
&lt;div class="comic-halftone p-4 sm:p-5 border-b-2 border-[#2c221a] dark:border-[#ded4c3] bg-[#f5efe4] dark:bg-[#211a14] flex flex-wrap items-center justify-between gap-3"&gt;
&lt;div class="flex items-center gap-2.5 flex-wrap min-w-0"&gt;
&lt;span class="comic-stamp bg-amber-600 text-white px-2.5 py-0.5 rounded text-[10px] font-black tracking-wider uppercase"&gt;
Interactive Workbench
&lt;/span&gt;
&lt;span class="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[10px] font-mono font-bold bg-emerald-100 text-emerald-800 dark:bg-emerald-950/60 dark:text-emerald-300 border border-emerald-300 dark:border-emerald-800 shrink-0"&gt;
&lt;span class="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse"&gt;&lt;/span&gt;
&lt;span&gt;LIVE&lt;/span&gt;
&lt;/span&gt;
&lt;h3 class="text-base sm:text-lg font-extrabold text-[#2c221a] dark:text-[#ede6db] tracking-tight truncate m-0"&gt;
HTTP Security Headers: A Practical Hardening Guide for CSP, HSTS, and Cross-Origin Isolation
&lt;/h3&gt;
&lt;/div&gt;
&lt;div class="flex items-center gap-2 shrink-0"&gt;
&lt;a href="https://toolbox.vishnudigital.com/http-headers" target="_blank" rel="noopener noreferrer" class="comic-btn-secondary !py-1 !px-3 !text-xs font-mono font-bold flex items-center gap-1.5 shadow-[2px_2px_0px_#2c221a] dark:shadow-[2px_2px_0px_#14100d] hover:translate-x-[-1px] hover:translate-y-[-1px] transition-all" title="Open full utility in new window"&gt;
&lt;span&gt;Open Fullscreen ↗&lt;/span&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="relative w-full overflow-hidden bg-[#fdfbf7] dark:bg-[#1a1410]" style="min-height: 420px;"&gt;
&lt;div id="embed-http-headers-1788851897573846000-skeleton" class="absolute inset-0 flex flex-col items-center justify-center p-8 bg-[#fdfbf7] dark:bg-[#1e1814] z-10 transition-opacity duration-300"&gt;
&lt;div class="flex items-center gap-3 p-4 rounded-xl border-2 border-[#2c221a] dark:border-[#ded4c3] bg-[#f5efe4] dark:bg-[#28201a] shadow-[2px_2px_0px_#2c221a] dark:shadow-[2px_2px_0px_#14100d] max-w-sm w-full"&gt;
&lt;div class="w-8 h-8 rounded-lg bg-amber-500/20 flex items-center justify-center shrink-0"&gt;
&lt;svg class="w-5 h-5 text-amber-600 animate-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"&gt;
&lt;circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"&gt;&lt;/circle&gt;
&lt;path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="min-w-0 flex-1"&gt;
&lt;div class="text-xs font-bold text-[#2c221a] dark:text-[#ede6db] truncate"&gt;Initializing Workbench...&lt;/div&gt;
&lt;div class="text-[10px] font-mono text-[#2c221a]/60 dark:text-[#ede6db]/60"&gt;100% Client-Side RAM Sandbox&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;iframe
id="embed-http-headers-1788851897573846000-frame"
data-base-src="https://toolbox.vishnudigital.com/http-headers?embed=true"
src="about:blank"
title="HTTP Security Headers: A Practical Hardening Guide for CSP, HSTS, and Cross-Origin Isolation – Free Online Utility"
class="w-full border-0 block opacity-0 transition-opacity duration-300"
style="height: 640px; min-height: 420px;"
loading="lazy"
allow="clipboard-read; clipboard-write; web-share"
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-downloads"&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;div class="p-3 sm:px-5 border-t border-[#ded4c3] dark:border-[#3b332c] bg-[#f5efe4]/80 dark:bg-[#211a14]/80 flex flex-wrap items-center justify-between gap-3 text-xs font-mono"&gt;
&lt;div class="flex items-center gap-2 text-[#2c221a]/70 dark:text-[#ede6db]/70"&gt;
&lt;span class="text-emerald-600 dark:text-emerald-400 font-bold"&gt;🔒 Private Execution:&lt;/span&gt;
&lt;span class="hidden sm:inline"&gt;Computations run in local browser memory. Zero server uploads.&lt;/span&gt;
&lt;span class="sm:hidden"&gt;Zero server uploads.&lt;/span&gt;
&lt;/div&gt;
&lt;div class="flex items-center gap-1.5 shrink-0"&gt;
&lt;a href="https://toolbox.vishnudigital.com/http-headers" target="_blank" rel="noopener noreferrer" class="font-bold text-amber-700 dark:text-amber-400 hover:text-amber-800 dark:hover:text-amber-300 hover:underline"&gt;
Launch Fullscreen →
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;script&gt;
(function() {
const containerId = "embed-http-headers-1788851897573846000";
const frame = document.getElementById(containerId + "-frame");
const skeleton = document.getElementById(containerId + "-skeleton");
if (!frame) return;
function getCurrentTheme() {
return document.documentElement.classList.contains("dark") ? "dark" : "light";
}
function resolveSrc() {
const baseSrc = frame.getAttribute("data-base-src");
if (!baseSrc) return "";
const theme = getCurrentTheme();
return baseSrc + (baseSrc.includes("?") ? "&amp;" : "?") + "theme=" + theme;
}
function loadIframe() {
if (frame.getAttribute("src") !== "about:blank") return;
const fullSrc = resolveSrc();
frame.src = fullSrc;
frame.onload = function() {
if (skeleton) {
skeleton.classList.add("opacity-0");
setTimeout(function() { skeleton.style.display = "none"; }, 300);
}
frame.classList.remove("opacity-0");
frame.classList.add("opacity-100");
syncThemeToIframe();
};
}
function syncThemeToIframe() {
if (!frame || !frame.contentWindow) return;
try {
frame.contentWindow.postMessage({
type: "TOOLBOX_THEME_CHANGE",
theme: getCurrentTheme()
}, "*");
} catch(e) {}
}
if ("IntersectionObserver" in window) {
const observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
loadIframe();
observer.disconnect();
}
});
}, { rootMargin: "250px" });
observer.observe(frame);
} else {
loadIframe();
}
window.addEventListener("message", function(event) {
if (
event.data &amp;&amp;
typeof event.data === "object" &amp;&amp;
event.data.type === "TOOLBOX_RESIZE" &amp;&amp;
event.data.height
) {
if (event.source === frame.contentWindow) {
const measured = Math.max(event.data.height + 20, 420);
frame.style.height = measured + "px";
}
}
});
const themeObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(m) {
if (m.attributeName === "class") {
syncThemeToIframe();
}
});
});
themeObserver.observe(document.documentElement, { attributes: true });
document.querySelectorAll(".theme-toggle").forEach(function(btn) {
btn.addEventListener("click", function() {
setTimeout(syncThemeToIframe, 50);
});
});
})();
&lt;/script&gt;
&lt;div class="tool-hero-cta not-prose my-10 p-6 sm:p-7 rounded-2xl border-2 border-[#2c221a] dark:border-[#ded4c3] bg-[#fdfbf7] dark:bg-[#28201a] shadow-[4px_4px_0px_#2c221a] dark:shadow-[4px_4px_0px_#14100d] flex flex-col md:flex-row items-start md:items-center justify-between gap-6 transition-all" role="complementary" aria-label="Interactive Companion Tool"&gt;
&lt;div class="space-y-2 max-w-xl"&gt;
&lt;div class="flex items-center gap-2"&gt;
&lt;span class="comic-stamp bg-amber-600 text-white px-2.5 py-0.5 rounded text-[10px] font-black tracking-wider uppercase"&gt;
Free In-Browser Tool
&lt;/span&gt;
&lt;span class="text-xs font-mono font-bold text-stone-500 dark:text-stone-400"&gt;TOOLBOX UTILITY&lt;/span&gt;
&lt;/div&gt;
&lt;h3 class="text-xl font-extrabold text-[#2c221a] dark:text-[#ede6db] tracking-tight m-0"&gt;
HTTP Header Security Auditor
&lt;/h3&gt;
&lt;p class="text-sm text-stone-600 dark:text-stone-300 leading-relaxed font-medium m-0"&gt;
Paste a raw response, get an A&amp;#43; to F hardening grade with per-header findings, and export a matching Nginx or Apache configuration snippet.
&lt;/p&gt;
&lt;/div&gt;
&lt;div class="shrink-0 w-full md:w-auto"&gt;
&lt;a href="https://toolbox.vishnudigital.com/http-headers" class="comic-btn-primary w-full md:w-auto text-center" &gt;
Audit Your HTTP Headers →
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="headers-are-the-cheapest-security-you-will-ever-deploy"&gt;Headers Are the Cheapest Security You Will Ever Deploy&lt;/h2&gt;
&lt;p&gt;Most web hardening work requires code changes, reviews, and regression testing. Security headers require a configuration line at the edge and cost nothing at runtime.&lt;/p&gt;
&lt;p&gt;They are also the layer most often left at framework defaults — which is why a first audit of a mature application so often returns a grade of D or F.&lt;/p&gt;
&lt;p&gt;The table below is the shortlist. Everything after it is detail.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header&lt;/th&gt;
&lt;th&gt;Blocks&lt;/th&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Content-Security-Policy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cross-site scripting, data injection&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Critical&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Strict-Transport-Security&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Protocol downgrade, SSL stripping&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Critical&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Content-Type-Options&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MIME-sniffing confusion attacks&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Critical&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frame-ancestors&lt;/code&gt; / &lt;code&gt;X-Frame-Options&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clickjacking, UI redress&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;High&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Referrer-Policy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL and token leakage via &lt;code&gt;Referer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Permissions-Policy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unwanted camera, mic, geolocation access&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COOP&lt;/code&gt; / &lt;code&gt;COEP&lt;/code&gt; / &lt;code&gt;CORP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cross-origin leaks; enables isolation&lt;/td&gt;
&lt;td&gt;Situational&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Cache-Control: no-store&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sensitive data cached on shared devices&lt;/td&gt;
&lt;td&gt;Situational&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="content-security-policy-the-one-that-does-the-work"&gt;Content-Security-Policy: The One That Does the Work&lt;/h2&gt;
&lt;p&gt;CSP tells the browser which sources of script, style, and other content are legitimate. Done properly, it turns a cross-site scripting bug from a full account takeover into a blocked console error.&lt;/p&gt;
&lt;h3 id="the-modern-strict-policy"&gt;The Modern Strict Policy&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Content-Security-Policy:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; default-src &amp;#39;self&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; script-src &amp;#39;nonce-{RANDOM}&amp;#39; &amp;#39;strict-dynamic&amp;#39; https: &amp;#39;unsafe-inline&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; object-src &amp;#39;none&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; base-uri &amp;#39;self&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; frame-ancestors &amp;#39;none&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; require-trusted-types-for &amp;#39;script&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each line earns its place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;'nonce-{RANDOM}'&lt;/code&gt;&lt;/strong&gt; — a fresh, cryptographically random value of at least 128 bits, generated &lt;strong&gt;per response&lt;/strong&gt; and echoed on every legitimate &lt;code&gt;&amp;lt;script nonce=&amp;quot;...&amp;quot;&amp;gt;&lt;/code&gt;. A reused or predictable nonce is no protection at all.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;'strict-dynamic'&lt;/code&gt;&lt;/strong&gt; — lets a nonce-approved script load further scripts it creates programmatically, and &lt;strong&gt;causes host allowlists to be ignored&lt;/strong&gt;. This matters because allowlist-based policies are routinely bypassable through JSONP endpoints and open redirects on allowlisted CDNs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;https: 'unsafe-inline'&lt;/code&gt;&lt;/strong&gt; — a deliberate fallback for browsers too old to understand nonces. Modern browsers &lt;strong&gt;ignore &lt;code&gt;'unsafe-inline'&lt;/code&gt; whenever a nonce or hash is present&lt;/strong&gt;, which is specified behaviour, so this line is backward compatibility rather than a hole.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;object-src 'none'&lt;/code&gt;&lt;/strong&gt; — removes the &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt;, and legacy plugin bypass surface. There is almost never a reason to allow it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;base-uri 'self'&lt;/code&gt;&lt;/strong&gt; — prevents an injected &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag from redirecting every relative URL on the page to an attacker&amp;rsquo;s host.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;frame-ancestors 'none'&lt;/code&gt;&lt;/strong&gt; — the modern clickjacking control, superseding &lt;code&gt;X-Frame-Options&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="roll-it-out-in-report-only-mode-first"&gt;Roll It Out in Report-Only Mode First&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Content-Security-Policy-Report-Only: default-src &amp;#39;self&amp;#39;; report-uri /csp-report;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;Report-Only&lt;/code&gt; variant logs violations without blocking anything. Run it for a full traffic cycle — including whatever nightly and monthly jobs render pages — before switching to the enforcing header. Deploying an untested CSP straight to enforcement is the fastest way to take your own site down.&lt;/p&gt;
&lt;h3 id="directives-that-do-not-behave-as-expected"&gt;Directives That Do Not Behave as Expected&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;default-src&lt;/code&gt; is not a universal fallback.&lt;/strong&gt; It does not cover &lt;code&gt;base-uri&lt;/code&gt;, &lt;code&gt;frame-ancestors&lt;/code&gt;, &lt;code&gt;form-action&lt;/code&gt;, or &lt;code&gt;sandbox&lt;/code&gt;. Set those explicitly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;'unsafe-eval'&lt;/code&gt;&lt;/strong&gt; re-enables &lt;code&gt;eval&lt;/code&gt; and the string form of &lt;code&gt;setTimeout&lt;/code&gt;. Some older bundlers and templating libraries need it; treat needing it as technical debt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A &lt;code&gt;meta http-equiv&lt;/code&gt; CSP silently drops &lt;code&gt;frame-ancestors&lt;/code&gt;, &lt;code&gt;report-uri&lt;/code&gt;, and &lt;code&gt;sandbox&lt;/code&gt;,&lt;/strong&gt; and only applies from the point the parser reads the tag. Send CSP as a real header.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="in-article-ad-wrapper not-prose my-8 p-3 sm:p-4 rounded-xl border-2 border-stone-900/40 dark:border-stone-400/40 bg-[#fdfbf7] dark:bg-[#211c18] shadow-[3px_3px_0px_rgba(41,30,20,0.3)] dark:shadow-[3px_3px_0px_rgba(242,237,228,0.2)] overflow-hidden"&gt;
&lt;div class="flex items-center justify-between mb-2 border-b border-stone-200 dark:border-stone-800 pb-1"&gt;
&lt;span class="text-[10px] font-mono font-bold uppercase tracking-widest text-stone-500 dark:text-stone-400"&gt;Advertisement&lt;/span&gt;
&lt;span class="text-[9px] font-mono uppercase px-1.5 py-0.5 rounded bg-amber-100 dark:bg-amber-950 text-amber-800 dark:text-amber-300"&gt;Sponsored&lt;/span&gt;
&lt;/div&gt;
&lt;div class="w-full flex justify-center items-center overflow-hidden min-h-[250px]"&gt;
&lt;ins class="adsbygoogle"
style="display:block; text-align:center; min-height:250px; width:100%;"
data-ad-layout="in-article"
data-ad-format="fluid"
data-ad-client="ca-pub-5474548764059850"
&gt;&lt;/ins&gt;
&lt;script&gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="strict-transport-security"&gt;Strict-Transport-Security&lt;/h2&gt;
&lt;p&gt;HSTS (&lt;strong&gt;RFC 6797&lt;/strong&gt;) instructs the browser to refuse plaintext HTTP for a host for a fixed period — closing the window in which a first plaintext request can be intercepted and stripped.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Token&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;max-age&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Seconds to remember the HTTPS-only rule&lt;/td&gt;
&lt;td&gt;Required. 63072000 = two years&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;includeSubDomains&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Applies to every subdomain&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Verify every subdomain serves HTTPS first&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;preload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests inclusion in browser preload lists&lt;/td&gt;
&lt;td&gt;Not part of RFC 6797; see hstspreload.org&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Preload eligibility&lt;/strong&gt;, per hstspreload.org, requires a valid certificate, an HTTP-to-HTTPS redirect on the same host, and a header on the HTTPS root with &lt;code&gt;max-age&lt;/code&gt; of at least &lt;strong&gt;31536000&lt;/strong&gt; plus both &lt;code&gt;includeSubDomains&lt;/code&gt; and &lt;code&gt;preload&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Two operational warnings:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Preloading is hard to undo.&lt;/strong&gt; Removal requests take effect only as browsers ship new versions, which can mean months. A subdomain that cannot serve HTTPS becomes unreachable for users whose browsers hold the preload entry.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ramp &lt;code&gt;max-age&lt;/code&gt; upward.&lt;/strong&gt; Start at &lt;code&gt;300&lt;/code&gt;, then a day, then a week, then a year. A mistake at &lt;code&gt;max-age=63072000&lt;/code&gt; is a two-year commitment for every visitor who received it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;HSTS is only sent over HTTPS.&lt;/strong&gt; Browsers ignore it on a plaintext response, by design.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="clickjacking-sniffing-and-referrer-leakage"&gt;Clickjacking, Sniffing, and Referrer Leakage&lt;/h2&gt;
&lt;h3 id="x-content-type-options-nosniff"&gt;&lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;One value, no options, no downside. It stops the browser from second-guessing your &lt;code&gt;Content-Type&lt;/code&gt;, which is what turns an uploaded &lt;code&gt;.txt&lt;/code&gt; file containing markup into stored XSS. Send it on every response.&lt;/p&gt;
&lt;h3 id="x-frame-options"&gt;&lt;code&gt;X-Frame-Options&lt;/code&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;X-Frame-Options: DENY # or SAMEORIGIN
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Defined in &lt;strong&gt;RFC 7034&lt;/strong&gt; and superseded by CSP &lt;code&gt;frame-ancestors&lt;/code&gt;. Note that the &lt;code&gt;ALLOW-FROM&lt;/code&gt; value was never implemented by Chrome or Safari — to permit a specific external framer, you must use &lt;code&gt;frame-ancestors https://partner.example&lt;/code&gt;. Sending both headers is common and harmless, as long as they do not contradict each other.&lt;/p&gt;
&lt;h3 id="referrer-policy"&gt;&lt;code&gt;Referrer-Policy&lt;/code&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Referrer-Policy: strict-origin-when-cross-origin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is now the browser default in Chrome and Firefox, but sending it explicitly documents the intent and protects against a proxy or framework overriding it. Cross-origin requests then carry only the origin — no path, no query string, no accidental token disclosure. Use &lt;code&gt;no-referrer&lt;/code&gt; for pages whose URLs are themselves sensitive, such as password-reset links.&lt;/p&gt;
&lt;h3 id="permissions-policy"&gt;&lt;code&gt;Permissions-Policy&lt;/code&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;An empty allowlist &lt;code&gt;()&lt;/code&gt; denies the feature to the document and every frame it embeds. Deny what you do not use — a third-party script in an iframe cannot prompt for a camera it has no permission to request. This header replaces the older &lt;code&gt;Feature-Policy&lt;/code&gt;, which used a different value syntax.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="cross-origin-isolation-coop-coep-corp"&gt;Cross-Origin Isolation: COOP, COEP, CORP&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Cross-Origin-Opener-Policy: same-origin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Cross-Origin-Embedder-Policy: require-corp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Cross-Origin-Resource-Policy: same-origin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;COOP &lt;code&gt;same-origin&lt;/code&gt;&lt;/strong&gt; severs the &lt;code&gt;window.opener&lt;/code&gt; relationship with cross-origin pages, closing a family of tab-nabbing and cross-window attacks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;COEP &lt;code&gt;require-corp&lt;/code&gt;&lt;/strong&gt; requires every cross-origin subresource to opt in explicitly, via CORS or &lt;code&gt;Cross-Origin-Resource-Policy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CORP&lt;/strong&gt; is the opt-in mechanism resources use to declare who may embed them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;COOP plus COEP together put the document in a &lt;strong&gt;cross-origin isolated&lt;/strong&gt; state, which is a prerequisite for &lt;code&gt;SharedArrayBuffer&lt;/code&gt; and high-resolution timers. That makes them mandatory for WebAssembly-heavy applications — and also the pair most likely to break third-party embeds. &lt;strong&gt;COOP alone is a safe, valuable default;&lt;/strong&gt; adopt COEP only when you need isolation and have audited every embedded resource.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="headers-to-delete"&gt;Headers to Delete&lt;/h2&gt;
&lt;p&gt;Hardening is subtraction as well as addition.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-XSS-Protection&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Remove&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The Chrome XSS Auditor it controlled was removed; the filter itself introduced information-leak bugs. OWASP advises against it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Powered-By&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Remove&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Discloses runtime and version, handing an attacker a CVE shortlist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Minimise&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Apache/2.4.41 (Ubuntu)&lt;/code&gt; names the exact patch level to target&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-AspNet-Version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Remove&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Same disclosure problem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Public-Key-Pins&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Remove&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HPKP is obsolete and removed from browsers; use Certificate Transparency and CAA records&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Also remember &lt;strong&gt;&lt;code&gt;Cache-Control: no-store&lt;/code&gt;&lt;/strong&gt; on any authenticated response. A shared or kiosk browser retaining an account page in its back-forward cache is a real disclosure, and &lt;code&gt;no-cache&lt;/code&gt; alone does not prevent storage.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="server-configuration-pitfalls"&gt;Server Configuration Pitfalls&lt;/h2&gt;
&lt;h3 id="nginx-inheritance-and-error-responses"&gt;Nginx: inheritance and error responses&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Content-Security-Policy&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;default-src&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;self&amp;#39;&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;max-age=63072000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;includeSubDomains&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Content-Type-Options&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;nosniff&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Referrer-Policy&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;strict-origin-when-cross-origin&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Two behaviours cause almost every &amp;ldquo;header missing on some pages&amp;rdquo; report:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;add_header&lt;/code&gt; directives are not merged across levels.&lt;/strong&gt; A single &lt;code&gt;add_header&lt;/code&gt; inside a &lt;code&gt;location&lt;/code&gt; block discards &lt;strong&gt;all&lt;/strong&gt; inherited &lt;code&gt;add_header&lt;/code&gt; directives from &lt;code&gt;server&lt;/code&gt; and &lt;code&gt;http&lt;/code&gt;. Repeat the full set in that block.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Without &lt;code&gt;always&lt;/code&gt;, headers are only added to successful responses.&lt;/strong&gt; Your 404 and 500 pages ship unprotected — and those are exactly the pages most likely to reflect user input.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="apache"&gt;Apache&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-apache" data-lang="apache"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Header&lt;/span&gt; always set Content-Security-Policy &lt;span class="s2"&gt;&amp;#34;default-src &amp;#39;self&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Header&lt;/span&gt; always set Strict-Transport-Security &lt;span class="s2"&gt;&amp;#34;max-age=63072000; includeSubDomains&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Header&lt;/span&gt; always set X-Content-Type-Options &lt;span class="s2"&gt;&amp;#34;nosniff&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Header&lt;/span&gt; always unset X-Powered-By
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Header always set&lt;/code&gt; is the equivalent of Nginx&amp;rsquo;s &lt;code&gt;always&lt;/code&gt;, covering error responses too.&lt;/p&gt;
&lt;h3 id="a-note-on-cors"&gt;A Note on CORS&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; is not a hardening header — it &lt;strong&gt;relaxes&lt;/strong&gt; the same-origin policy. And &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt; cannot be combined with &lt;code&gt;Access-Control-Allow-Credentials: true&lt;/code&gt;; browsers reject that pairing. Reflecting an arbitrary &lt;code&gt;Origin&lt;/code&gt; header back while allowing credentials is a serious vulnerability, not a configuration convenience.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-by-step-auditing-with-toolbox"&gt;Step-by-Step: Auditing with Toolbox&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Capture your real headers.&lt;/strong&gt; Run &lt;code&gt;curl -sSI https://yoursite.example&lt;/code&gt; or copy the response headers from your browser&amp;rsquo;s Network panel.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open the tool&lt;/strong&gt;: visit the
.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Paste the raw response&lt;/strong&gt; into the input box, or start from one of the built-in presets to see what a hardened and an unhardened response look like side by side.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Read the Audit tab&lt;/strong&gt; for the letter grade from &lt;strong&gt;A+ to F&lt;/strong&gt; and the per-header findings behind it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check the Table tab&lt;/strong&gt; for the parsed name-value view, which is the quickest way to spot a duplicated or malformed header.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Export from the Config tab&lt;/strong&gt; — select Nginx or Apache and copy the generated hardening snippet straight into your server or edge configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Re-audit after deploying,&lt;/strong&gt; and check a 404 as well as a 200. Missing headers on error responses is the single most common regression.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Parsing and grading happen entirely in your browser; the tool never fetches your site or transmits the headers you paste. That means you can safely audit a staging environment behind a VPN, or a response containing session cookies.&lt;/p&gt;
&lt;p&gt;Two related hardening steps sit just outside the header layer: verifying that inbound webhooks are genuinely signed, covered in the
, and validating token claims properly, covered in the
.&lt;/p&gt;
&lt;section class="faqs-container not-prose my-12 border-t-2 border-stone-200 dark:border-stone-800 pt-8" aria-label="Frequently Asked Questions"&gt;
&lt;div class="flex items-center gap-2 mb-6"&gt;
&lt;span class="comic-stamp bg-amber-600 text-white px-2 py-0.5 rounded text-[10px] font-black tracking-wider"&gt;FAQ&lt;/span&gt;
&lt;h2 class="text-2xl font-extrabold text-[#2c221a] dark:text-[#ede6db] tracking-tight m-0"&gt;
Frequently Asked Questions
&lt;/h2&gt;
&lt;/div&gt;
&lt;details name="faq" class="group border-2 border-[#2c221a] dark:border-[#ded4c3] rounded-xl p-4 bg-[#fdfbf7] dark:bg-[#28201a] shadow-[3px_3px_0px_#2c221a] dark:shadow-[3px_3px_0px_#14100d] mb-3 transition-all"&gt;
&lt;summary class="flex items-center justify-between cursor-pointer font-bold text-[#2c221a] dark:text-[#ede6db] select-none text-base"&gt;
&lt;span&gt;Which HTTP security headers actually matter most?&lt;/span&gt;
&lt;span class="text-xs font-mono font-bold transition-transform duration-200 group-open:rotate-180 shrink-0 ml-2"&gt;▼&lt;/span&gt;
&lt;/summary&gt;
&lt;p class="mt-3 text-sm text-stone-600 dark:text-stone-300 leading-relaxed font-medium mb-0"&gt;
Four carry most of the value. Content-Security-Policy limits which scripts can execute and is the primary defence against cross-site scripting. Strict-Transport-Security forces HTTPS and prevents downgrade attacks. X-Content-Type-Options with the value nosniff stops MIME confusion attacks. Either CSP frame-ancestors or X-Frame-Options prevents clickjacking. Referrer-Policy and Permissions-Policy are valuable but reduce data leakage rather than blocking an active attack.
&lt;/p&gt;
&lt;/details&gt;
&lt;details name="faq" class="group border-2 border-[#2c221a] dark:border-[#ded4c3] rounded-xl p-4 bg-[#fdfbf7] dark:bg-[#28201a] shadow-[3px_3px_0px_#2c221a] dark:shadow-[3px_3px_0px_#14100d] mb-3 transition-all"&gt;
&lt;summary class="flex items-center justify-between cursor-pointer font-bold text-[#2c221a] dark:text-[#ede6db] select-none text-base"&gt;
&lt;span&gt;Why is unsafe-inline ignored in my Content-Security-Policy?&lt;/span&gt;
&lt;span class="text-xs font-mono font-bold transition-transform duration-200 group-open:rotate-180 shrink-0 ml-2"&gt;▼&lt;/span&gt;
&lt;/summary&gt;
&lt;p class="mt-3 text-sm text-stone-600 dark:text-stone-300 leading-relaxed font-medium mb-0"&gt;
That behaviour is specified, not a bug. When a directive contains a nonce or a hash source expression, the browser ignores unsafe-inline for that directive. The design intent is that a page can send unsafe-inline as a fallback for very old browsers while modern browsers enforce the stricter nonce-based policy. If you want inline scripts to run in a modern browser, you must give each one the matching nonce or hash.
&lt;/p&gt;
&lt;/details&gt;
&lt;details name="faq" class="group border-2 border-[#2c221a] dark:border-[#ded4c3] rounded-xl p-4 bg-[#fdfbf7] dark:bg-[#28201a] shadow-[3px_3px_0px_#2c221a] dark:shadow-[3px_3px_0px_#14100d] mb-3 transition-all"&gt;
&lt;summary class="flex items-center justify-between cursor-pointer font-bold text-[#2c221a] dark:text-[#ede6db] select-none text-base"&gt;
&lt;span&gt;What does HSTS preload require?&lt;/span&gt;
&lt;span class="text-xs font-mono font-bold transition-transform duration-200 group-open:rotate-180 shrink-0 ml-2"&gt;▼&lt;/span&gt;
&lt;/summary&gt;
&lt;p class="mt-3 text-sm text-stone-600 dark:text-stone-300 leading-relaxed font-medium mb-0"&gt;
To be accepted onto the browser preload list at hstspreload.org, a site must serve a valid certificate, redirect all HTTP traffic to HTTPS on the same host, and send a Strict-Transport-Security header on the HTTPS root with a max-age of at least 31536000 seconds, plus the includeSubDomains and preload tokens. Preloading is difficult to reverse quickly, so confirm that every subdomain can serve HTTPS before submitting.
&lt;/p&gt;
&lt;/details&gt;
&lt;details name="faq" class="group border-2 border-[#2c221a] dark:border-[#ded4c3] rounded-xl p-4 bg-[#fdfbf7] dark:bg-[#28201a] shadow-[3px_3px_0px_#2c221a] dark:shadow-[3px_3px_0px_#14100d] mb-3 transition-all"&gt;
&lt;summary class="flex items-center justify-between cursor-pointer font-bold text-[#2c221a] dark:text-[#ede6db] select-none text-base"&gt;
&lt;span&gt;Is X-Frame-Options still needed if I use CSP frame-ancestors?&lt;/span&gt;
&lt;span class="text-xs font-mono font-bold transition-transform duration-200 group-open:rotate-180 shrink-0 ml-2"&gt;▼&lt;/span&gt;
&lt;/summary&gt;
&lt;p class="mt-3 text-sm text-stone-600 dark:text-stone-300 leading-relaxed font-medium mb-0"&gt;
It is largely redundant in current browsers, which prefer frame-ancestors when both are present. Many organisations still send both, because X-Frame-Options remains the header that legacy scanners, corporate proxies, and older embedded browsers understand. Sending both is harmless provided they agree; a DENY value alongside a permissive frame-ancestors list is a configuration conflict waiting to confuse an audit.
&lt;/p&gt;
&lt;/details&gt;
&lt;details name="faq" class="group border-2 border-[#2c221a] dark:border-[#ded4c3] rounded-xl p-4 bg-[#fdfbf7] dark:bg-[#28201a] shadow-[3px_3px_0px_#2c221a] dark:shadow-[3px_3px_0px_#14100d] mb-3 transition-all"&gt;
&lt;summary class="flex items-center justify-between cursor-pointer font-bold text-[#2c221a] dark:text-[#ede6db] select-none text-base"&gt;
&lt;span&gt;Can I set security headers with an HTML meta tag instead?&lt;/span&gt;
&lt;span class="text-xs font-mono font-bold transition-transform duration-200 group-open:rotate-180 shrink-0 ml-2"&gt;▼&lt;/span&gt;
&lt;/summary&gt;
&lt;p class="mt-3 text-sm text-stone-600 dark:text-stone-300 leading-relaxed font-medium mb-0"&gt;
Only partially. A Content-Security-Policy can be delivered via meta http-equiv, but the frame-ancestors, report-uri, and sandbox directives are ignored in that form, and the policy applies only after the parser reaches the tag. No other security header works as a meta tag at all. Send security headers from the server or the edge, where they cover every response including redirects and errors.
&lt;/p&gt;
&lt;/details&gt;
&lt;/section&gt;</description></item></channel></rss>