<?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>DEV Community: Ozan</title>
    <description>The latest articles on DEV Community by Ozan (@ozankozan).</description>
    <link>https://dev.to/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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3221679%2Fbc381285-5b15-4ffd-a36a-2ace554cf324.jpeg</url>
      <title>DEV Community: Ozan</title>
      <link>https://dev.to/ozankozan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ozankozan"/>
    <language>en</language>
    <item>
      <title>🐳 CI/CD pipeline with Docker Scout</title>
      <dc:creator>Ozan</dc:creator>
      <pubDate>Tue, 10 Nov 2026 11:42:56 +0000</pubDate>
      <link>https://dev.to/ozankozan/cicd-pipeline-with-docker-scout-2jgd</link>
      <guid>https://dev.to/ozankozan/cicd-pipeline-with-docker-scout-2jgd</guid>
      <description>&lt;p&gt;You build the image, tag it, ship it... everything looks great. Then your security team flags a CVE in a package you didn’t even know was in there.&lt;/p&gt;

&lt;p&gt;Docker Scout is Docker’s built-in vulnerability scanning and SBOM (Software Bill of Materials) tool. No extra install, no separate tooling: it scans your images against 20+ advisory sources (NVD, GitHub Advisory DB, CISA KEV, etc.) and lets you enforce policy evaluation before an image is allowed to ship. It turns container security from a one-time checkbox into a continuous process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Worth knowing:&lt;/strong&gt; &lt;em&gt;Docker Scout isn’t the first attempt at this. Before Scout, Docker shipped docker scan, which relied on Snyk’s engine under the hood. Vulnerability scanning for local Docker images was introduced in late 2020 with the command docker scan. The Docker Scan command would use Snyk’s engine to scan Docker images for security vulnerabilities. The docker scan command is deprecated and will no longer be supported after April 13th, 2023. Run the docker scout cves command to continue to get vulnerabilities on your images. So Scout is Docker’s own engine, not a rebrand of the old plugin. itnext&lt;br&gt;
dev&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One more thing worth knowing: Docker Scout’s native Slack integration is being retired on July 30, 2026. So the practical move is to run docker scout cves / docker scout policy directly inside your pipeline and route the result to your own notification channel.&lt;/p&gt;

&lt;p&gt;A minimal Jenkins step, report first, enforce once you trust the signal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="nf"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Image Scan'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Report only for now. Flip exitCode to 1 to fail the build on HIGH/CRITICAL.&lt;/span&gt;
            &lt;span class="kd"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'''
                    docker scout cves myorg/backend:${GIT_COMMIT} \
                      --only-severity critical,high \
                      --exit-code 0
                '''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;returnStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="n"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;slackSend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'#ci-alerts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'danger'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Critical CVEs found in myorg/backend:${GIT_COMMIT}"&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notification best practices, in short:&lt;br&gt;
→ Alert only on HIGH/CRITICAL and pipeline failures, not every scan.&lt;br&gt;
→ Keep webhook URLs in Jenkins Credentials, never in the Jenkinsfile.&lt;br&gt;
→ A failed notification shouldn’t fail your build; wrap it, don’t gate on it.&lt;br&gt;
→ The point: a broken build at 2am shouldn’t surface at 9am. Good alerting is signal, not noise.&lt;/p&gt;

&lt;p&gt;Scan continuously, alert on what matters, decide your own threshold for when to block. That’s what makes container security part of the daily workflow instead of a quarterly audit.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devops</category>
      <category>docker</category>
      <category>security</category>
    </item>
    <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://dev.to/ozankozan/getting-real-client-ips-behind-cloudflare-or-other-proxy-in-laravel-1j6i</link>
      <guid>https://dev.to/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://dev.to/ozankozan/understanding-content-security-policy-csp-1hh5</link>
      <guid>https://dev.to/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://dev.to/ozankozan/zero-downtime-deploy-with-pm2-docker-and-reverse-proxy-3f06</link>
      <guid>https://dev.to/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>
