<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Web-Security | Toolbox Guides &amp; Articles</title><link>https://blog.toolbox.vishnudigital.com/tags/web-security/</link><atom:link href="https://blog.toolbox.vishnudigital.com/tags/web-security/index.xml" rel="self" type="application/rss+xml"/><description>Web-Security</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>Web-Security</title><link>https://blog.toolbox.vishnudigital.com/tags/web-security/</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><item><title>JWT Security 101: How to Decode, Inspect, and Verify Claims Client-Side</title><link>https://blog.toolbox.vishnudigital.com/jwt-security-101-decode-inspect-claims-guide/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://blog.toolbox.vishnudigital.com/jwt-security-101-decode-inspect-claims-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;
To decode and inspect a JSON Web Token safely, paste the token string into a client-side JWT decoder. The tool splits the string across two period delimiters (.) into Header, Payload, and Signature, decodes the Base64Url strings via browser JavaScript, and formats the claims with zero network transmission to third-party servers.
&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: 4 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: RFC 7519&lt;/span&gt;
&lt;/div&gt;
&lt;/aside&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;
JWT Token Inspector &amp;amp; Decoder
&lt;/h3&gt;
&lt;p class="text-sm text-stone-600 dark:text-stone-300 leading-relaxed font-medium m-0"&gt;
Analyze JWT expiration, token issuance, audience, and signature algorithms locally with real-time expiration count-downs.
&lt;/p&gt;
&lt;/div&gt;
&lt;div class="shrink-0 w-full md:w-auto"&gt;
&lt;a href="https://toolbox.vishnudigital.com/jwt-decoder" class="comic-btn-primary w-full md:w-auto text-center" &gt;
Inspect JWT Token Safely →
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="what-is-a-json-web-token-jwt"&gt;What Is a JSON Web Token (JWT)?&lt;/h2&gt;
&lt;p&gt;Defined by &lt;strong&gt;RFC 7519&lt;/strong&gt;, a &lt;strong&gt;JSON Web Token&lt;/strong&gt; is an open, compact, URL-safe container for transferring claims between two parties. JWTs are the dominant authentication mechanism in modern single-page applications (SPAs), microservice architectures, and OAuth 2.0 / OpenID Connect (OIDC) workflows.&lt;/p&gt;
&lt;p&gt;A JWT consists of three distinct parts separated by dots (&lt;code&gt;.&lt;/code&gt;):&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;Header.Payload.Signature
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3MDAwMDAwMDB9.4pz-KE...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="the-three-components-of-a-jwt"&gt;The Three Components of a JWT&lt;/h2&gt;
&lt;h3 id="1-the-header"&gt;1. The Header&lt;/h3&gt;
&lt;p&gt;The header describes the cryptographic metadata and token type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&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="nt"&gt;&amp;#34;alg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;HS256&amp;#34;&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="nt"&gt;&amp;#34;typ&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;JWT&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;alg&lt;/code&gt;&lt;/strong&gt;: The cryptographic signing algorithm (e.g., &lt;code&gt;HS256&lt;/code&gt;, &lt;code&gt;RS256&lt;/code&gt;, &lt;code&gt;ES256&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;typ&lt;/code&gt;&lt;/strong&gt;: Token type, typically &lt;code&gt;&amp;quot;JWT&amp;quot;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-the-payload-claims"&gt;2. The Payload (Claims)&lt;/h3&gt;
&lt;p&gt;The payload contains statements about an entity (usually the authenticated user) and additional session metadata.&lt;/p&gt;
&lt;h4 id="standard-registered-claims"&gt;Standard Registered Claims:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;iss&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;Issuer&lt;/em&gt;): Identifies the authority that issued the JWT.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;sub&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;Subject&lt;/em&gt;): Identifies the principal subject of the token (e.g., user ID).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;aud&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;Audience&lt;/em&gt;): Identifies the intended recipients or resource servers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;exp&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;Expiration Time&lt;/em&gt;): Unix epoch timestamp after which the token must not be accepted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;nbf&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;Not Before&lt;/em&gt;): Identifies the timestamp before which the token is invalid.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;iat&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;Issued At&lt;/em&gt;): Timestamp recording when the token was generated.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-the-signature"&gt;3. The Signature&lt;/h3&gt;
&lt;p&gt;The signature validates that the token was neither tampered with nor forged in transit. For HMAC-SHA256:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;HMACSHA256&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="nx"&gt;base64UrlEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base64UrlEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&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="nx"&gt;secret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;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="critical-jwt-security-pitfalls"&gt;Critical JWT Security Pitfalls&lt;/h2&gt;
&lt;h3 id="1-the-none-algorithm-attack"&gt;1. The &amp;ldquo;None&amp;rdquo; Algorithm Attack&lt;/h3&gt;
&lt;p&gt;In early implementations of RFC 7519, servers supported unsigned tokens where &lt;code&gt;&amp;quot;alg&amp;quot;: &amp;quot;none&amp;quot;&lt;/code&gt;. Attackers bypassed authentication by altering the payload to grant admin privileges and setting &lt;code&gt;&amp;quot;alg&amp;quot;: &amp;quot;none&amp;quot;&lt;/code&gt; with an empty signature. Modern verification libraries strictly whitelist approved algorithms (&lt;code&gt;algorithms: ['RS256']&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="2-key-confusion-asymmetric-to-symmetric"&gt;2. Key Confusion (Asymmetric to Symmetric)&lt;/h3&gt;
&lt;p&gt;When an API verifies tokens using an asymmetric algorithm (like &lt;code&gt;RS256&lt;/code&gt;), the server uses a &lt;strong&gt;public key&lt;/strong&gt; to verify and a &lt;strong&gt;private key&lt;/strong&gt; to sign. In key confusion attacks, an adversary changes the header to &lt;code&gt;&amp;quot;alg&amp;quot;: &amp;quot;HS256&amp;quot;&lt;/code&gt; and signs the token using the server&amp;rsquo;s publicly accessible public key as the HMAC secret! Vulnerable servers verifying with &lt;code&gt;jwt.verify(token, key)&lt;/code&gt; mistakenly treat the public key string as a shared symmetric secret.&lt;/p&gt;
&lt;h3 id="3-leaking-production-secrets-to-web-tools"&gt;3. Leaking Production Secrets to Web Tools&lt;/h3&gt;
&lt;p&gt;Never paste live customer session tokens or sensitive API tokens into third-party servers. Using our
ensures parsing executes purely in local browser memory.&lt;/p&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-jwt-decoder-1788851897567275000-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;
JWT Security 101: How to Decode, Inspect, and Verify Claims Client-Side
&lt;/h3&gt;
&lt;/div&gt;
&lt;div class="flex items-center gap-2 shrink-0"&gt;
&lt;a href="https://toolbox.vishnudigital.com/jwt-decoder" 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="px-5 py-2.5 bg-[#fdfbf7] dark:bg-[#28201a] border-b border-[#ded4c3] dark:border-[#3b332c] text-xs font-medium text-[#2c221a]/80 dark:text-[#ede6db]/80"&gt;
Decode and inspect JWT headers, standard claims, and expiration timestamps securely in your browser.
&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-jwt-decoder-1788851897567275000-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-jwt-decoder-1788851897567275000-frame"
data-base-src="https://toolbox.vishnudigital.com/jwt-decoder?embed=true"
src="about:blank"
title="JWT Security 101: How to Decode, Inspect, and Verify Claims Client-Side – Free Online Utility"
class="w-full border-0 block opacity-0 transition-opacity duration-300"
style="height: 560px; 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/jwt-decoder" 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-jwt-decoder-1788851897567275000";
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;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;Is a JWT token encrypted?&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;
No. Standard JWTs (RFC 7519) are signed, not encrypted. The header and payload are simply Base64Url-encoded JSON strings. Anyone with access to the token string can decode and read the underlying claims in plaintext.
&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 it dangerous to paste production JWTs into random online debuggers?&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;
Many online token inspectors transmit tokens over HTTP to their backend servers for logging or telemetry. If your token contains confidential user IDs, role permissions, or OAuth access scopes, server-side log ingestion creates severe security liabilities. Always use client-side tools.
&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 is the &amp;#39;algorithm none&amp;#39; vulnerability in JWT verification?&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;
The &amp;#39;none&amp;#39; algorithm is an optional spec in RFC 7519 for unsigned tokens. If an insecure server verification library accepts alg: &amp;#39;none&amp;#39;, an attacker can strip the signature, modify the payload (e.g. set &amp;#39;admin&amp;#39;: true), and forge unauthorized authentication.
&lt;/p&gt;
&lt;/details&gt;
&lt;/section&gt;</description></item></channel></rss>