title: Content Security Policy: Allow files after initial load?
body: <p>The Content-Security-Policy header is, in theory, awesome. In practice, I've run into a problem. If a page loads the CSP and later you, for example, load a login page dynamically, the JS files for that page must have already been included. Because even if the login page uses an absolute URL which matches the domain the index page is from (And is set in the CSP for script-src), it will prevent it from loading.</p>
  
  <p>When I used a nonce to try to circumvent this, the same issue arose, but it was intermittent. Sometimes it would let the same-nonce javascript file load in, and sometimes it wouldn't.</p>
  
  <p>In case it's unclear, the dynamically-loaded script triggers CSP's idea of an "unsafe-inline script."</p>
  
  <p>Is there a 100% effective way to load dynamic JS/CSS files with a strong CSP?</p>
  
  <p>Freeform code example:</p>
  
  <p>index.php:</p>
  
  <pre><code>&lt;?php Header("Content-Security-Policy: default-src 'none'; script-src 'self' https://example.com"); ?&gt;
  &lt;script src="triggerLogin.js"&gt;&lt;/script&gt;
  &lt;a href="login.php" class="loginLink"&gt;Login&lt;/a&gt;
  &lt;div class="futureLoginPage"&gt;&lt;/div&gt;
  </code></pre>
  
  <p>triggerLogin.js: (CSP Allows)</p>
  
  <pre><code>$('.loginLink').on('click', function(event)
  {
      $('.futureLoginPage').load('/login.php');
      event.stopPropagation();
      event.preventDefault();
      return false;
  });
  </code></pre>
  
  <p>login.php:</p>
  
  <pre><code>&lt;script src="https://example.com/loginFormVerify.js" type="text/javascript"&gt;&lt;/script&gt;
  &lt;input type="text" class="user" placeholder="Username" /&gt;
  &lt;input type="text" class="pass" placeholder="Password" /&gt;
  &lt;input type="button" class="send" value="Send" /&gt;
  &lt;div class="authStatus"&gt;&lt;/div&gt;
  </code></pre>
  
  <p>loginFormVerify.js: (CSP Forbids)</p>
  
  <pre><code>$('.send').on('click', function()
  {
      post("/verification.php", { user: $('.user').val(), pass: $('.pass').val() }, function(data)
      {
          $('.authStatus').html(data);
      });
  });
  </code></pre>
  
  <p>It doesn't matter if I use <a href="https://example.com/" rel="nofollow noreferrer">https://example.com/</a> in the 'post' functions, as CSP doesn't even load the loginFormVerify.js file, let alone check its contents.</p>
  
  <p>In reverse, it doesn't matter if I remove <a href="https://example.com/" rel="nofollow noreferrer">https://example.com/</a> from the JS script sources to let it resolve to its own understanding of the domain. It still fails.</p>
  
  <p>It doesn't matter if I add type="text/javascript" or type="application/javascript" to one or both of the tags, either. And as mentioned above, a nonce only intermittently helps on a dynamically-loaded JS file sent from the server.</p>
  
  <p>While we're at it, it doesn't matter if I have ' ' around the domain name in the header.</p>
  
  <p>Also, I can remove the &lt;/script&gt; tags and use the below to the exact same result:</p>
  
  <pre><code>&lt;script src="..." /&gt;
  </code></pre>
  
  <p>I've attempted the above connection style on Safari, Chrome and Firefox with identical results. Safari and Chrome on mobile as well.</p>
  
  <p>I'm aware one workaround is to condense all JS functionality into a single file at the index.php level, but I'm curious if there's a way to handle this some other way.</p>
  
  <ul>
  <li>I make no guarantees that the above example code works in any capacity and recognize the above snippet is far from best practice.</li>
  <li>The point of the above code is exclusively to provide an example of how CSP fails to provide a desired result in a dynamic environment, for the purposes of the question:</li>
  </ul>
  
  <p>Is there a 100% effective workaround without reducing CSP's useful nature, other than preloading all scripts (Which can be harmful to speed, maintenance, bandwidth, and -- for those that care -- SEO)?</p>