title: CORS upload file to S3, got SignatureDoesNotMatch error
body: <p>I want to upload video files from browser to S3. 
  Here is the my steps:</p>
  
  <ol>
  <li>Send <code>GET</code> request to API Gateway, invoke Lambda to get a signed URL.</li>
  <li>Use the signed URL directly to upload files to S3 using <code>PUT</code>.</li>
  </ol>
  
  <p>Step 1 works fine, I can use the signed URL to upload file through <code>curl</code>:</p>
  
  <pre><code>curl -X PUT https://bucket-name.s3.amazonaws.com/testupload/videofile.mp4?AWSAccessKeyId=MY_AWS_ACCESS_KEY_ID&amp;Expires=1523329853&amp;Signature=XXXXX -T videophile.mp4
  </code></pre>
  
  <p>But upload the file to S3 from browser, I get 403 response with <code>SignatureDoesNotMatch</code> error:</p>
  
  <blockquote>
    <p>&lt;Error&gt;</p>
    
    <p>&lt;Code&gt;SignatureDoesNotMatch&lt;/Code&gt;</p>
    
    <p>&lt;Message&gt;The request signature we calculated does not match the signature you provided. Check your key and signing method.&lt;/Message&gt;</p>
    
    <p>&lt;AWSAccessKeyId&gt;MY_AWS_ACCESS_KEY_ID&lt;/AWSAccessKeyId&gt;</p>
    
    <p>...</p>
    
    <p>...</p>
    
    <p>&lt;/Error&gt;</p>
  </blockquote>
  
  <p>I checked the access key is the one I used in lambda to create signed url</p>
  
  <hr>
  
  <h2>Client side Javascript:</h2>
  
  <pre><code>$.ajax({
      url : signedurl,
      type : "PUT",
      data : uploadfile,
      dataType : "text",
      cache : false,
      contentType : file.type,
      processData : false
  })
  .done(function() {
  
  })
  .fail(function(e) {
      console.error(e.status, e.statusText)
      console.error(e.responseText)
  });
  </code></pre>
  
  <h2>Bucket CORS config:</h2>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"&gt;    
  &lt;CORSRule&gt;
      &lt;AllowedOrigin&gt;*&lt;/AllowedOrigin&gt;
      &lt;AllowedMethod&gt;GET&lt;/AllowedMethod&gt;
      &lt;AllowedMethod&gt;POST&lt;/AllowedMethod&gt;
      &lt;AllowedMethod&gt;PUT&lt;/AllowedMethod&gt;
      &lt;AllowedMethod&gt;HEAD&lt;/AllowedMethod&gt;
      &lt;AllowedMethod&gt;DELETE&lt;/AllowedMethod&gt;
      &lt;MaxAgeSeconds&gt;3000&lt;/MaxAgeSeconds&gt;
      &lt;AllowedHeader&gt;*&lt;/AllowedHeader&gt;
  &lt;/CORSRule&gt;
  &lt;/CORSConfiguration&gt;
  </code></pre>