51

CORS is really driving me crazy and I'm really out of ideas as of what to try to make it work.

I have created a simple APIG Api with 1 resource called 'abc' and added 2 methods GET and POST both with Authorization set to NONE and API Key Required set to false, everything deployed to a stage called 'dev'.

Of course I enabled CORS on both methods and I see the 3 headers Access-Control-Allow-Origin, Access-Control-Allow-Headers and Access-Control-Allow-Methods added to the OPTIONS method and the Access-Control-Allow-Origin added to the POST and GET methods.

Both calls are mapped to the same lambda function that simply outputs a 'Hello from Lambda' text to the console.

Then I have created a simple html page I hosted as a static website on S3, pointed a domain to it using Route53 and started testing the API using jQuery $.ajax to make the calls.

All seems easy, straightforward and exactly as explained in the docs, except only the GET works and outputs the text to the console as expected. The POST version results in the following error:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://example.com' is therefore not allowed access. The response had HTTP status code 400.

The preflight call works and returns 200 OK and all headers are there, but the POST call returns that error and a 400 Bad Request.

Please any help is really appreciated, I hope the AWS team is watching too...

Thanks guys.


EDITED - Copied from Google Chrome:

POST Raw Request Headers:

POST /dev/urls HTTP/1.1
Host: kykul1mshe.execute-api.us-east-1.amazonaws.com
Connection: keep-alive
Content-Length: 73
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Content-Type: application/json
Referer: http://example.com/dev.html
Accept-Encoding: gzip, deflate, br
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4

POST Raw Response Headers:

HTTP/1.1 400 Bad Request
Date: Fri, 19 Aug 2016 02:14:16 GMT
Content-Type: application/json
Content-Length: 177
Connection: keep-alive
x-amzn-RequestId: a1160e45-65b2-11e6-9766-cd61e49fbcdb
X-Cache: Error from cloudfront
Via: 1.1 d64756b4df47ce24d6c62b5a8de97e87.cloudfront.net (CloudFront)
X-Amz-Cf-Id: N9mf7apicKbSM_MiZjePbEgZGIFKckWJ3lZljH8iHVKFVTcIIOQuHg==

This returns 400 Bad Request

OPTIONS Raw Request Headers:

Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:kykul1mshe.execute-api.us-east-1.amazonaws.com
Origin:http://example.com
Referer:http://example.com/dev.html
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36

OPTIONS Raw Response Headers:

Access-Control-Allow-Headers:Content-Type,X-Amz-Date,Authorization,X-Api-Key,Cache-Control,X-Requested-With
Access-Control-Allow-Methods:POST,OPTIONS
Access-Control-Allow-Origin:*
Connection:keep-alive
Content-Length:79
Content-Type:application/json
Date:Fri, 19 Aug 2016 02:14:16 GMT
Via:1.1 d64756b4df47ce24d6c62b5a8de97e87.cloudfront.net (CloudFront)
X-Amz-Cf-Id:KpGEDmIuf5RHcUnBWuA3oEMZgWHwrjy3SpLuOflRhAD8IIx5vyKGSw==
x-amzn-RequestId:a10bae11-65b2-11e6-bcf7-63b49c24629e
X-Cache:Miss from cloudfront

This returns 200 OK

HBR
  • 901
  • 1
  • 8
  • 15
  • Hi, I am from api gateway. I don't see anything wrong with the way you setup your api. Could you update with the raw requests? That'll help in debugging. – Abhigna Nagaraja Aug 18 '16 at 23:02
  • Thank you for replying @AbhignaNagaraja - I updated the post with the headers I got in Google Chrome (I've just hidden the real domain name). – HBR Aug 19 '16 at 03:12

11 Answers11

31

If you are using proxy integration in API Gateway, then enabling CORS from API Gateway doesn't work. You have to set the Header 'Access-Control-Allow-Origin' from your Lambda code itself.

Its mentioned in the doc.

Python code sample:

    response = {
        'statusCode': 200,
        'headers': {
            'Access-Control-Allow-Origin': '*'
        },
        'body': json.dumps({'message': 'CORS enabled')
    }
    return response
Dawn T Cherian
  • 4,968
  • 3
  • 24
  • 35
  • This is the correct answer. Worked for me for both Node and Python. thank you! – Mirek Jun 02 '20 at 19:56
  • Thank you. I must have spent 20 hrs fighting with this stuff. Here's a more direct link to the "[it doesn't work with proxy integration](https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html#apigateway-enable-cors-proxy)" docs – gman Oct 24 '20 at 08:40
  • from one cherian to another. Thank you – joel Oct 28 '20 at 11:34
  • This is strange. Any reason why it doesn't work? – Prashanth Wagle Apr 29 '21 at 10:42
28

Ok, I found the origin of the problem, which happens to be totally unrelated to APIG, and confirms what @AbhignaNagaraja mentioned, that my APIG was properly configured.

The issue is actually in the way I called jQuery.ajax, which I thought was smart enough to convert my parameters to a JSON string when contentType is 'application/json'. It seems I had to manually stringify the JSON params rather than passing a JSON and having jQuery stringify it.

So this is the bad call:

$.ajax({
        url: myEndpoint,
        type: 'POST',
        crossDomain: true,
        data: {
            url: $('#url').val()
        },
        headers: {
            "X-Api-Key": 'blablabla'
        },
        dataType: 'json',
        contentType: "application/json",
        success: function (data) {
            console.info(data);
        }
    });

And this is the right call:

 $.ajax({
        url: myEndpoint,
        type: 'POST',
        crossDomain: true,
        data: JSON.stringify({
            url: $('#url').val()
        }),
        headers: {
            "X-Api-Key": 'blablabla'
        },
        dataType: 'json',
        contentType: "application/json",
        success: function (data) {
            console.info(data);
        }
    });

This can be a hint if you are debugging such an issue with CORS: just download the AWS APIG SDK and try executing the call using the apigClient provided by AWS and compare headers with the ones you get with your custom client. When examining the 2 sets of headers I got with jQuery and apigClient, I noticed the Request Payload looked different and thats how I realized the format was wrong, then the 400 code and the No 'Access-Control-Allow-Origin' header is present all made sense.

I hope this helps.

HBR
  • 901
  • 1
  • 8
  • 15
  • *"It seems I had to manually stringify the JSON params rather than passing a JSON and having jQuery stringify it.":* If it were json, jquery wouldn't have needed to stringify it. What you *were* passing was an object, not json, and jquery converts anything that isn't a string to a string (param string). By stringifying it, you turned it into a string and thus jquery didn't touch it. – Kevin B Oct 10 '17 at 22:50
  • Thanks, stringifying resolved the same problem for me. – deanmau5 Jan 09 '18 at 09:40
  • 2
    so it makes sense for an invalid JSON payload to incur the absence of CORS header in APIG response? – gigi2 Jan 22 '18 at 20:51
  • I understand this was over two years ago, however do you remember if you used lambda proxy integration or not? – openwonk Oct 16 '18 at 01:23
  • stringifying json object when making a call to Lambda solved this issue for me. Thanks. Not sure why it gives an error about CORS in the first place though. – aspnetdeveloper Nov 10 '20 at 21:23
14

I had a similar issue, but with lambda proxy integration:

  • CORS activated on AWS API Gateway using the browser

  • lambda-proxy integration activated

When using the lambda proxy integration, you can return custom headers from inside the code of the lambda:

        var result = {
        statusCode: data.statusCode | 200,
        headers: {
          "Access-Control-Allow-Origin": "*"
        },
        body: JSON.stringify(responseBody)
    };
    callback(null, result);

This way you get the CORS header sent. I think there might be a better way to get it to work with the lambda proxy integration without coupling the CORS inside the code of the lambda, please let me know if you know.

arseneoaa
  • 286
  • 5
  • 5
  • This actually works when using Lambda proxy integration. Thanks! – openwonk Oct 16 '18 at 02:32
  • Side note, I also enabled CORS on each resource in my API Gateway instance... not sure if that helped, however it did add / allow for a CORS header that (I see now) the Lambda function produces. – openwonk Oct 16 '18 at 02:36
8

Here in 2021 still having CORS problems. My setup is React App -> API Gateway -> Lambda Application -> Dynamo DB. My API requires an apiKey.

If you reach all the way here, none of the above worked like mine. This how I debug my issue:

  • I've enabled CORS on my GET request which in turned generated an OPTIONS method. note that OPTIONS method should not require apiKey.

  • Enabled Cloudwatch Logs for API Gateway, where I saw requires API Key and API Key is not associated with a Usage Plan for API Stage

  • Added apiKey in Usage Plan.

  • Called both GET and OPTION method on Postman to test it's working. (both getting status 200 OK)

  • Noticed still getting this error

enter image description here

  • but upon further inspection headers, I allowed specific headers only when I enabled CORS on my API Gateway

enter image description here

In other words, Access-Control-Allow-Headers Needs to match the headers you add in your actual request

           {
                'Content-Type': 'application/json',
                'X-API-Key': 'secret',
                //'Access-Control-Allow-Origin': '*',
            }

For me adding: 'Access-Control-Allow-Origin': '*' from the request point of view actually was causing the CORS issue, since it was not part of the default allow headers.

pho_pho
  • 672
  • 11
  • 30
mel3kings
  • 8,857
  • 3
  • 60
  • 68
  • 2
    This should be higher up. It was never clear in AWS documenation that the answer was the remove these headers from my code. It was always stated to make sure everything was identical. This worked perfectly. – justKeepCodin Dec 04 '22 at 13:17
5

I was also stuck in this error, and after digging, I found that in non 2XX responses, the API was not giving Access-Control-Allow-Origin header in the response. Hence, while the OPTION method and successful (2XX) responses had this header, the 4XX and 5XX did not. One can also confirm this using PostMan and inspecting the headers of the bad response.

After tweaking with configuration I made sure to return that header in all responses.

Abhishek
  • 2,543
  • 4
  • 34
  • 46
5

Here in 2020 and still finding other causes of the API Gateway CORS issues. For me (not using lambda proxy) I was still getting the CORS error even after using the API dropdown to enable CORS, and even after adding the 'Access-Control-Allow-Origin' header to the response from my Lambda function.

What fixed it for me as adding the 'Access-Control-Allow-Origin' to the default Gateway Responses. Within the API Gateway UI, on the left-side menu, find Gateway Responses. Select 'Default 4XX' and edit it to include the 'Access-Control-Allow-Origin'. I set the value at '*' to be totally open during testing (including the quotes). Do the same for 'Default 5XX'. My ajax calls were able get passed the CORS error and now I see the real cause - the required parameters I set for the method weren't being passed in correctly.

Yet, I don't know why did that present as a CORS error.

סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
Chris Smith
  • 592
  • 3
  • 9
3

I had a similar issue - and it had nothing to do with the way that the API was configured or the POST request that I was making on the front-end. What fixed the problem for me was deploying the API on AWS API Gateway. Once you create an API method/resource, and tie them to a lambda function, they do not auto deploy.

You have to click "Actions" and then "Deploy API" in order to access these MicroServices from the front-end.

Marquistador
  • 1,841
  • 19
  • 26
3

to resolve this i present to you my config of the Post configuration in apy gateway

Option method - Integration REsponse - Headers Mapping

X-Requested-With '*'
Access-Control-Allow-Headers 'Content-Type,x-requested-with,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Access-Control-Allow-Methods'
Access-Control-Allow-Origin 'http://localhost:4200'
Access-Control-Allow-Methods 'POST,OPTIONS'

Post Method - Integration Response - Headers Mapping

Access-Control-Allow-Origin "*" ---> can be changed by your ip obviously

I hope this helps you

2

Sometimes CORS error occurs if the lambda time out is small.

1

There have been many posts that direct you to make sure the lambda function is returning the appropriate CORS headers, and they are correct. However, it is also critical that the json object is stringified using JSON.stringify(). It seems that Postman does this for us, so it is misleading when the Postman request and the $.ajax request send the same json object; yet one succeeds and one fails.

rickfarina
  • 116
  • 1
  • 3
0

2021 October, I tried all above and solved it by

  • Create an Empty Model enter image description here
  • Options > Method Response > Response Body for 200 > Add Response Model > ‘application/json’ and select Empty Model created above enter image description here
Nisal Gunawardana
  • 1,345
  • 16
  • 20