title: Intermittent 403 CORS Errors (Access-Control-Allow-Origin) With Cloudfront Using Signed URLs To GET S3 Objects
body: <h1>In Brief</h1>
  
  <p>In order to keep the uploaded media (S3 objects) private for all the clients on my multi-tenant system I implemented a Cloudfront CDN deployment and configured it (and its Origin S3 Bucket) to force the use of signed URLs in order to GET any of the objects.
  <br><br><br></p>
  
  <h1>The Method</h1>
  
  <p>First, the user is authenticated via my system, and then a signed URL is generated and returned to them using the <a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudFront/Signer.html" rel="noreferrer"><code>AWS.CloudFront.Signer.getSignedUrl()</code></a> method provided by the <a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/index.html" rel="noreferrer">AWS JS SDK</a>. so they can make the call to CF/S3 to download the object (image, PDF, docx, etc). Pretty standard stuff.
  <br><br><br></p>
  
  <h1>The Problem</h1>
  
  <p>The above method works 95% of the time. The user obtains a signed URL from my system and then when they make an XHR to GET the object it's retrieved just fine.</p>
  
  <p>But, 5% of the time a 403 is thrown with a CORS error stating that the client origin <code>is not allowed by Access-Control-Allow-Origin</code>.</p>
  
  <p><a href="https://i.stack.imgur.com/uFHEW.png" rel="noreferrer"><img src="https://i.stack.imgur.com/uFHEW.png" alt="The error, from Safari in this case."></a></p>
  
  <p>This bug (error) has been confirmed across all environments: localhost, dev.myapp.com, prod.myapp.com. And across all platforms/browsers.</p>
  
  <p>There's such a lack of rhyme or reason to it that I'm actually starting to think this is an AWS bug (they do happen, from time-to-time).
  <br><br><br></p>
  
  <h1>The Debugging Checklist So Far</h1>
  
  <p>I've been going out of my mind for days now trying to figure this out. Here's what I've attempted so far:</p>
  
  <blockquote>
    <p><strong>Have you tried a different browser/platform?</strong></p>
    
    <p>Yes. The issue is present across all client origins, browsers (and
    versions), and all platforms.
    <br><br></p>
    
    <p><strong>Is your S3 Bucket configured for CORS correctly?</strong></p>
    
    <p>Yes. It's wide-open in fact. I've even set <code>&lt;MaxAgeSeconds&gt;0&lt;/MaxAgeSeconds&gt;</code> in
    order to prevent cacheing of any pre-flight <code>OPTIONS</code> requests by the
    client:</p>
    
    <p><a href="https://i.stack.imgur.com/iSJbb.png" rel="noreferrer"><img src="https://i.stack.imgur.com/iSJbb.png" alt="CORS settings"></a>
    <br><br></p>
    
    <p><strong>Is the signed URL expired?</strong></p>
    
    <p>Nope. All of the signed URLs are set to expire 24hrs after generation. This problem has shown up even seconds
    after any given signed URL is generated.
    <br><br></p>
    
    <p><strong>Is there an issue with the method used to generate the signed URLs?</strong></p>
    
    <p>Unlikely. I'm simply using the <code>AWS.CloudFront.Signer.getSignedUrl()</code>
    method of their JS SDK. The signed URLs <em>do</em> work most of the time, so
    it would seem very strange that it would be an issue with the signing
    process. Also, the error is clearly a CORS error, not a signature
    mis-match error.
    <br><br></p>
    
    <p><strong>Is it a timezone/server clock issue?</strong></p>
    
    <p>Nope. The system does serve users across many timezones, but that
    theory proved to be false given that the signed URLs are all generated
    on the server-side. The timezone of the client doesn't matter, it gets
    a signed URL good for 24hrs from the time of generation no matter what
    TZ it's in.
    <br><br></p>
    
    <p><strong>Is your CF distro configured properly?</strong></p>
    
    <p>Yes, so far as I can make out by following several <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-cors" rel="noreferrer">AWS guides</a>,
    <a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html" rel="noreferrer">tutorials</a>, <a href="https://aws.amazon.com/premiumsupport/knowledge-center/no-access-control-allow-origin-error/" rel="noreferrer">docs and such</a>.</p>
    
    <p>Here's a screenshot for brevity. You can see that I've disabled
    cacheing entirely in an attempt to rule that out as a cause:</p>
    
    <p><a href="https://i.stack.imgur.com/xif0T.png" rel="noreferrer"><img src="https://i.stack.imgur.com/xif0T.png" alt="CF distro config"></a>
    <br><br></p>
    
    <p><strong>Are you seeing this error for all mime-types?</strong> </p>
    
    <p>No. This error hasn't been seen for any images, audio, or video files
    (objects). With much testing already done, this error only seems to
    show up when attempting to GET a document or PDF file (.doc, .docx,
    .pdf).   This lead me to believe that this was simply an <code>Accept</code> header
    mis-match error: The client was sending an XHR with the the header
    <code>Accept: pdf</code>, but really the signature was generated for <code>Accept: application/pdf</code>.
    I haven't yet been able to fully rule this out as a
    cause. But it's highly unlikely given that the errors are
    intermittent. So if it were a <code>Accept</code> header mis-match problem then it
    should be an error every time.</p>
    
    <p>Also, the XHR is sending <code>Accept: */*</code> so it's highly unlikely this is where the issue is.</p>
  </blockquote>
  
  <p><br><br></p>
  
  <h1>The Question</h1>
  
  <p>I've really hit a wall on this one. Can anyone see what I'm missing here? The best I can come up with is that this is some sort of "timing" issue. What sort of timing issue, or if it even is a timing issue, I've yet to figure out.</p>
  
  <p>Thanks in advance for any help.</p>