<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged web-security</title>
<link>https://coderlegion.com/tag/web-security</link>
<description>Powered by Question2Answer</description>
<item>
<title>Enterprise Cloud WAF vs Self-Hosted: A Cost-and-Control Breakdown</title>
<link>https://coderlegion.com/30549/enterprise-cloud-waf-vs-self-hosted-a-cost-and-control-breakdown</link>
<description>&lt;h2 id=&quot;enterprise-cloud-waf-vs-self-hosted-a-cost-and-control-breakdown&quot;&gt;Enterprise Cloud WAF vs Self-Hosted: A Cost-and-Control Breakdown&lt;/h2&gt;
&lt;p&gt;When a larger organization evaluates a WAF, the conversation quickly moves past &quot;does it block attacks?&quot; to &quot;who controls it, and what does it cost?&quot; Here's how an enterprise cloud WAF compares to a self-hosted one on those two axes.&lt;/p&gt;
&lt;h3 id=&quot;control&quot;&gt;Control&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;self-hosted WAF&lt;/strong&gt; (like SafeLine) puts configuration in your hands. You set the rules, you see the raw traffic, and the data stays on your infrastructure. For teams with strict data-residency or compliance needs, that control is the whole point.&lt;/p&gt;
&lt;p&gt;An &lt;strong&gt;enterprise cloud WAF&lt;/strong&gt; (like Imperva) centralizes control with the vendor. You get a polished console and bundled services (CDN, DDoS, bot management), but you operate within their model and route traffic through their edge.&lt;/p&gt;
&lt;h3 id=&quot;cost&quot;&gt;Cost&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Self-hosted:&lt;/strong&gt; the Community Edition is free for up to 10 apps at 800 QPS. Larger needs use paid tiers, but there's no per-seat or per-property enterprise premium.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enterprise cloud:&lt;/strong&gt; priced as a subscription sized to your traffic and feature set, typically with a contract and onboarding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a handful of sites, self-hosted is effectively free to start. For a global estate with compliance and SLA requirements, the managed platform's bundled services can justify the spend.&lt;/p&gt;
&lt;h3 id=&quot;where-each-fits&quot;&gt;Where each fits&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Self-hosted &lt;/th&gt;&lt;th&gt;Enterprise cloud&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Best for &lt;/td&gt;&lt;td&gt;Small teams, cost-sensitive, control-focused &lt;/td&gt;&lt;td&gt;Large orgs, compliance, SLA needs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Cost model &lt;/td&gt;&lt;td&gt;Free tier + optional paid tiers &lt;/td&gt;&lt;td&gt;Enterprise subscription&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Data &lt;/td&gt;&lt;td&gt;On your servers &lt;/td&gt;&lt;td&gt;Through vendor edge&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Extras &lt;/td&gt;&lt;td&gt;Dashboard, stats &lt;/td&gt;&lt;td&gt;CDN, DDoS, bot mgmt&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;getting-started-self-hosted&quot;&gt;Getting started self-hosted&lt;/h3&gt;
&lt;p&gt;If you want to see the control model for yourself, install SafeLine on a server you already have:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bash -c &quot;$(curl -fsSLk https://waf.chaitin.com/release/latest/manager.sh)&quot; -- --en
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;https://&amp;lt;your-server-ip&amp;gt;:9443&lt;/code&gt;, point it at your app, and your traffic is filtered — no contract, no DNS change.&lt;/p&gt;
&lt;h3 id=&quot;faq&quot;&gt;FAQ&lt;/h3&gt;
&lt;h4 id=&quot;is-self-hosted-enterprise-grade-&quot;&gt;Is self-hosted &quot;enterprise-grade&quot;?&lt;/h4&gt;
&lt;p&gt;The detection engine is production-grade semantic analysis with low false positives, used at scale. The free tier is sized for ~10 apps; paid tiers cover more.&lt;/p&gt;
&lt;h4 id=&quot;do-i-lose-the-cdn-with-self-hosted-&quot;&gt;Do I lose the CDN with self-hosted?&lt;/h4&gt;
&lt;p&gt;A self-hosted WAF focuses on the application layer. Pair it with whatever CDN you already use.&lt;/p&gt;
&lt;h4 id=&quot;which-is-faster-to-deploy-&quot;&gt;Which is faster to deploy?&lt;/h4&gt;
&lt;p&gt;Self-hosted: one command and a console. Enterprise cloud: onboarding plus routing setup.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Ready to give SafeLine a try?&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐ &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/chaitin/SafeLine&quot;&gt;SafeLine WAF on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🔗 &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🧪 &lt;a rel=&quot;nofollow&quot; href=&quot;https://demo.waf.chaitin.com:9443/statistics&quot;&gt;Live Demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30549/enterprise-cloud-waf-vs-self-hosted-a-cost-and-control-breakdown</guid>
<pubDate>Sat, 10 Oct 2026 20:30:22 +0000</pubDate>
</item>
<item>
<title>Appliance WAF vs Container WAF: The Deployment Trade-off</title>
<link>https://coderlegion.com/30550/appliance-waf-vs-container-waf-the-deployment-trade-off</link>
<description>&lt;h2 id=&quot;appliance-waf-vs-container-waf-the-deployment-trade-off&quot;&gt;Appliance WAF vs Container WAF: The Deployment Trade-off&lt;/h2&gt;
&lt;p&gt;Not all WAFs deploy the same way. Some ship as dedicated hardware or virtual appliances; others run as a lightweight container on a server you already own. The difference changes your cost, your footprint, and how fast you can get protected.&lt;/p&gt;
&lt;h3 id=&quot;the-appliance-model&quot;&gt;The appliance model&lt;/h3&gt;
&lt;p&gt;An &lt;strong&gt;appliance-based WAF&lt;/strong&gt; (like Barracuda WAF) is typically delivered as a hardware box, a virtual appliance, or a cloud instance. It's often purchased with a support contract and managed through the vendor's console. The upside is vendor backing; the downside is heavier provisioning and dedicated resources.&lt;/p&gt;
&lt;h3 id=&quot;the-container-model&quot;&gt;The container model&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;container-based WAF&lt;/strong&gt; (like SafeLine) runs as a reverse proxy on your existing server. One install command, and it's filtering traffic. No dedicated hardware, no separate instance to babysit.&lt;/p&gt;
&lt;h3 id=&quot;comparing-the-trade-offs&quot;&gt;Comparing the trade-offs&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Appliance WAF &lt;/th&gt;&lt;th&gt;Container WAF&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Provisioning &lt;/td&gt;&lt;td&gt;Hardware/virtual appliance + setup &lt;/td&gt;&lt;td&gt;One container on existing server&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Footprint &lt;/td&gt;&lt;td&gt;Dedicated resources &lt;/td&gt;&lt;td&gt;Lightweight, shared host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Support model &lt;/td&gt;&lt;td&gt;Vendor contract &lt;/td&gt;&lt;td&gt;Community + paid tiers&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Cost &lt;/td&gt;&lt;td&gt;License + often support &lt;/td&gt;&lt;td&gt;Free Community Edition; paid tiers&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Time to protect &lt;/td&gt;&lt;td&gt;Longer &lt;/td&gt;&lt;td&gt;Minutes&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;which-should-you-choose-&quot;&gt;Which should you choose?&lt;/h3&gt;
&lt;p&gt;If your environment mandates an approved appliance with vendor SLAs, that's a procurement decision a container WAF doesn't aim to replace. For most teams, a container WAF is enough and dramatically cheaper to run.&lt;/p&gt;
&lt;p&gt;Spin up SafeLine on a server you already have:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bash -c &quot;$(curl -fsSLk https://waf.chaitin.com/release/latest/manager.sh)&quot; -- --en
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;https://&amp;lt;your-server-ip&amp;gt;:9443&lt;/code&gt;, point it at your app, and your traffic is filtered — no appliance to provision.&lt;/p&gt;
&lt;h3 id=&quot;faq-1&quot;&gt;FAQ&lt;/h3&gt;
&lt;h4 id=&quot;does-safeline-need-dedicated-hardware-&quot;&gt;Does SafeLine need dedicated hardware?&lt;/h4&gt;
&lt;p&gt;No. It runs as a container on a server you already operate.&lt;/p&gt;
&lt;h4 id=&quot;can-a-container-waf-block-the-same-attacks-as-an-appliance-&quot;&gt;Can a container WAF block the same attacks as an appliance?&lt;/h4&gt;
&lt;p&gt;For application-layer threats (SQLi, XSS, bots), yes — both filter HTTP traffic; the delivery model is what differs.&lt;/p&gt;
&lt;h4 id=&quot;which-is-faster-to-deploy--1&quot;&gt;Which is faster to deploy?&lt;/h4&gt;
&lt;p&gt;Container: one command and a console. Appliance: provisioning plus configuration.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Ready to give SafeLine a try?&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐ &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/chaitin/SafeLine&quot;&gt;SafeLine WAF on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🔗 &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🧪 &lt;a rel=&quot;nofollow&quot; href=&quot;https://demo.waf.chaitin.com:9443/statistics&quot;&gt;Live Demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30550/appliance-waf-vs-container-waf-the-deployment-trade-off</guid>
<pubDate>Sat, 10 Oct 2026 10:27:41 +0000</pubDate>
</item>
<item>
<title>Appliance WAF vs Container WAF: The Deployment Trade-off</title>
<link>https://coderlegion.com/30560/appliance-waf-vs-container-waf-the-deployment-trade-off</link>
<description>&lt;h2 id=&quot;appliance-waf-vs-container-waf-the-deployment-trade-off-1&quot;&gt;Appliance WAF vs Container WAF: The Deployment Trade-off&lt;/h2&gt;
&lt;p&gt;Not all WAFs deploy the same way. Some ship as dedicated hardware or virtual appliances; others run as a lightweight container on a server you already own. The difference changes your cost, your footprint, and how fast you can get protected.&lt;/p&gt;
&lt;h3 id=&quot;the-appliance-model-1&quot;&gt;The appliance model&lt;/h3&gt;
&lt;p&gt;An &lt;strong&gt;appliance-based WAF&lt;/strong&gt; (like Barracuda WAF) is typically delivered as a hardware box, a virtual appliance, or a cloud instance. It's often purchased with a support contract and managed through the vendor's console. The upside is vendor backing; the downside is heavier provisioning and dedicated resources.&lt;/p&gt;
&lt;h3 id=&quot;the-container-model-1&quot;&gt;The container model&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;container-based WAF&lt;/strong&gt; (like SafeLine) runs as a reverse proxy on your existing server. One install command, and it's filtering traffic. No dedicated hardware, no separate instance to babysit.&lt;/p&gt;
&lt;h3 id=&quot;comparing-the-trade-offs-1&quot;&gt;Comparing the trade-offs&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Appliance WAF &lt;/th&gt;&lt;th&gt;Container WAF&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Provisioning &lt;/td&gt;&lt;td&gt;Hardware/virtual appliance + setup &lt;/td&gt;&lt;td&gt;One container on existing server&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Footprint &lt;/td&gt;&lt;td&gt;Dedicated resources &lt;/td&gt;&lt;td&gt;Lightweight, shared host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Support model &lt;/td&gt;&lt;td&gt;Vendor contract &lt;/td&gt;&lt;td&gt;Community + paid tiers&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Cost &lt;/td&gt;&lt;td&gt;License + often support &lt;/td&gt;&lt;td&gt;Free Community Edition; paid tiers&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Time to protect &lt;/td&gt;&lt;td&gt;Longer &lt;/td&gt;&lt;td&gt;Minutes&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;which-should-you-choose--1&quot;&gt;Which should you choose?&lt;/h3&gt;
&lt;p&gt;If your environment mandates an approved appliance with vendor SLAs, that's a procurement decision a container WAF doesn't aim to replace. For most teams, a container WAF is enough and dramatically cheaper to run.&lt;/p&gt;
&lt;p&gt;Spin up SafeLine on a server you already have:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bash -c &quot;$(curl -fsSLk https://waf.chaitin.com/release/latest/manager.sh)&quot; -- --en
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;https://&amp;lt;your-server-ip&amp;gt;:9443&lt;/code&gt;, point it at your app, and your traffic is filtered — no appliance to provision.&lt;/p&gt;
&lt;h3 id=&quot;faq-2&quot;&gt;FAQ&lt;/h3&gt;
&lt;h4 id=&quot;does-safeline-need-dedicated-hardware--1&quot;&gt;Does SafeLine need dedicated hardware?&lt;/h4&gt;
&lt;p&gt;No. It runs as a container on a server you already operate.&lt;/p&gt;
&lt;h4 id=&quot;can-a-container-waf-block-the-same-attacks-as-an-appliance--1&quot;&gt;Can a container WAF block the same attacks as an appliance?&lt;/h4&gt;
&lt;p&gt;For application-layer threats (SQLi, XSS, bots), yes — both filter HTTP traffic; the delivery model is what differs.&lt;/p&gt;
&lt;h4 id=&quot;which-is-faster-to-deploy--2&quot;&gt;Which is faster to deploy?&lt;/h4&gt;
&lt;p&gt;Container: one command and a console. Appliance: provisioning plus configuration.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Ready to give SafeLine a try?&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐ &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/chaitin/SafeLine&quot;&gt;SafeLine WAF on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🔗 &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🧪 &lt;a rel=&quot;nofollow&quot; href=&quot;https://demo.waf.chaitin.com:9443/statistics&quot;&gt;Live Demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30560/appliance-waf-vs-container-waf-the-deployment-trade-off</guid>
<pubDate>Sat, 10 Oct 2026 06:09:10 +0000</pubDate>
</item>
<item>
<title>Enterprise Cloud WAF vs Self-Hosted: A Cost-and-Control Breakdown</title>
<link>https://coderlegion.com/30559/enterprise-cloud-waf-vs-self-hosted-a-cost-and-control-breakdown</link>
<description>&lt;h2 id=&quot;enterprise-cloud-waf-vs-self-hosted-a-cost-and-control-breakdown-1&quot;&gt;Enterprise Cloud WAF vs Self-Hosted: A Cost-and-Control Breakdown&lt;/h2&gt;
&lt;p&gt;When a larger organization evaluates a WAF, the conversation quickly moves past &quot;does it block attacks?&quot; to &quot;who controls it, and what does it cost?&quot; Here's how an enterprise cloud WAF compares to a self-hosted one on those two axes.&lt;/p&gt;
&lt;h3 id=&quot;control-1&quot;&gt;Control&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;self-hosted WAF&lt;/strong&gt; (like SafeLine) puts configuration in your hands. You set the rules, you see the raw traffic, and the data stays on your infrastructure. For teams with strict data-residency or compliance needs, that control is the whole point.&lt;/p&gt;
&lt;p&gt;An &lt;strong&gt;enterprise cloud WAF&lt;/strong&gt; (like Imperva) centralizes control with the vendor. You get a polished console and bundled services (CDN, DDoS, bot management), but you operate within their model and route traffic through their edge.&lt;/p&gt;
&lt;h3 id=&quot;cost-1&quot;&gt;Cost&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Self-hosted:&lt;/strong&gt; the Community Edition is free for up to 10 apps at 800 QPS. Larger needs use paid tiers, but there's no per-seat or per-property enterprise premium.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enterprise cloud:&lt;/strong&gt; priced as a subscription sized to your traffic and feature set, typically with a contract and onboarding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a handful of sites, self-hosted is effectively free to start. For a global estate with compliance and SLA requirements, the managed platform's bundled services can justify the spend.&lt;/p&gt;
&lt;h3 id=&quot;where-each-fits-1&quot;&gt;Where each fits&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Self-hosted &lt;/th&gt;&lt;th&gt;Enterprise cloud&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Best for &lt;/td&gt;&lt;td&gt;Small teams, cost-sensitive, control-focused &lt;/td&gt;&lt;td&gt;Large orgs, compliance, SLA needs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Cost model &lt;/td&gt;&lt;td&gt;Free tier + optional paid tiers &lt;/td&gt;&lt;td&gt;Enterprise subscription&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Data &lt;/td&gt;&lt;td&gt;On your servers &lt;/td&gt;&lt;td&gt;Through vendor edge&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Extras &lt;/td&gt;&lt;td&gt;Dashboard, stats &lt;/td&gt;&lt;td&gt;CDN, DDoS, bot mgmt&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;getting-started-self-hosted-1&quot;&gt;Getting started self-hosted&lt;/h3&gt;
&lt;p&gt;If you want to see the control model for yourself, install SafeLine on a server you already have:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bash -c &quot;$(curl -fsSLk https://waf.chaitin.com/release/latest/manager.sh)&quot; -- --en
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;https://&amp;lt;your-server-ip&amp;gt;:9443&lt;/code&gt;, point it at your app, and your traffic is filtered — no contract, no DNS change.&lt;/p&gt;
&lt;h3 id=&quot;faq-3&quot;&gt;FAQ&lt;/h3&gt;
&lt;h4 id=&quot;is-self-hosted-enterprise-grade--1&quot;&gt;Is self-hosted &quot;enterprise-grade&quot;?&lt;/h4&gt;
&lt;p&gt;The detection engine is production-grade semantic analysis with low false positives, used at scale. The free tier is sized for ~10 apps; paid tiers cover more.&lt;/p&gt;
&lt;h4 id=&quot;do-i-lose-the-cdn-with-self-hosted--1&quot;&gt;Do I lose the CDN with self-hosted?&lt;/h4&gt;
&lt;p&gt;A self-hosted WAF focuses on the application layer. Pair it with whatever CDN you already use.&lt;/p&gt;
&lt;h4 id=&quot;which-is-faster-to-deploy--3&quot;&gt;Which is faster to deploy?&lt;/h4&gt;
&lt;p&gt;Self-hosted: one command and a console. Enterprise cloud: onboarding plus routing setup.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Ready to give SafeLine a try?&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐ &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/chaitin/SafeLine&quot;&gt;SafeLine WAF on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🔗 &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🧪 &lt;a rel=&quot;nofollow&quot; href=&quot;https://demo.waf.chaitin.com:9443/statistics&quot;&gt;Live Demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30559/enterprise-cloud-waf-vs-self-hosted-a-cost-and-control-breakdown</guid>
<pubDate>Sat, 10 Oct 2026 06:08:23 +0000</pubDate>
</item>
<item>
<title>WAF 101: What Actually Happens When a Request Hits Your Firewall</title>
<link>https://coderlegion.com/30551/waf-101-what-actually-happens-when-a-request-hits-your-firewall</link>
<description>&lt;h2 id=&quot;waf-101-what-actually-happens-when-a-request-hits-your-firewall&quot;&gt;WAF 101: What Actually Happens When a Request Hits Your Firewall&lt;/h2&gt;
&lt;p&gt;You've been told a WAF sits in front of your app and blocks bad traffic. But what actually happens, step by step, when a single HTTP request arrives? Here's the lifecycle — no marketing, just the mechanics.&lt;/p&gt;
&lt;h3 id=&quot;1-the-request-hits-the-proxy&quot;&gt;1. The request hits the proxy&lt;/h3&gt;
&lt;p&gt;A WAF runs as a reverse proxy. Every request to your app lands at the WAF first, not your application. The WAF is the gatekeeper; your app only ever sees what the gatekeeper lets through.&lt;/p&gt;
&lt;h3 id=&quot;2-it-parses-the-request&quot;&gt;2. It parses the request&lt;/h3&gt;
&lt;p&gt;The WAF reads the pieces that matter: the URL, the headers, the cookies, and the request body. A network firewall only looks at IPs and ports; a WAF understands HTTP, so it can see the &lt;em&gt;content&lt;/em&gt; of the request.&lt;/p&gt;
&lt;h3 id=&quot;3-it-makes-a-decision&quot;&gt;3. It makes a decision&lt;/h3&gt;
&lt;p&gt;For each request, the WAF decides &lt;strong&gt;allow&lt;/strong&gt;, &lt;strong&gt;block&lt;/strong&gt;, or &lt;strong&gt;challenge&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Allow&lt;/strong&gt; — looks like a normal request, forward to your app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Block&lt;/strong&gt; — matches an attack pattern (SQL injection, XSS payload, malicious bot), drop it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Challenge&lt;/strong&gt; — suspicious but not certain, e.g., a CAPTCHA or JavaScript check.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;4-two-ways-it-detects-attacks&quot;&gt;4. Two ways it detects attacks&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Signature-based:&lt;/strong&gt; matches known attack strings, like an antivirus. Needs constant updating, and novel attacks slip through.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Semantic analysis:&lt;/strong&gt; understands what the request is &lt;em&gt;trying to do&lt;/em&gt; (for example, &quot;this is a SQL command&quot;), so it catches variants without a signature. SafeLine uses this approach, which keeps false positives low.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;5-clean-traffic-moves-on&quot;&gt;5. Clean traffic moves on&lt;/h3&gt;
&lt;p&gt;Anything the WAF allows is forwarded to your application. Anything blocked never reaches your code — so the attack surface your app has to defend shrinks to near zero.&lt;/p&gt;
&lt;h3 id=&quot;why-placement-matters&quot;&gt;Why placement matters&lt;/h3&gt;
&lt;p&gt;A self-hosted WAF like SafeLine runs on your own server as a reverse proxy. Install it with one command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bash -c &quot;$(curl -fsSLk https://waf.chaitin.com/release/latest/manager.sh)&quot; -- --en
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then open &lt;code&gt;https://&amp;lt;your-server-ip&amp;gt;:9443&lt;/code&gt;, point it at your app, and every request flows through the gatekeeper first — no third party in the path.&lt;/p&gt;
&lt;h3 id=&quot;faq-4&quot;&gt;FAQ&lt;/h3&gt;
&lt;h4 id=&quot;does-a-waf-replace-my-network-firewall-&quot;&gt;Does a WAF replace my network firewall?&lt;/h4&gt;
&lt;p&gt;No. They handle different layers. The network firewall governs connections; the WAF understands HTTP.&lt;/p&gt;
&lt;h4 id=&quot;will-it-slow-my-site-&quot;&gt;Will it slow my site?&lt;/h4&gt;
&lt;p&gt;A well-tuned WAF adds minimal latency. SafeLine's free tier handles 800 QPS per instance.&lt;/p&gt;
&lt;h4 id=&quot;is-a-waf-enough-on-its-own-&quot;&gt;Is a WAF enough on its own?&lt;/h4&gt;
&lt;p&gt;It covers the application layer. Pair it with patching, rate limiting, and (if needed) a CDN for broader coverage.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Ready to protect your sites without paying for a cloud WAF?&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐ &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/chaitin/SafeLine&quot;&gt;SafeLine WAF on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🔗 &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🧪 &lt;a rel=&quot;nofollow&quot; href=&quot;https://demo.waf.chaitin.com:9443/statistics&quot;&gt;Live Demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30551/waf-101-what-actually-happens-when-a-request-hits-your-firewall</guid>
<pubDate>Sat, 10 Oct 2026 04:24:57 +0000</pubDate>
</item>
<item>
<title>Self-Hosted vs Managed WAF: Which Deployment Fits Your Team?</title>
<link>https://coderlegion.com/30548/self-hosted-vs-managed-waf-which-deployment-fits-your-team</link>
<description>&lt;h2 id=&quot;self-hosted-vs-managed-waf-which-deployment-fits-your-team-&quot;&gt;Self-Hosted vs Managed WAF: Which Deployment Fits Your Team?&lt;/h2&gt;
&lt;p&gt;Picking a WAF usually comes down to one question: do you want to run the security infrastructure yourself, or pay someone to run it for you? That single decision shapes your cost, your control, and how much operational work lands on your plate.&lt;/p&gt;
&lt;h3 id=&quot;two-deployment-models&quot;&gt;Two deployment models&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Self-hosted (e.g., SafeLine).&lt;/strong&gt; You install the WAF on your own server — one command — and it runs as a reverse proxy in front of your app. Your traffic never leaves your network, and you decide the config.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Managed cloud (e.g., Sucuri).&lt;/strong&gt; The vendor runs the WAF in their own network. You point your DNS at them, and they filter traffic before it reaches you. Someone else operates the infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;what-you-actually-give-up-or-keep&quot;&gt;What you actually give up or keep&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Self-hosted &lt;/th&gt;&lt;th&gt;Managed cloud&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Where traffic flows &lt;/td&gt;&lt;td&gt;Your server only &lt;/td&gt;&lt;td&gt;Through the vendor's cloud&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Upfront cost &lt;/td&gt;&lt;td&gt;Free tier available &lt;/td&gt;&lt;td&gt;Recurring subscription&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Operational work &lt;/td&gt;&lt;td&gt;You patch and monitor &lt;/td&gt;&lt;td&gt;Vendor handles uptime&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Control over config &lt;/td&gt;&lt;td&gt;Full &lt;/td&gt;&lt;td&gt;Limited to their console&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Data residency &lt;/td&gt;&lt;td&gt;Stays with you &lt;/td&gt;&lt;td&gt;On the vendor's servers&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;how-to-decide&quot;&gt;How to decide&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choose self-hosted&lt;/strong&gt; if you want predictable cost, full control, and to keep request data on your own infrastructure. SafeLine's free Community Edition covers up to 10 apps at 800 QPS and installs in one line:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bash -c &quot;$(curl -fsSLk https://waf.chaitin.com/release/latest/manager.sh)&quot; -- --en
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then open &lt;code&gt;https://&amp;lt;your-server-ip&amp;gt;:9443&lt;/code&gt; and point it at your app. No DNS change required.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choose managed cloud&lt;/strong&gt; if you'd rather offload operations entirely and you also want extras like malware cleanup and a CDN bundled in. The trade-off is a monthly fee and routing traffic through a third party.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-bottom-line&quot;&gt;The bottom line&lt;/h3&gt;
&lt;p&gt;Neither is &quot;better&quot; in the abstract — they optimize for different things. Self-hosted wins on cost and control; managed wins on convenience. If you're a small team that wants strong protection without a sales call, starting self-hosted is usually the faster, cheaper path.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Ready to protect your sites without paying for a cloud WAF?&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐ &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/chaitin/SafeLine&quot;&gt;SafeLine WAF on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🔗 &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🧪 &lt;a rel=&quot;nofollow&quot; href=&quot;https://demo.waf.chaitin.com:9443/statistics&quot;&gt;Live Demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30548/self-hosted-vs-managed-waf-which-deployment-fits-your-team</guid>
<pubDate>Sat, 10 Oct 2026 03:59:58 +0000</pubDate>
</item>
<item>
<title>What Is Authentication? A Beginner's Guide (2026)</title>
<link>https://coderlegion.com/25969/what-is-authentication-a-beginners-guide-2026</link>
<description>&lt;p&gt;Every time you type your email and password into a login form, something is quietly deciding whether you actually get in. That decision — proving you are who you say you are — is called &lt;strong&gt;authentication&lt;/strong&gt;, and it's one of those concepts every developer touches constantly but rarely stops to explain from scratch.&lt;/p&gt;
&lt;p&gt;If you've ever wondered what actually happens between &quot;click Sign In&quot; and &quot;welcome back,&quot; or mixed up &lt;em&gt;authentication&lt;/em&gt; with &lt;em&gt;authorization&lt;/em&gt; in an interview, this guide is for you. By the end, you'll know exactly what authentication is, how the major methods work under the hood, and how to build a basic authentication flow yourself.&lt;/p&gt;
&lt;h3 id=&quot;what-is-authentication-quick-answer-&quot;&gt;What Is Authentication? (Quick Answer)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Authentication is the process of verifying that someone is who they claim to be.&lt;/strong&gt; It answers one question: &lt;em&gt;&quot;Who are you?&quot;&lt;/em&gt; This usually happens by checking a password, a code from your phone, a fingerprint, or a token — something only the real user should have or know.&lt;/p&gt;
&lt;p&gt;Authentication is different from &lt;strong&gt;authorization&lt;/strong&gt;, which answers a separate question: &lt;em&gt;&quot;What are you allowed to do?&quot;&lt;/em&gt; A logged-in user is authenticated. Whether that user can delete another person's account is authorization. Mixing these two up is one of the most common beginner mistakes — keep them mentally separate from the start.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;💡 &lt;strong&gt;Simple way to remember it:&lt;/strong&gt; Authentication = ID check at the door. Authorization = the guest list that decides which rooms you can enter once you're inside.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;why-authentication-matters&quot;&gt;Why Authentication Matters&lt;/h3&gt;
&lt;p&gt;Without proper authentication and authorization, an app that stores personal data could expose private information to unauthorized users. Authentication provides the identity context that many authorization, personalization, and auditing systems rely on.&lt;/p&gt;
&lt;p&gt;It matters for a few concrete reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Privacy&lt;/strong&gt; — only the right person sees their own data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt; — prevents account takeover and unauthorized access&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Personalization&lt;/strong&gt; — apps can remember who you are across visits&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accountability&lt;/strong&gt; — actions can be traced back to a real identity (important for admin tools, financial apps, audit logs)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;authentication-vs-authorization-the-difference-that-trips-everyone-up&quot;&gt;Authentication vs. Authorization: The Difference That Trips Everyone Up&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Authentication &lt;/th&gt;&lt;th&gt;Authorization&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Question it answers &lt;/td&gt;&lt;td&gt;Who are you? &lt;/td&gt;&lt;td&gt;What can you do?&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Happens &lt;/td&gt;&lt;td&gt;First &lt;/td&gt;&lt;td&gt;After authentication&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Example &lt;/td&gt;&lt;td&gt;Logging in with a password &lt;/td&gt;&lt;td&gt;Only admins can access &lt;code&gt;/admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Failure result &lt;/td&gt;&lt;td&gt;401 Unauthorized &lt;/td&gt;&lt;td&gt;403 Forbidden&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Why the HTTP status codes are confusingly named:&lt;/strong&gt; &lt;code&gt;401 Unauthorized&lt;/code&gt; actually means &quot;you're not authenticated&quot; (log in first), while &lt;code&gt;403 Forbidden&lt;/code&gt; means &quot;you're authenticated, but not allowed to do this.&quot; Yes, &lt;code&gt;401&lt;/code&gt; should really be called &quot;Unauthenticated&quot; — it's a long-standing naming quirk in the HTTP spec that trips up almost every developer at least once.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/role-based-authentication-nextjs-16&quot;&gt;Related: Role-Based Access Control (RBAC) in Next.js 16&lt;/a&gt; goes deep on the authorization half of this once you're ready for it.&lt;/p&gt;
&lt;h3 id=&quot;how-authentication-works-step-by-step-&quot;&gt;How Authentication Works (Step by Step)&lt;/h3&gt;
&lt;p&gt;At a high level, almost every authentication system follows the same basic loop:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The user provides credentials&lt;/strong&gt; — a password, a fingerprint, a one-time code, or an existing account from another provider (like signing in with Google)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The server verifies those credentials&lt;/strong&gt; — checking a hashed password, validating a signed token, or confirming a biometric match&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The server issues proof of identity&lt;/strong&gt; — usually a session or a token, stored in a cookie or sent back to the client&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;That proof is sent with future requests&lt;/strong&gt; — so the user doesn't have to log in again on every page&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;User submits credentials
        ↓
Server verifies credentials
        ↓
Server issues session/token
        ↓
Client stores it (cookie)
        ↓
Future requests include it
        ↓
Server verifies it's still valid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That last step — verifying the proof of identity on every request — is what keeps a user &quot;logged in&quot; without asking for their password on every single page.&lt;/p&gt;
&lt;h3 id=&quot;the-main-types-of-authentication&quot;&gt;The Main Types of Authentication&lt;/h3&gt;
&lt;p&gt;There isn't just one way to authenticate someone. Here are the methods you'll actually run into as a developer.&lt;/p&gt;
&lt;h4 id=&quot;1-password-based-authentication&quot;&gt;1. Password-Based Authentication&lt;/h4&gt;
&lt;p&gt;The classic: a username/email plus a password, checked against a hashed value stored in your database. Never store plain-text passwords — always hash them with a slow, password-specific hashing algorithm such as &lt;strong&gt;Argon2id&lt;/strong&gt;. If it's not available, &lt;strong&gt;bcrypt&lt;/strong&gt; or another appropriate password hashing algorithm can be used.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// lib/auth/hash-password.ts
import bcrypt from &quot;bcryptjs&quot;;

const SALT_ROUNDS = 12;

export async function hashPassword(plainPassword: string): Promise&amp;lt;string&amp;gt; {
  return bcrypt.hash(plainPassword, SALT_ROUNDS);
}

export async function verifyPassword(
  plainPassword: string,
  hashedPassword: string
): Promise&amp;lt;boolean&amp;gt; {
  return bcrypt.compare(plainPassword, hashedPassword);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;What's happening here:&lt;/strong&gt; &lt;code&gt;bcrypt.hash()&lt;/code&gt; doesn't just scramble the password — it applies a slow, computationally expensive algorithm on purpose, which increases the cost of each password guess and makes large-scale brute-force attacks significantly more expensive. &lt;code&gt;SALT_ROUNDS = 12&lt;/code&gt; is above OWASP's minimum bcrypt work factor of 10, but the right value depends on your server's hardware and traffic. Benchmark your production environment and choose a cost that makes password verification sufficiently expensive without creating unacceptable latency.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;bcrypt limitation:&lt;/strong&gt; bcrypt has a hard 72-byte input limit, and most implementations (including &lt;code&gt;bcryptjs&lt;/code&gt;) silently truncate anything past it rather than erroring. Because UTF-8 characters like emoji or accented letters can take up to 4 bytes each, a password that looks well under the limit in &lt;em&gt;characters&lt;/em&gt; can still get silently cut short in &lt;em&gt;bytes&lt;/em&gt; — which can cause two different passwords to be treated as equivalent after truncation.&lt;br&gt;
OWASP currently recommends enforcing a maximum of 72 bytes (or less, depending on the implementation) when bcrypt is used. Because UTF-8 characters can occupy multiple bytes, a character-count limit alone doesn't necessarily correspond to bcrypt's byte limit.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;Common Mistake:&lt;/strong&gt; Comparing passwords with a plain &lt;code&gt;===&lt;/code&gt; check after &quot;encrypting&quot; them with something reversible like Base64. That's encoding, not hashing — it can be reversed instantly. Only use a dedicated password-hashing library.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;2-session-based-authentication&quot;&gt;2. Session-Based Authentication&lt;/h4&gt;
&lt;p&gt;After a successful login, the server creates a &lt;strong&gt;session&lt;/strong&gt; record stored in server-side session storage such as a database or shared cache — and gives the browser a random session ID in an &lt;code&gt;httpOnly&lt;/code&gt; cookie. On every request, the server looks up that ID to know who's logged in.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// lib/auth/session.ts
import { cookies } from &quot;next/headers&quot;;
import { randomUUID } from &quot;crypto&quot;;

export async function createSession(userId: string, expiresAt: Date) {
  // Generate a random, unguessable session ID — never store the raw
  // userId (or anything else predictable) directly as the cookie's
  // value. The cookie is a reference you look up server-side; it
  // should never double as proof of identity on its own.
  const sessionId = randomUUID();

  // Persist the mapping server-side, e.g.:
  // await db.session.create({ data: { sessionId, userId, expiresAt } });

  const cookieStore = await cookies();

  cookieStore.set(&quot;session_id&quot;, sessionId, {
    httpOnly: true,
    secure: true,
    sameSite: &quot;lax&quot;,
    expires: expiresAt,
    path: &quot;/&quot;,
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Why each flag matters:&lt;/strong&gt; &lt;code&gt;httpOnly&lt;/code&gt; stops client-side JavaScript from reading the cookie (blocking a common XSS attack path). &lt;code&gt;secure&lt;/code&gt; means it's only sent over HTTPS. &lt;code&gt;sameSite: &quot;lax&quot;&lt;/code&gt; limits when the browser sends the cookie in cross-site requests and provides some CSRF protection. For state-changing operations, you should still implement an appropriate CSRF defense rather than relying on SameSite alone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; straightforward server-side revocation; deleting a session record can invalidate that session immediately, and deleting all of a user's sessions can log them out everywhere.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; can require a server-side session lookup on authenticated requests, although caching can reduce the cost.&lt;/p&gt;
&lt;h4 id=&quot;3-token-based-authentication-jwt-&quot;&gt;3. Token-Based Authentication (JWT)&lt;/h4&gt;
&lt;p&gt;Instead of looking anything up server-side, a &lt;strong&gt;JSON Web Token (JWT)&lt;/strong&gt; packs the user's identity directly into a signed token. In a stateless JWT design, the server can verify the token cryptographically without looking up a session record in the database.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// lib/auth/jwt.ts
import { SignJWT, jwtVerify } from &quot;jose&quot;;

const secretKey = new TextEncoder().encode(process.env.AUTH_SECRET);

export async function signToken(userId: string, expiresAt: Date) {
  return new SignJWT({})
    .setProtectedHeader({ alg: &quot;HS256&quot; })
    .setSubject(userId)
    .setIssuedAt()
    .setExpirationTime(expiresAt)
    .sign(secretKey);
}

export async function verifyToken(token: string) {
  try {
    const { payload } = await jwtVerify(token, secretKey, {
      algorithms: [&quot;HS256&quot;],
    });
    return payload;
  } catch {
    return null; // invalid or expired
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;What's happening here:&lt;/strong&gt; &lt;code&gt;jose&lt;/code&gt; is the standard, actively maintained library for signing and verifying JWTs using the Web Crypto API — it works in Node.js, edge runtimes, and browsers. &lt;code&gt;setSubject(userId)&lt;/code&gt; stores the user's ID in the token's standard &lt;code&gt;sub&lt;/code&gt; claim. &lt;code&gt;jwtVerify()&lt;/code&gt; automatically rejects the token if the signature doesn't match or if it's expired, so you never write manual expiry checks.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;Signing is not encryption.&lt;/strong&gt; A JWT's payload is only base64url-encoded, not encrypted — anyone can decode and read it. The signature only proves it wasn't tampered with. Never put sensitive data (passwords, secrets) inside a JWT payload.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; no database lookup needed to verify identity, works well across distributed/stateless servers.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; harder to instantly revoke a single token before it expires (it's valid until it naturally expires unless you build extra machinery around it).&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;💡 For a deeper side-by-side of these two models — including how each behaves at scale — see &lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/session-vs-jwt-for-nextjs-16&quot;&gt;Session vs JWT: Which Should You Use in Next.js 16?&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;4-oauth-openid-connect-social-login-&quot;&gt;4. OAuth / OpenID Connect (Social Login)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;OAuth 2.0&lt;/strong&gt; is an authorization framework that lets an application obtain limited access to resources on behalf of a user. It doesn't, by itself, define how an application verifies the user's identity.&lt;/p&gt;
&lt;p&gt;For &quot;Sign in with Google,&quot; &quot;Sign in with Microsoft,&quot; and similar login flows, applications commonly use &lt;strong&gt;OpenID Connect (OIDC)&lt;/strong&gt; — an identity layer built on top of OAuth 2.0. The identity provider authenticates the user and returns an &lt;strong&gt;ID token&lt;/strong&gt; containing claims about that authentication.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;User clicks &quot;Sign in with GitHub&quot;
        ↓
Redirected to GitHub
        ↓
User authenticates and approves access
        ↓
GitHub redirects back with a code
        ↓
Your server exchanges the code for tokens
        ↓
Your server validates the OIDC identity information
        ↓
Your app creates its own session
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is why OAuth is so popular for beginners and production apps alike — you outsource the hardest parts (password storage, breach risk, 2FA) to a provider that already does it well.&lt;/p&gt;
&lt;h4 id=&quot;5-multi-factor-authentication-mfa-2fa-&quot;&gt;5. Multi-Factor Authentication (MFA / 2FA)&lt;/h4&gt;
&lt;p&gt;MFA requires &lt;strong&gt;more than one&lt;/strong&gt; proof of identity — typically &quot;something you know&quot; (a password) plus &quot;something you have&quot; (a phone, an authenticator app code, a hardware key). Even if a password leaks, the attacker still can't get in without the second factor.&lt;/p&gt;
&lt;h4 id=&quot;6-passwordless-authentication-magic-links-amp-passkeys-&quot;&gt;6. Passwordless Authentication (Magic Links &amp;amp; Passkeys)&lt;/h4&gt;
&lt;p&gt;Two increasingly common alternatives to passwords:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Magic links&lt;/strong&gt; — the user enters their email, gets a one-time sign-in link, and clicking it logs them in. No password to remember or leak.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Passkeys (WebAuthn)&lt;/strong&gt; — device-based authentication using Face ID, Windows Hello, or a hardware security key. No shared secret exists to steal in the first place, which makes passkeys resistant to phishing in a way passwords fundamentally aren't.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;a-minimal-authentication-flow-in-next-js&quot;&gt;A Minimal Authentication Flow in Next.js&lt;/h3&gt;
&lt;p&gt;Here's a simplified but realistic login flow using &lt;strong&gt;Next.js 16&lt;/strong&gt;, &lt;strong&gt;TypeScript&lt;/strong&gt;, and session cookies — the same core pattern used by most production apps, just without the database wiring.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// actions/login.ts
&quot;use server&quot;;

import { redirect } from &quot;next/navigation&quot;;
import { verifyPassword } from &quot;@/lib/auth/hash-password&quot;;
import { createSession } from &quot;@/lib/auth/session&quot;;
import { z } from &quot;zod&quot;;

const loginSchema = z.object({
  email: z.email(),
  password: z.string().min(8),
});

export async function login(prevState: unknown, formData: FormData) {
  const parsed = loginSchema.safeParse({
    email: formData.get(&quot;email&quot;),
    password: formData.get(&quot;password&quot;),
  });

  if (!parsed.success) {
    return { error: &quot;Enter a valid email and password.&quot; };
  }

  // Look up the user in your database (pseudocode)
  const user = await db.user.findUnique({ where: { email: parsed.data.email } });

  if (!user || !(await verifyPassword(parsed.data.password, user.passwordHash))) {
    // Same generic error for both cases — don't reveal which one was wrong
    return { error: &quot;Invalid email or password.&quot; };
  }

  const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days
  await createSession(user.id, expiresAt);

  redirect(&quot;/dashboard&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;What's happening here:&lt;/strong&gt; the form data is validated with Zod before touching the database — never trust raw input. Notice the login failure returns the &lt;strong&gt;same generic message&lt;/strong&gt; whether the email doesn't exist or the password is wrong; revealing which one failed makes it easier for attackers to enumerate valid accounts. A working session gets created only after the password check succeeds, then the user is redirected.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;Common Mistake:&lt;/strong&gt; Checking authentication only in proxy and assuming that's enough. Proxy-level checks are a good UX shortcut (fast redirect before a page even loads), but sensitive Server Actions, Route Handlers, and data-access functions should independently verify authentication and authorization rather than relying solely on &lt;code&gt;proxy.ts&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Building this by hand is a great way to learn, but most production apps reach for a maintained library instead — see &lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/how-to-add-nextauth-in-nextjs-16&quot;&gt;How to Add Auth.js (NextAuth v5) in Next.js 16&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/how-to-add-clerk-authentication-in-nextjs-16&quot;&gt;Add Clerk Authentication to Next.js 16&lt;/a&gt;, or (if you're already on Supabase) &lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/supabase-auth-vs-firebase-auth&quot;&gt;Supabase Auth vs Firebase Auth&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;common-authentication-mistakes-to-avoid&quot;&gt;Common Authentication Mistakes to Avoid&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Storing plain-text passwords&lt;/strong&gt; — always hash with Argon2id or bcrypt, never store or log the raw password&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Storing a predictable value as the session cookie&lt;/strong&gt; — the cookie should be a random, unguessable reference, never the raw user ID or anything else an attacker could construct&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weak or reused session/JWT secrets&lt;/strong&gt; — generate a long, random secret and never commit it to git&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Revealing which field was wrong&lt;/strong&gt; — &quot;Invalid email&quot; vs. &quot;Invalid password&quot; tells attackers an account exists&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skipping rate limiting&lt;/strong&gt; — without it, attackers can brute-force passwords with unlimited attempts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Relying only on client-side checks&lt;/strong&gt; — a hidden button or disabled form field doesn't stop a direct API request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forgetting to expire sessions/tokens&lt;/strong&gt; — every session and token should have a sane expiry, not live forever&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;frequently-asked-questions&quot;&gt;Frequently Asked Questions&lt;/h3&gt;
&lt;h4 id=&quot;what-is-the-difference-between-authentication-and-authorization-&quot;&gt;What is the difference between authentication and authorization?&lt;/h4&gt;
&lt;p&gt;Authentication confirms &lt;em&gt;who&lt;/em&gt; someone is (logging in). Authorization decides &lt;em&gt;what&lt;/em&gt; they're allowed to do once they're recognized (permissions, roles, access control). You always need authentication before authorization can happen.&lt;/p&gt;
&lt;h4 id=&quot;is-a-login-system-the-same-thing-as-authentication-&quot;&gt;Is a login system the same thing as authentication?&lt;/h4&gt;
&lt;p&gt;Yes — a login system is simply the implementation of authentication. Whatever mechanism confirms a user's identity (password, OAuth, passkey) is functioning as authentication, regardless of what you call the feature.&lt;/p&gt;
&lt;h4 id=&quot;what-s-the-difference-between-sessions-and-jwts-&quot;&gt;What's the difference between sessions and JWTs?&lt;/h4&gt;
&lt;p&gt;A session stores authentication/session state on the server and gives the browser only a random reference ID, which allows instant revocation. A JWT stores the identity data directly inside a signed token the server can verify without a database lookup, trading easy revocation for less server-side state.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Keep JWT minimal — a JWT should not be treated as a general-purpose user profile or database replacement.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;do-i-need-a-library-to-build-authentication-or-can-i-build-it-myself-&quot;&gt;Do I need a library to build authentication, or can I build it myself?&lt;/h4&gt;
&lt;p&gt;You can build basic password + session authentication yourself using the patterns above, but for anything production-facing, an established library (&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/how-to-add-nextauth-in-nextjs-16&quot;&gt;Auth.js&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/how-to-add-clerk-authentication-in-nextjs-16&quot;&gt;Clerk&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/authjs-vs-better-auth&quot;&gt;Better Auth&lt;/a&gt;, or your database provider's built-in auth) is strongly recommended — they handle edge cases like token rotation, breach detection, and secure defaults that are easy to get subtly wrong by hand.&lt;/p&gt;
&lt;h4 id=&quot;is-two-factor-authentication-2fa-actually-necessary-&quot;&gt;Is two-factor authentication (2FA) actually necessary?&lt;/h4&gt;
&lt;p&gt;For anything holding sensitive data — financial info, personal messages, admin access — yes. 2FA dramatically reduces account takeover risk even when a password leaks, since the attacker also needs the second factor.&lt;/p&gt;
&lt;h4 id=&quot;are-passkeys-better-than-passwords-&quot;&gt;Are passkeys better than passwords?&lt;/h4&gt;
&lt;p&gt;Generally yes, from a security standpoint — passkeys are phishing-resistant because there's no shared secret to trick someone into revealing. Adoption is still growing, though, so most apps currently offer passkeys as an &lt;em&gt;option&lt;/em&gt; alongside passwords rather than a full replacement.&lt;/p&gt;
&lt;h3 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h3&gt;
&lt;p&gt;Authentication boils down to one question — &quot;who are you?&quot; — answered through a password, a token, a device, or some combination of them. Once you separate authentication from authorization, the ecosystem becomes easier to understand: passwords and passkeys are authentication mechanisms, sessions and JWTs are ways to maintain authenticated state, and OAuth/OIDC provide delegated authorization and federated identity.&lt;/p&gt;
&lt;p&gt;If you're building this in a real Next.js app, the next logical steps are wiring up role-based authorization on top of your authentication layer, and deciding between session-based and JWT-based auth based on whether instant revocation matters for your product.&lt;/p&gt;
&lt;h3 id=&quot;continue-learning&quot;&gt;Continue Learning&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/session-vs-jwt-for-nextjs-16&quot;&gt;Session vs JWT: Which Should You Use in Next.js 16?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/role-based-authentication-nextjs-16&quot;&gt;Role-Based Access Control (RBAC) in Next.js 16 (2026 Beginner's Guide)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/authjs-vs-better-auth&quot;&gt;Auth.js vs Better Auth in 2026: Which Should You Use?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;/https://devstacked.tech/blog/clerk-vs-nextauth-2026&quot;&gt;NextAuth (Auth.js) vs Clerk in 2026: Compared&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/blog/supabase-auth-vs-firebase-auth&quot;&gt;Supabase Auth vs Firebase Auth in 2026&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;free-developer-tools&quot;&gt;Free Developer Tools&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/tools/zod-schema-generator&quot;&gt;Zod Schema Generator&lt;/a&gt; — generate a validated schema for your role/permission payloads straight from a JSON sample or TypeScript interface, including a ready-to-use Server Action.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devstacked.tech/tools/rls-policy-generator&quot;&gt;Supabase RLS Policy Generator&lt;/a&gt; — pair this guide's application-level RBAC with database-level Row Level Security. Generates owner-only, team, and admin-override policies with the &lt;code&gt;auth.uid()&lt;/code&gt; performance wrapper built in.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25969/what-is-authentication-a-beginners-guide-2026</guid>
<pubDate>Tue, 01 Sep 2026 05:07:56 +0000</pubDate>
</item>
<item>
<title>How a Dead Polyfill.io Link Got My Entire GitHub Pages Domain Flagged as Dangerous</title>
<link>https://coderlegion.com/22868/how-a-dead-polyfill-io-link-got-my-entire-github-pages-domain-flagged-as-dangerous</link>
<description>&lt;p&gt;I recently published a digital anthology project at &lt;code&gt;nevmenandr.github.io/WLA&lt;/code&gt;.&lt;br&gt;
The setup was smooth – I used &lt;strong&gt;Obsidian&lt;/strong&gt; with the excellent&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/jobindjohn/obsidian-publish-mkdocs/&quot;&gt;obsidian-publish-mkdocs&lt;/a&gt; plugin to generate a clean, documentation‑style site.  &lt;/p&gt;
&lt;p&gt;Everything worked beautifully… until Google Chrome started showing a bright red warning:&lt;br&gt;
&lt;strong&gt;&quot;Deceptive site ahead&quot;&lt;/strong&gt; – not just for my new project, but for &lt;strong&gt;every&lt;/strong&gt; site hosted under my &lt;code&gt;nevmenandr.github.io&lt;/code&gt; domain.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11879479001821455838&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-culprit-a-single-innocent-looking-script-tag&quot;&gt;The Culprit: A Single, Innocent‑Looking Script Tag&lt;/h3&gt;
&lt;p&gt;While inspecting the generated HTML, I noticed lines where the &lt;code&gt;polyfill.io&lt;/code&gt; link caught my eye. I tried visiting it directly (the full URL with &lt;code&gt;/v3/polyfill.min.js?features=es6&lt;/code&gt; on the polyfill.io domain).&lt;br&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;401 Authorization Required&lt;/code&gt; – a dead service.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;a-critical-red-flag-the-repository-is-stale&quot;&gt;A Critical Red Flag: The Repository Is Stale&lt;/h3&gt;
&lt;p&gt;Digging deeper, I realised that the &lt;code&gt;obsidian-publish-mkdocs&lt;/code&gt; repository on GitHub &lt;strong&gt;has not been updated for over two years&lt;/strong&gt;.  &lt;/p&gt;
&lt;p&gt;This is a classic warning sign:  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The plugin still bundles legacy dependencies that were once free but have since changed hands.  &lt;/li&gt;
&lt;li&gt;The maintainer hasn't patched or removed deprecated resources.  &lt;/li&gt;
&lt;li&gt;No security audits or dependency refreshes have occurred in that time.  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While the tool works brilliantly out‑of‑the‑box, &lt;strong&gt;it's effectively unmaintained&lt;/strong&gt; – and that's a risk you inherit as a user.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-triggered-google-s-safe-browsing&quot;&gt;Why This Triggered Google's Safe Browsing&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;polyfill.io&lt;/code&gt; used to be a free, trusted CDN. But the domain was sold to a new owner who now requires payment and returns &lt;strong&gt;401&lt;/strong&gt; (or even malicious redirects) for unauthenticated requests.&lt;/p&gt;
&lt;p&gt;Google's crawler saw that my site included a script from a domain that now behaves suspiciously – and &lt;strong&gt;flagged the entire &lt;code&gt;nevmenandr.github.io&lt;/code&gt; origin&lt;/strong&gt; as a potential threat. This is a domain‑level penalty, meaning &lt;strong&gt;all&lt;/strong&gt; my other GitHub Pages projects were blocked too.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-fix-step-by-step-recovery-guide&quot;&gt;The Fix: Step‑by‑Step Recovery Guide&lt;/h3&gt;
&lt;p&gt;If you ever face this, here's the exact process that got my site back online within ~24 hours.&lt;/p&gt;
&lt;h4 id=&quot;1-remove-the-offending-resource&quot;&gt;1. Remove the Offending Resource&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Locate and delete&lt;/strong&gt; the problematic script tag from your HTML template.&lt;br&gt;
In my case, I disabled the polyfill inclusion in the MkDocs theme config (or simply removed the line from the generated &lt;code&gt;index.html&lt;/code&gt;).  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rebuild and redeploy&lt;/strong&gt; your site to GitHub Pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;2-verify-the-site-is-clean&quot;&gt;2. Verify the Site is Clean&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Visit your URL in a private/incognito window to confirm the red warning still appears (it will, until Google re‑crawls).&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;Google Transparency Report&lt;/strong&gt; tool (search for &quot;Google Transparency Report&quot; and then enter your domain in the Safe Browsing section) to check your domain's current status.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;3-claim-ownership-in-google-search-console&quot;&gt;3. Claim Ownership in Google Search Console&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Google Search Console&lt;/strong&gt; (search for it – it's Google's official tool for webmasters).&lt;/li&gt;
&lt;li&gt;Add your site as a &lt;strong&gt;property&lt;/strong&gt; (use the URL‑prefix type, e.g., &lt;code&gt;https://nevmenandr.github.io/WLA/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Verify ownership using the recommended method – for GitHub Pages, the easiest is:&lt;ol&gt;
&lt;li&gt;Download the HTML verification file from Search Console.&lt;/li&gt;
&lt;li&gt;Add it to the root of your repository (&lt;code&gt;/path/to/your/repo/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Commit and push – GitHub Pages will serve it automatically.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;4-request-a-review&quot;&gt;4. Request a Review&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;In Search Console, navigate to &lt;strong&gt;Security &amp;amp; Manual Actions&lt;/strong&gt; → &lt;strong&gt;Security Issues&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If the warning is still active, you'll see a &lt;strong&gt;&quot;Request Review&quot;&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;In the review form:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Explain&lt;/strong&gt; clearly that you've removed the external resource that caused the issue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mention&lt;/strong&gt; that you've scanned your site for any other suspicious content.&lt;/li&gt;
&lt;li&gt;If you use any other external scripts, list them and confirm they are safe.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;5-wait-usually-24-48-hours-&quot;&gt;5. Wait (Usually 24‑48 Hours)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Google's team will review your request. In my case, the domain was cleared in about &lt;strong&gt;24 hours&lt;/strong&gt;.  &lt;/li&gt;
&lt;li&gt;You can monitor the status in Search Console – it will change from &quot;Pending&quot; to &quot;Approved&quot; or &quot;Rejected&quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=306045379319634575&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;key-lessons-for-developers&quot;&gt;Key Lessons for Developers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Third‑party scripts are liabilities.&lt;/strong&gt; Always audit CDN links – especially free ones that can change ownership or monetisation models overnight.&lt;br&gt;
Consider self‑hosting critical libraries or using well‑established CDNs with clear SLAs (e.g., cdnjs, jsdelivr).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check the maintenance status of your tools.&lt;/strong&gt;&lt;br&gt;
A plugin that hasn't seen a commit in two years is a ticking time bomb. Always review the last update date before relying on any open‑source dependency in production. If it's stale, fork it, update the dependencies yourself, or look for an actively maintained alternative.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Google Safe Browsing penalises at the domain level.&lt;/strong&gt;&lt;br&gt;
A single compromised resource can take down &lt;strong&gt;all&lt;/strong&gt; your sub‑projects, including unrelated ones. This is particularly painful on GitHub Pages where you share the &lt;code&gt;*.github.io&lt;/code&gt; namespace.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automate dependency checks.&lt;/strong&gt;&lt;br&gt;
Add a step in your CI/CD pipeline to test external URLs for HTTP 2xx responses. For example, with a simple &lt;code&gt;curl&lt;/code&gt; check:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;if ! curl -s -o /dev/null -w &quot;%{http_code}&quot; https://polyfill.io/v3/polyfill.min.js | grep -q &quot;200&quot;; then
  echo &quot;⚠️  Polyfill.io is unreachable – aborting build&quot;
  exit 1
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Know the recovery path.&lt;/strong&gt;&lt;br&gt;
Having a verified Google Search Console property is &lt;strong&gt;not optional&lt;/strong&gt; if you publish public sites. It's your only channel to communicate with Google's security team when false positives occur.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't panic – the process works.&lt;/strong&gt;&lt;br&gt;
The warning looked scary, but the fix was straightforward. The key is to act quickly, be transparent in your review request, and double‑check that no other deprecated services remain.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;final-thought&quot;&gt;Final Thought&lt;/h3&gt;
&lt;p&gt;Open‑source tools like &lt;code&gt;obsidian-publish-mkdocs&lt;/code&gt; are brilliant for productivity – but they often bundle legacy dependencies for maximum compatibility, and when a repository stagnates for years, those dependencies can rot. As developers, it's our responsibility to &lt;strong&gt;review every external asset&lt;/strong&gt; and &lt;strong&gt;check the pulse of our toolchain&lt;/strong&gt; before going live.&lt;/p&gt;
&lt;p&gt;One dead polyfill link and an unmaintained plugin cost me a day of heartache. Don't let it happen to you. 🚀&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22868/how-a-dead-polyfill-io-link-got-my-entire-github-pages-domain-flagged-as-dangerous</guid>
<pubDate>Wed, 26 Aug 2026 04:47:00 +0000</pubDate>
</item>
<item>
<title>How I Designed an Anti-Proxy Attendance System Using Dynamic QR Authentication</title>
<link>https://coderlegion.com/22897/how-i-designed-an-anti-proxy-attendance-system-using-dynamic-qr-authentication</link>
<description>&lt;p&gt;&quot;Anyone can generate a QR code. Designing one that actually prevents misuse is the hard part.&quot;&lt;/p&gt;
&lt;p&gt;When people hear &quot;QR-based attendance,&quot; the first question is usually:&lt;/p&gt;
&lt;p&gt;&quot;Can't someone just take a photo of the QR code and send it to their friend?&quot;&lt;/p&gt;
&lt;p&gt;The answer is: yes, if the system is designed poorly.&lt;/p&gt;
&lt;p&gt;That question became one of the biggest engineering challenges while building my attendance platform.&lt;/p&gt;
&lt;p&gt;Instead of thinking about QR codes as images, I started thinking about them as temporary authentication tokens.&lt;/p&gt;
&lt;p&gt;The Problem&lt;br&gt;
Most QR attendance systems work like this:&lt;/p&gt;
&lt;p&gt;Teacher generates a QR code.&lt;br&gt;
Students scan it.&lt;br&gt;
Attendance is marked.&lt;br&gt;
Simple.&lt;/p&gt;
&lt;p&gt;Unfortunately, it's also vulnerable.&lt;/p&gt;
&lt;p&gt;A student can:&lt;/p&gt;
&lt;p&gt;Photograph the QR code.&lt;br&gt;
Send it through WhatsApp.&lt;br&gt;
Another student scans it from somewhere else.&lt;br&gt;
Attendance gets recorded.&lt;br&gt;
The QR code itself becomes the vulnerability.&lt;/p&gt;
&lt;p&gt;Rethinking the QR Code&lt;br&gt;
Instead of creating a QR code that simply points to an attendance page, I designed it to behave more like a short-lived authentication token.&lt;/p&gt;
&lt;p&gt;Every QR code is generated dynamically and remains valid only for a limited period (in my implementation, I set this to 11 seconds).&lt;/p&gt;
&lt;p&gt;Once it expires, the system rejects it.&lt;/p&gt;
&lt;p&gt;This dramatically reduces the opportunity for someone to reuse an older QR code.&lt;/p&gt;
&lt;p&gt;Authentication Beyond the QR&lt;br&gt;
Scanning the QR code alone isn't enough.&lt;/p&gt;
&lt;p&gt;The backend performs additional validation before marking attendance.&lt;/p&gt;
&lt;p&gt;Rather than trusting the client, the server becomes the source of truth.&lt;/p&gt;
&lt;p&gt;This reduces opportunities for client-side manipulation and keeps the attendance workflow under server control.&lt;/p&gt;
&lt;p&gt;Designing for the Real World&lt;br&gt;
One challenge I encountered was balancing security with usability.&lt;/p&gt;
&lt;p&gt;If the QR expires too quickly, students may struggle to scan it in time.&lt;/p&gt;
&lt;p&gt;If it stays active for too long, it becomes easier to misuse.&lt;/p&gt;
&lt;p&gt;Finding the right balance required thinking about actual classroom conditions rather than only technical implementation.&lt;/p&gt;
&lt;p&gt;Lessons Learned&lt;br&gt;
One lesson stood out throughout this project:&lt;/p&gt;
&lt;p&gt;Security isn't usually one big feature.&lt;/p&gt;
&lt;p&gt;It's the result of multiple smaller decisions working together.&lt;/p&gt;
&lt;p&gt;A dynamic QR code helps.&lt;/p&gt;
&lt;p&gt;Server-side validation helps.&lt;/p&gt;
&lt;p&gt;Short-lived sessions help.&lt;/p&gt;
&lt;p&gt;Each layer reduces risk.&lt;/p&gt;
&lt;p&gt;What's Next?&lt;br&gt;
This article only covers one piece of the system.&lt;/p&gt;
&lt;p&gt;In future posts, I'll explore:&lt;/p&gt;
&lt;p&gt;Backend architecture&lt;br&gt;
Firestore data modeling&lt;br&gt;
Authentication flow&lt;br&gt;
Scaling the platform&lt;br&gt;
Engineering decisions behind the project&lt;br&gt;
Building software has taught me that writing code is only part of engineering.&lt;/p&gt;
&lt;p&gt;Designing systems that continue to work under real-world conditions is where the interesting problems begin. In this project, I used libraries like qrcode on the backend to generate dynamic QR codes and html5-qrcode on the frontend to scan and verify them efficiently, while SHA-256 hashing was used on the backend to securely generate and validate the QR token payloads.&lt;/p&gt;
&lt;p&gt;⭐ GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/siddarthpatelkama&quot;&gt;https://github.com/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;💼 LinkedIn: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/siddarthpatelkama&quot;&gt;https://www.linkedin.com/in/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22897/how-i-designed-an-anti-proxy-attendance-system-using-dynamic-qr-authentication</guid>
<pubDate>Fri, 17 Jul 2026 16:39:51 +0000</pubDate>
</item>
<item>
<title>JWT Storage: Cookies vs LocalStorage – Which Is Right for Your App?</title>
<link>https://coderlegion.com/7607/jwt-storage-cookies-vs-localstorage-which-is-right-for-your-app</link>
<description>&lt;h1&gt;JWT Storage: Cookies vs LocalStorage&lt;/h1&gt;
&lt;p&gt;Where should you store JWT tokens? This guide covers both approaches with working code examples to help you make an informed decision.&lt;/p&gt;
&lt;h2&gt;What You'll Learn&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Implement JWT authentication using HTTP-only cookies and localStorage&lt;/li&gt;
&lt;li&gt;Understand XSS and CSRF attack implications&lt;/li&gt;
&lt;li&gt;Choose the right storage method for your application&lt;/li&gt;
&lt;li&gt;Apply defense-in-depth security strategies&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Two Storage Options&lt;/h2&gt;
&lt;h3&gt;HTTP-Only Cookies&lt;/h3&gt;
&lt;p&gt;Cookies with the &lt;code&gt;httpOnly&lt;/code&gt; flag cannot be accessed by JavaScript, providing protection against XSS token theft.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key characteristics:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic: Browser sends tokens with requests&lt;/li&gt;
&lt;li&gt;JavaScript-proof: Cannot be stolen via XSS&lt;/li&gt;
&lt;li&gt;Configurable: &lt;code&gt;Secure&lt;/code&gt; and &lt;code&gt;SameSite&lt;/code&gt; flags for added security&lt;/li&gt;
&lt;li&gt;Vulnerable to CSRF without proper protection&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;LocalStorage&lt;/h3&gt;
&lt;p&gt;Tokens stored in localStorage require manual attachment to requests via JavaScript.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key characteristics:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Manual control: You decide when to send tokens&lt;/li&gt;
&lt;li&gt;JavaScript-accessible: Easy to use but vulnerable to XSS&lt;/li&gt;
&lt;li&gt;CSRF immune: Tokens aren't sent automatically&lt;/li&gt;
&lt;li&gt;Developer-managed: You handle storage and cleanup&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The Core Trade-off&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Cookies are safer from XSS but vulnerable to CSRF. LocalStorage is immune to CSRF but exposed to XSS.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Implementation: Cookies Approach&lt;/h2&gt;
&lt;h3&gt;Backend (Node.js/Express)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const express = require('express');
const jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser');

const app = express();
app.use(cookieParser());
app.use(express.json());

const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';

// Login endpoint
app.post('/api/auth/login', async (req, res) =&amp;gt; {
  const { email, password } = req.body;
  const user = await validateUser(email, password);
  
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  const token = jwt.sign(
    { userId: user.id, email: user.email },
    JWT_SECRET,
    { expiresIn: '15m' }
  );
  
  res.cookie('access_token', token, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
    maxAge: 15 * 60 * 1000
  });
  
  res.json({ success: true, user: { id: user.id, email: user.email } });
});

// Protected endpoint
app.get('/api/user/profile', (req, res) =&amp;gt; {
  const token = req.cookies['access_token'];
  
  if (!token) {
    return res.status(401).json({ error: 'Not authenticated' });
  }
  
  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    res.json({ user: decoded });
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Frontend (React)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async function handleLogin(email, password) {
  const response = await fetch('http://localhost:3000/api/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    credentials: 'include', // Critical for cookies
    body: JSON.stringify({ email, password })
  });
  
  const data = await response.json();
  if (response.ok) {
    window.location.href = '/dashboard';
  }
}

// Making authenticated requests
fetch('/api/user/profile', {
  credentials: 'include' // Browser automatically includes cookie
});&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Implementation: LocalStorage Approach&lt;/h2&gt;
&lt;h3&gt;Backend&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const express = require('express');
const jwt = require('jsonwebtoken');

const app = express();
app.use(express.json());

// Login endpoint - returns token in response body
app.post('/api/auth/login', async (req, res) =&amp;gt; {
  const { email, password } = req.body;
  const user = await validateUser(email, password);
  
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  const token = jwt.sign(
    { userId: user.id, email: user.email },
    JWT_SECRET,
    { expiresIn: '15m' }
  );
  
  res.json({ 
    success: true,
    token: token,
    user: { id: user.id, email: user.email }
  });
});

// Protected endpoint - expects Authorization header
app.get('/api/user/profile', (req, res) =&amp;gt; {
  const authHeader = req.headers.authorization;
  
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Not authenticated' });
  }
  
  const token = authHeader.substring(7);
  
  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    res.json({ user: decoded });
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Frontend&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async function handleLogin(email, password) {
  const response = await fetch('http://localhost:3000/api/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password })
  });
  
  const data = await response.json();
  
  if (response.ok) {
    localStorage.setItem('access_token', data.token);
    window.location.href = '/dashboard';
  }
}

// Making authenticated requests
const token = localStorage.getItem('access_token');
fetch('/api/user/profile', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Security Deep Dive&lt;/h2&gt;
&lt;h3&gt;XSS Attacks&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;LocalStorage:&lt;/strong&gt; If XSS occurs, attackers can trivially steal tokens with &lt;code&gt;localStorage.getItem('access_token')&lt;/code&gt; and use them from anywhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTTP-Only Cookies:&lt;/strong&gt; JavaScript cannot read the token. Attackers can make requests during XSS but cannot steal the token for later use.&lt;/p&gt;
&lt;h3&gt;CSRF Attacks&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Cookies:&lt;/strong&gt; Browsers automatically send cookies with requests, even from malicious sites. Use &lt;code&gt;SameSite&lt;/code&gt; cookies and CSRF tokens for protection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LocalStorage:&lt;/strong&gt; Immune to CSRF because tokens must be manually attached via JavaScript, which cannot access cross-origin localStorage.&lt;/p&gt;
&lt;h2&gt;Making the Decision&lt;/h2&gt;
&lt;h3&gt;Choose HTTP-Only Cookies if:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Building financial, healthcare, or high-security applications&lt;/li&gt;
&lt;li&gt;Frontend and backend share a domain&lt;/li&gt;
&lt;li&gt;Using server-side rendering (Next.js, Remix)&lt;/li&gt;
&lt;li&gt;Security is top priority&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Choose LocalStorage if:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Building a modern SPA (React, Vue, Angular)&lt;/li&gt;
&lt;li&gt;Cross-origin setup with CORS complexity&lt;/li&gt;
&lt;li&gt;Building mobile or hybrid apps&lt;/li&gt;
&lt;li&gt;Using GraphQL or custom protocols&lt;/li&gt;
&lt;li&gt;Have strong XSS prevention (CSP, security audits)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Hybrid Approach (Best Security)&lt;/h3&gt;
&lt;p&gt;Store short-lived access tokens in memory (5-15 min) and long-lived refresh tokens in HTTP-only cookies. This combines the benefits of both approaches.&lt;/p&gt;
&lt;h2&gt;Best Practices&lt;/h2&gt;
&lt;h3&gt;Universal (Do These Always)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Always use HTTPS in production&lt;/li&gt;
&lt;li&gt;Keep tokens short-lived (15-30 minutes)&lt;/li&gt;
&lt;li&gt;Implement token rotation&lt;/li&gt;
&lt;li&gt;Build proper logout (clear client + invalidate server)&lt;/li&gt;
&lt;li&gt;Log authentication events and monitor patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;XSS Prevention&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Implement Content Security Policy (CSP)&lt;/li&gt;
&lt;li&gt;Sanitize user input with libraries like DOMPurify&lt;/li&gt;
&lt;li&gt;Avoid dangerous DOM manipulation (&lt;code&gt;innerHTML&lt;/code&gt;, &lt;code&gt;eval&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Keep dependencies updated (&lt;code&gt;npm audit&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CSRF Prevention (For Cookies)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;SameSite&lt;/code&gt; cookie attribute (&lt;code&gt;'strict'&lt;/code&gt; or &lt;code&gt;'lax'&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Implement CSRF tokens for sensitive operations&lt;/li&gt;
&lt;li&gt;Validate request origins&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Troubleshooting&lt;/h2&gt;
&lt;h3&gt;Cookies not sent cross-origin&lt;/h3&gt;
&lt;p&gt;Backend: Set CORS with &lt;code&gt;credentials: true&lt;/code&gt;. Frontend: Use &lt;code&gt;credentials: 'include'&lt;/code&gt; in fetch.&lt;/p&gt;
&lt;h3&gt;SameSite=None not working&lt;/h3&gt;
&lt;p&gt;Requires &lt;code&gt;Secure&lt;/code&gt; flag (HTTPS only).&lt;/p&gt;
&lt;h3&gt;Tokens not syncing across tabs (localStorage)&lt;/h3&gt;
&lt;p&gt;Listen to &lt;code&gt;storage&lt;/code&gt; events to detect changes in other tabs.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;HTTP-only cookies&lt;/strong&gt; protect against XSS token theft but require CSRF protection. &lt;strong&gt;LocalStorage&lt;/strong&gt; is immune to CSRF but vulnerable to XSS. The &lt;strong&gt;hybrid approach&lt;/strong&gt; offers the best security.&lt;/p&gt;
&lt;p&gt;For high-security apps, use cookies or hybrid. For modern SPAs with strong XSS prevention, localStorage is acceptable. Remember: security is a process, not a checkbox.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;For a more detailed version of this guide with additional examples, troubleshooting scenarios, and in-depth explanations, visit: &lt;a rel=&quot;nofollow&quot; href=&quot;https://stackinsight.dev/blog/jwt-storage-cookies-vs-localstorage-which-is-right-for-your-app/&quot;&gt;https://stackinsight.dev/blog/jwt-storage-cookies-vs-localstorage-which-is-right-for-your-app/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
<category>App Security</category>
<guid isPermaLink="true">https://coderlegion.com/7607/jwt-storage-cookies-vs-localstorage-which-is-right-for-your-app</guid>
<pubDate>Sat, 22 Nov 2025 05:19:56 +0000</pubDate>
</item>
</channel>
</rss>