<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ozan</title>
    <description>The latest articles on Forem by Ozan (@ozankozan).</description>
    <link>https://forem.com/ozankozan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3221679%2Fbc381285-5b15-4ffd-a36a-2ace554cf324.jpeg</url>
      <title>Forem: Ozan</title>
      <link>https://forem.com/ozankozan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ozankozan"/>
    <language>en</language>
    <item>
      <title>Getting Real Client IPs Behind Cloudflare Proxy in Laravel</title>
      <dc:creator>Ozan</dc:creator>
      <pubDate>Thu, 19 Feb 2026 13:58:00 +0000</pubDate>
      <link>https://forem.com/ozankozan/getting-real-client-ips-behind-cloudflare-or-other-proxy-in-laravel-1j6i</link>
      <guid>https://forem.com/ozankozan/getting-real-client-ips-behind-cloudflare-or-other-proxy-in-laravel-1j6i</guid>
      <description>&lt;p&gt;If your server sits behind Cloudflare with a firewall that only allows&lt;br&gt;
  Cloudflare IP ranges, &lt;code&gt;$request-&amp;gt;ip()&lt;/code&gt; returns the Cloudflare proxy IP,&lt;br&gt;
  not the real client IP.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why X-Forwarded-For Isn't Enough
&lt;/h2&gt;

&lt;p&gt;The common advice is to use &lt;code&gt;trustProxies(at: '*')&lt;/code&gt; and read&lt;br&gt;
  &lt;code&gt;X-Forwarded-For&lt;/code&gt;. The problem: users can inject fake entries into this&lt;br&gt;
  header before it reaches Cloudflare, and Cloudflare appends rather than&lt;br&gt;
  replaces:&lt;/p&gt;

&lt;p&gt;User sends:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;X-Forwarded-For: 1.1.1.1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Cloudflare appends its own, resulting in:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;X-Forwarded-For: 1.1.1.1, 26.55.52.xx, 172.70.216.62&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;trustProxies(at: '*')&lt;/code&gt;, Laravel trusts everything and returns the&lt;br&gt;
  leftmost IP — the spoofed one.&lt;/p&gt;
&lt;h2&gt;
  
  
  CF-Connecting-IP Is the Right Header
&lt;/h2&gt;

&lt;p&gt;Cloudflare sets &lt;code&gt;CF-Connecting-IP&lt;/code&gt; to the actual connecting client IP and&lt;br&gt;
  strips any user-supplied header with the same name. It cannot be spoofed.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Fix: A Simple Middleware
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Middleware/CloudflareRealIp.php&lt;/span&gt;

  &lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Middleware&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Closure&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\Request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\HttpFoundation\Response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CloudflareRealIp&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nv"&gt;$realIp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'CF-Connecting-IP'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$realIp&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;filter_var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$realIp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;FILTER_VALIDATE_IP&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REMOTE_ADDR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$realIp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
              &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'X-Forwarded-For'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$realIp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Register it in bootstrap/app.php — prepend ensures it runs before&lt;br&gt;
  Laravel's TrustProxies middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-&amp;gt;withMiddleware(function (Middleware $middleware): void {
      $middleware-&amp;gt;prepend(CloudflareRealIp::class);
      $middleware-&amp;gt;trustProxies(at: '*');
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why Not Check Cloudflare IP Ranges?&lt;/p&gt;

&lt;p&gt;You might want to verify that the request actually comes from Cloudflare&lt;br&gt;
  before trusting CF-Connecting-IP. However, if your firewall already&lt;br&gt;
  blocks all non-Cloudflare traffic at the network level, this check is&lt;br&gt;
  redundant — and it breaks when a load balancer or internal proxy sits&lt;br&gt;
  between Cloudflare and your app (REMOTE_ADDR becomes an internal IP like&lt;br&gt;
  10.0.1.6 instead of a Cloudflare IP).&lt;/p&gt;

&lt;p&gt;If you don't have a firewall, add the range check — but if you do, skip it.&lt;/p&gt;

&lt;p&gt;Verify It Works&lt;/p&gt;

&lt;p&gt;Add a quick debug route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Route::get('/ip-check', function (Request $request) {
      return [
          'request_ip'      =&amp;gt; $request-&amp;gt;ip(),
          'remote_addr'     =&amp;gt; $request-&amp;gt;server('REMOTE_ADDR'),
          'x_forwarded_for' =&amp;gt; $request-&amp;gt;header('X-Forwarded-For'),
          'cf_connecting_ip'=&amp;gt; $request-&amp;gt;header('CF-Connecting-IP'),
      ];
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before the fix you'd see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  {
    "request_ip": "172.70.216.62",
    "cf_connecting_ip": "26.55.52.xx"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the fix, request_ip matches &lt;code&gt;cf_connecting_ip&lt;/code&gt; the real client IP.&lt;/p&gt;

&lt;p&gt;Remove the debug route before going to production :))&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>cloudflare</category>
      <category>php</category>
      <category>proxy</category>
    </item>
    <item>
      <title>Understanding Content Security Policy (CSP)</title>
      <dc:creator>Ozan</dc:creator>
      <pubDate>Wed, 19 Nov 2025 17:14:37 +0000</pubDate>
      <link>https://forem.com/ozankozan/understanding-content-security-policy-csp-1hh5</link>
      <guid>https://forem.com/ozankozan/understanding-content-security-policy-csp-1hh5</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CSP is an HTTP header&lt;/strong&gt; that tells browsers "&lt;em&gt;only run scripts from these trusted sources.&lt;/em&gt;" It's your defense against XSS attacks where hackers inject malicious JavaScript into your site. Start with &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt; to see what breaks, fix inline scripts by adding &lt;code&gt;nonce&lt;/code&gt; attributes, whitelist external domains you trust, then enforce with &lt;code&gt;Content-Security-Policy&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Test using &lt;a href="https://csp-evaluator.withgoogle.com/" rel="noopener noreferrer"&gt;Google's CSP Evaluator&lt;/a&gt;. Avoid &lt;code&gt;'unsafe-inline'&lt;/code&gt; and &lt;code&gt;'unsafe-eval'&lt;/code&gt; like the plague—they defeat the whole purpose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvog0qg4svnkbq27ckbzz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvog0qg4svnkbq27ckbzz.png" alt=" " width="800" height="545"&gt;&lt;/a&gt;&lt;br&gt;
Image Source: &lt;a href="https://www.writesoftwarewell.com/content-security-policy/" rel="noopener noreferrer"&gt;https://www.writesoftwarewell.com/content-security-policy/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;CSP (Content Security Policy) is basically a bouncer for your website. It tells the browser, "&lt;strong&gt;Hey, only let in scripts, images or fonts from these approved sources.&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;Why should you care? Because XSS (Cross-Site Scripting) attacks are everywhere, and CSP is one of the best ways to protect your users from them.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Content Security Policy?
&lt;/h2&gt;

&lt;p&gt;Think of your website as a nightclub. Without CSP, anyone can walk in and start doing whatever they want. With CSP, you're the bouncer with a guest list—you explicitly tell the browser which scripts, styles, and other resources are allowed to run.&lt;/p&gt;

&lt;p&gt;Here's the basic idea: instead of the browser trusting everything by default, you flip the script. You say "only trust content from these specific places, and block everything else."&lt;/p&gt;
&lt;h3&gt;
  
  
  A Simple Example
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;default-src 'self'&lt;/code&gt; - "By default, only load stuff from my own domain"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;script-src 'self' https://trusted-cdn.com&lt;/code&gt; - "For JavaScript specifically, allow my domain and this one CDN I trust"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If someone tries to inject a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag pointing to &lt;code&gt;evil-hacker-site.com&lt;/code&gt;, the browser just... won't run it. Pretty neat, righ&lt;/p&gt;
&lt;h3&gt;
  
  
  It's Not Just About XSS
&lt;/h3&gt;

&lt;p&gt;CSP also protects against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clickjacking&lt;/strong&gt; - Someone embedding your site in an iframe to trick users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mixed content attacks&lt;/strong&gt; - Loading HTTP resources on HTTPS pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Malicious iframes&lt;/strong&gt; - Unexpected content being loaded into your page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unauthorized data exfiltration&lt;/strong&gt; - Scripts trying to send data to random domains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as security insurance. Your input validation might have a bug. Your sanitization library might miss something. CSP is there as a backup.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Nonce Trick: When You Actually Need Inline Scripts
&lt;/h2&gt;

&lt;p&gt;Okay, so you've got some inline JavaScript that you &lt;em&gt;really&lt;/em&gt; need to keep inline. Maybe it's server-rendered data, or a legacy widget that's hard to refactor. What do you do?&lt;/p&gt;

&lt;p&gt;Enter the &lt;code&gt;nonce&lt;/code&gt; attribute. It's basically a one-time password for your inline scripts.&lt;/p&gt;
&lt;h3&gt;
  
  
  How Nonces Work
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;For every page request, your server generates a random, unguessable string:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="c1"&gt;// Result: something like "dGhpcyBpcyBhIG5v=="&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Add this nonce to your CSP header:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;   Content-Security-Policy: script-src 'self' 'nonce-dGhpcyBpcyBhIG5v=='
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Add the same nonce to your inline script:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;nonce=&lt;/span&gt;&lt;span class="s"&gt;"dGhpcyBpcyBhIG5v=="&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This inline script is allowed!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;The browser checks: "Does the nonce in the script match the nonce in the CSP header? Yes? Cool, run it."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; An attacker can't inject a script with the correct nonce because they don't know what the random value is. It changes with every page load.&lt;/p&gt;
&lt;h3&gt;
  
  
  Practical Example: Server-Side Rendering
&lt;/h3&gt;

&lt;p&gt;Let's say you're using Node.js with Express and EJS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Generate a random nonce&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Set CSP header with the nonce&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Security-Policy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;`script-src 'self' 'nonce-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Pass the nonce to your template&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your EJS template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;nonce=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;%= nonce %&amp;gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User data loaded:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nonce changes on every request, so even if an attacker sees it once, it's useless for their attack.&lt;/p&gt;

&lt;h3&gt;
  
  
  Important: What Nonces DON'T Protect Against
&lt;/h3&gt;

&lt;p&gt;Nonces are great, but they won't save you from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;XSS vulnerabilities in your inline code itself&lt;/li&gt;
&lt;li&gt;Scripts loaded from whitelisted domains that are compromised&lt;/li&gt;
&lt;li&gt;JavaScript that's already on the page and gets called with malicious input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They're a way to say "this inline script is mine," not "this inline script is safe."&lt;/p&gt;

&lt;h2&gt;
  
  
  Key CSP Directives
&lt;/h2&gt;

&lt;p&gt;Here are the most important directives you should know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;default-src&lt;/code&gt;&lt;/strong&gt;: Fallback for other directives&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;script-src&lt;/code&gt;&lt;/strong&gt;: Controls where scripts can be loaded from&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;style-src&lt;/code&gt;&lt;/strong&gt;: Controls stylesheets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;img-src&lt;/code&gt;&lt;/strong&gt;: Controls images&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;connect-src&lt;/code&gt;&lt;/strong&gt;: Controls AJAX, WebSocket, and fetch requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;font-src&lt;/code&gt;&lt;/strong&gt;: Controls fonts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;frame-src&lt;/code&gt;&lt;/strong&gt;: Controls frames and iframes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;frame-ancestors&lt;/code&gt;&lt;/strong&gt;: Controls where your page can be embedded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;base-uri&lt;/code&gt;&lt;/strong&gt;: Controls the &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;form-action&lt;/code&gt;&lt;/strong&gt;: Controls form submission targets&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common CSP Values
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'self'&lt;/code&gt;&lt;/strong&gt;: Same origin as the document&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'none'&lt;/code&gt;&lt;/strong&gt;: Block everything&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'unsafe-inline'&lt;/code&gt;&lt;/strong&gt;: Allow inline scripts/styles (avoid if possible!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'unsafe-eval'&lt;/code&gt;&lt;/strong&gt;: Allow eval() and similar (avoid if possible!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;https:&lt;/code&gt;&lt;/strong&gt;: Allow any HTTPS resource&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;https://example.com&lt;/code&gt;&lt;/strong&gt;: Allow specific domain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'nonce-{random}'&lt;/code&gt;&lt;/strong&gt;: Allow specific inline script with matching nonce&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'sha256-{hash}'&lt;/code&gt;&lt;/strong&gt;: Allow specific inline script with matching hash&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Actually Implement CSP (Without Breaking Everything)
&lt;/h2&gt;

&lt;p&gt;Here's the thing: you can't just slap a strict CSP on your site and call it a day. If you do, everything will break and your users will revolt. Trust me, many developers have learned this the hard way.&lt;/p&gt;

&lt;p&gt;Here's the smart approach:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Start in Report-Only Mode
&lt;/h3&gt;

&lt;p&gt;Use the &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt; header first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-violations
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is genius because the browser will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not block anything&lt;/strong&gt; - Your site works normally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report violations&lt;/strong&gt; - You get a JSON report of what would have been blocked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You're basically doing a dry run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Set Up a Violation Report Endpoint
&lt;/h3&gt;

&lt;p&gt;Create an endpoint to catch these reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example Express.js endpoint&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/csp-violations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/csp-report&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CSP would have blocked:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Maybe save to database for analysis&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or just use a service like &lt;a href="https://report-uri.com" rel="noopener noreferrer"&gt;report-uri.com&lt;/a&gt; that visualizes everything for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Fix Your Code Based on Reports
&lt;/h3&gt;

&lt;p&gt;You'll probably discover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inline scripts everywhere (especially in legacy code)&lt;/li&gt;
&lt;li&gt;Scripts loaded from CDNs you forgot about&lt;/li&gt;
&lt;li&gt;Third-party widgets that load their own stuff&lt;/li&gt;
&lt;li&gt;Inline event handlers like &lt;code&gt;onclick="doThing()"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Time to refactor! Move inline scripts to external files, or use &lt;code&gt;nonce&lt;/code&gt; attributes (more on that in a sec).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Switch to Enforcement Mode
&lt;/h3&gt;

&lt;p&gt;Once the reports stop coming (or you've whitelisted legitimate sources), change from &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt; to &lt;code&gt;Content-Security-Policy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now you're actually protecting your users!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Keep Both Headers Running
&lt;/h3&gt;

&lt;p&gt;Pro tip: Keep report-only mode running even after you enforce. Use it to test stricter policies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: default-src 'self'
Content-Security-Policy-Report-Only: default-src 'none'; script-src 'self'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way you can see what a stricter policy would break before you deploy it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Your CSP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using Google's CSP Evaluator
&lt;/h3&gt;

&lt;p&gt;Google provides an excellent tool for testing your CSP: &lt;a href="https://csp-evaluator.withgoogle.com/" rel="noopener noreferrer"&gt;CSP Evaluator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simply paste your CSP header, and it will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Highlight security issues&lt;/li&gt;
&lt;li&gt;Suggest improvements&lt;/li&gt;
&lt;li&gt;Explain potential vulnerabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Manual Testing
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check browser console&lt;/strong&gt;: CSP violations appear in the console&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test inline scripts&lt;/strong&gt;: Try adding an inline script—it should be blocked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test external resources&lt;/strong&gt;: Try loading resources from non-whitelisted domains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use browser DevTools&lt;/strong&gt;: Network tab shows blocked resources&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- This should be blocked with a proper CSP --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is an inline script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- This should also be blocked --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://untrusted-domain.com/image.jpg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common CSP Mistakes (And How to Fix Them)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Using 'unsafe-inline' (The Cardinal Sin)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: script-src 'self' 'unsafe-inline'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this is terrible:&lt;/strong&gt; This is like installing a security door and then leaving it wide open. It allows &lt;em&gt;any&lt;/em&gt; inline script to run, including the ones attackers inject. You just defeated the entire purpose of CSP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Use &lt;code&gt;nonce&lt;/code&gt; attributes instead (see below) or move your scripts to external files.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Using 'unsafe-eval' (Also Bad)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: script-src 'self' 'unsafe-eval'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this sucks:&lt;/strong&gt; This allows &lt;code&gt;eval()&lt;/code&gt;, &lt;code&gt;new Function()&lt;/code&gt;, and similar code execution methods. Attackers love this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Refactor your code. Modern JavaScript rarely needs &lt;code&gt;eval()&lt;/code&gt;. If you think you need it, you probably don't.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Whitelisting Everything with Wildcards&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: script-src *
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What you just did:&lt;/strong&gt; "Allow scripts from literally anywhere on the internet"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Be specific. List the exact domains you trust.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Forgetting About base-uri&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you don't set &lt;code&gt;base-uri&lt;/code&gt;, attackers can inject &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tags to hijack relative URLs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Always include &lt;code&gt;base-uri 'self'&lt;/code&gt; or &lt;code&gt;base-uri 'none'&lt;/code&gt; in your policy.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Whitelisting Entire CDNs&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: script-src 'self' https://cdnjs.cloudflare.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; CDNs host thousands of libraries. An attacker can use any of them, including old vulnerable versions of jQuery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Use Subresource Integrity (SRI) hashes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"&lt;/span&gt;
        &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK"&lt;/span&gt;
        &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the browser will only run it if the hash matches.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. &lt;strong&gt;Trusting JSONP Endpoints&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you whitelist a domain that has JSONP endpoints, attackers can abuse them to bypass your CSP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Don't whitelist domains with JSONP. Use CORS-enabled APIs instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example: Strict CSP
&lt;/h2&gt;

&lt;p&gt;Here's an example of a strict, production-ready CSP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: 
  default-src 'none';
  script-src 'self' 'nonce-{random}';
  style-src 'self' 'nonce-{random}';
  img-src 'self' https://trusted-images.com;
  font-src 'self';
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';
  upgrade-insecure-requests;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This policy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocks everything by default&lt;/li&gt;
&lt;li&gt;Only allows scripts and styles from same origin with nonces&lt;/li&gt;
&lt;li&gt;Restricts images to same origin and a trusted CDN&lt;/li&gt;
&lt;li&gt;Prevents the page from being framed&lt;/li&gt;
&lt;li&gt;Forces HTTPS upgrades&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start strict, then relax if needed&lt;/strong&gt;: It's easier to add exceptions than to tighten a loose policy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use nonces for inline scripts&lt;/strong&gt;: Generate a new random nonce for each page load&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid 'unsafe-inline' and 'unsafe-eval'&lt;/strong&gt;: These significantly weaken your CSP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor violations&lt;/strong&gt;: Keep report-only mode running alongside enforcement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use SRI for third-party resources&lt;/strong&gt;: Add integrity checks to external scripts and styles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep your policy maintainable&lt;/strong&gt;: Document why each source is whitelisted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test thoroughly&lt;/strong&gt;: Use automated tests to ensure CSP doesn't break functionality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update regularly&lt;/strong&gt;: As your app evolves, keep your CSP up to date&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Beyond XSS: Other Security Benefits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preventing Clickjacking
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: frame-ancestors 'none'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents your site from being embedded in iframes, protecting against clickjacking attacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enforcing HTTPS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: upgrade-insecure-requests
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically upgrades HTTP requests to HTTPS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Restricting Form Targets
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: form-action 'self'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents forms from submitting to external domains, which could be used for phishing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging CSP Issues
&lt;/h2&gt;

&lt;p&gt;When things don't work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check the console&lt;/strong&gt;: CSP violations are logged with detailed information&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Report-Only mode&lt;/strong&gt;: Test changes without breaking production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify nonce generation&lt;/strong&gt;: Ensure nonces are unique per request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check for typos&lt;/strong&gt;: CSP syntax is strict&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test in multiple browsers&lt;/strong&gt;: Implementation may vary slightly&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Look, CSP isn't a magic bullet. You still need to validate inputs, escape outputs, and follow other security best practices. But it's one of the most powerful tools in your security toolkit.&lt;/p&gt;

&lt;p&gt;Here's what you need to remember:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Good News:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSP blocks most XSS attacks, even if your other defenses fail&lt;/li&gt;
&lt;li&gt;Modern browsers all support it&lt;/li&gt;
&lt;li&gt;It's not that hard to implement if you do it gradually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Reality Check:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can't just flip a switch—you need to refactor your code&lt;/li&gt;
&lt;li&gt;Legacy codebases with inline scripts everywhere will be a pain&lt;/li&gt;
&lt;li&gt;You'll probably break something in production at least once (we all do)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Your Action Plan:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with report-only mode TODAY (seriously, it's free intel)&lt;/li&gt;
&lt;li&gt;Fix the low-hanging fruit (inline event handlers, obvious inline scripts)&lt;/li&gt;
&lt;li&gt;Use nonces for the stuff you can't easily refactor&lt;/li&gt;
&lt;li&gt;Test with Google's CSP Evaluator&lt;/li&gt;
&lt;li&gt;Gradually tighten your policy over time&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;When You Really Need CSP:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You handle sensitive data (payments, healthcare, personal info)&lt;/li&gt;
&lt;li&gt;You have user-generated content (comments, forums, profiles)&lt;/li&gt;
&lt;li&gt;You want to sleep better at night&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When You Can Probably Skip It:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static blog with no user interaction&lt;/li&gt;
&lt;li&gt;Internal tools behind authentication with no user input&lt;/li&gt;
&lt;li&gt;You're just starting out and have bigger fish to fry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final thought: Start strict and relax if needed. It's way easier than starting loose and trying to tighten things later.&lt;/p&gt;

&lt;p&gt;Now go secure your websites! 🔒&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading and Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MDN Web Docs - CSP&lt;/strong&gt;: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP&lt;/a&gt;&lt;br&gt;
Comprehensive documentation with examples and browser compatibility information&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Content Security Policy Reference&lt;/strong&gt;: &lt;a href="https://content-security-policy.com" rel="noopener noreferrer"&gt;https://content-security-policy.com&lt;/a&gt;&lt;br&gt;
Quick reference guide with examples and explanations for all CSP directives&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Google CSP Evaluator&lt;/strong&gt;: &lt;a href="https://csp-evaluator.withgoogle.com/" rel="noopener noreferrer"&gt;https://csp-evaluator.withgoogle.com/&lt;/a&gt;&lt;br&gt;
Essential tool for testing and validating your CSP headers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write Software Well - CSP Guide&lt;/strong&gt;: &lt;a href="https://www.writesoftwarewell.com/content-security-policy/" rel="noopener noreferrer"&gt;https://www.writesoftwarewell.com/content-security-policy/&lt;/a&gt;&lt;br&gt;
Excellent practical guide with real-world examples and implementation strategies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CSP Level 3 Specification&lt;/strong&gt;: &lt;a href="https://www.w3.org/TR/CSP3/" rel="noopener noreferrer"&gt;https://www.w3.org/TR/CSP3/&lt;/a&gt;&lt;br&gt;
Official W3C specification for the latest CSP features&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OWASP CSP Cheat Sheet&lt;/strong&gt;: &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html" rel="noopener noreferrer"&gt;https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html&lt;/a&gt;&lt;br&gt;
Security-focused best practices from OWASP&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Stay secure, and happy coding!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>pcidss</category>
      <category>csp</category>
    </item>
    <item>
      <title>Zero Downtime Deploy with PM2, Docker and Reverse Proxy</title>
      <dc:creator>Ozan</dc:creator>
      <pubDate>Thu, 29 May 2025 10:52:25 +0000</pubDate>
      <link>https://forem.com/ozankozan/zero-downtime-deploy-with-pm2-docker-and-reverse-proxy-3f06</link>
      <guid>https://forem.com/ozankozan/zero-downtime-deploy-with-pm2-docker-and-reverse-proxy-3f06</guid>
      <description>&lt;p&gt;Uptime is critical. Whether you're running a SaaS, API, or web platform, users expect availability—always. In this guide, we'll walk through how to achieve zero downtime deployments using &lt;strong&gt;PM2, Docker&lt;/strong&gt; and a reverse proxy like &lt;strong&gt;NGINX&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Zero Downtime Matters
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;User Experience:&lt;/strong&gt; No interruptions during new releases&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business Continuity:&lt;/strong&gt; Prevent revenue loss during deploys&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;App Runtime:&lt;/strong&gt; Node.js with PM2&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Containerization:&lt;/strong&gt; Docker&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web Server / Proxy:&lt;/strong&gt; NGINX or Caddy&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI/CD Tool:&lt;/strong&gt; (Drone.io (my new favorite), Jenkins, GitHub Actions, etc.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Use PM2 to Manage the App Process&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PM2 is a Node.js process manager that supports graceful restarts, monitoring, and clustering.&lt;/p&gt;

&lt;p&gt;Example Dockerfile with PM2&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:18

WORKDIR /app

# Add build-time env vars
ARG NEXT_PUBLIC_CDN_URL
ARG NEXT_PUBLIC_CDN_HOST
ENV NEXT_PUBLIC_CDN_URL=$NEXT_PUBLIC_CDN_URL
ENV NEXT_PUBLIC_CDN_HOST=$NEXT_PUBLIC_CDN_HOST

COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

RUN npm install -g pm2

EXPOSE 3000
CMD ["pm2-runtime", "start", "node_modules/next/dist/bin/next", "--", "start", "-p", "3000"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Create a Safe Docker Build + Deploy Flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will:&lt;/p&gt;

&lt;p&gt;Build the Docker image&lt;/p&gt;

&lt;p&gt;Run it on a temporary port&lt;/p&gt;

&lt;p&gt;Perform health checks&lt;/p&gt;

&lt;p&gt;If OK, swap it with the current live container&lt;/p&gt;

&lt;p&gt;Example Shell Script (simplified)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build --no-cache -t app-latest .
docker run -d --name app-temp -p 3132:3000 app-latest

# Wait and test health
sleep 5
if docker exec app-temp curl -s http://localhost:3000 | grep -q '&amp;lt;title&amp;gt;'; then
  docker stop app-live &amp;amp;&amp;amp; docker rm app-live
  docker stop app-temp &amp;amp;&amp;amp; docker rm app-temp
  docker run -d --name app-live -p 3131:3000 app-latest
else
  echo "Health check failed. Aborting deploy."
  docker stop app-temp &amp;amp;&amp;amp; docker rm app-temp
  exit 1
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Port 3132 is used temporarily; 3131 is the live port behind the reverse proxy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Set Up Reverse Proxy with NGINX&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
  listen 80;

  location / {
    proxy_pass http://localhost:3131;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NGINX routes traffic to the live container. When we swap containers on port 3131, traffic never notices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uptime Monitoring
&lt;/h2&gt;

&lt;p&gt;Use tools like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uptime.kuma.pet" rel="noopener noreferrer"&gt;Uptime Kuma&lt;/a&gt; (Docker-based)&lt;br&gt;
&lt;a href="https://betterstack.com/uptime" rel="noopener noreferrer"&gt;Better Uptime&lt;/a&gt;, &lt;br&gt;
&lt;a href="https://upptime.js.org" rel="noopener noreferrer"&gt;Upptime&lt;/a&gt; (hosted)&lt;/p&gt;

&lt;p&gt;Set them to monitor /healthz or homepage on localhost:3131.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Tips
&lt;/h2&gt;

&lt;p&gt;Avoid running Docker containers as &lt;strong&gt;root user&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don’t mount the Docker socket (/var/run/docker.sock) unless absolutely necessary&lt;/p&gt;

&lt;p&gt;Use USER node or UID-based permissions in Dockerfile&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Zero downtime deployment is achievable and maintainable with PM2, Docker, and a reverse proxy. This setup avoids user-facing errors, improves reliability, and keeps your engineering team confident in every release.&lt;/p&gt;

&lt;p&gt;Want to go further? Add CI/CD integration with Drone or GitHub Actions and automate everything end-to-end.&lt;/p&gt;

&lt;p&gt;That's all folks! ✨&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhltlvjwoxv2zkvgbkxn1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhltlvjwoxv2zkvgbkxn1.png" alt="Image description" width="686" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>node</category>
      <category>monitoring</category>
    </item>
  </channel>
</rss>
