43

I'm stuck on an issue with API gateway and I've gone through all the other SO answers on this, AWS forums and have been through their docs but still no joy.

I am trying to setup an API using AWS API gateway which calls a Lambda function which reads/writes to a table in DynamoDB.

The Lambda function to DynamoDB is working. I have created an API in AWS, and created a GET and OPTIONS methods for it. I read AWS does not enforce the OPTIONS for only GET/POST but i was getting a preflight error in my ajax call when there was no OPTIONS method so I added one.

For now just to make progress I am not using an API key or Authorization. I can successfully call my GET method using POSTMAN, which returns the contents of the DynamoDB table.

But when i try using a JQuery ajax call i get

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

I can see using Chrome dev tools under the network tab, the OPTIONS method returning status 200 and the GET returns status 200 but with the above error.

I have tried enabling CORS on both the OPTIONS and GET methods, have re-deployed the API after every change, have tried the following (http://enable-cors.org/server_awsapigateway.html) but always get the same error in the console.

I am executing the ajax call from a file on my desktop so origin is null as the page will be deployed to S3 as its a single web page application in JS.

When I enabled CORS on my GET and OPTIONS i can see that Access-Control-Allow-Headers is 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token' and Access-Control-Allow-Origin* is '*'

My Ajax call looks like below. I also tried copying the exact headers POSTMAN uses, which has the Authorization header set (which i have turned off in AWS for now) but i always get the same error above

var awsHeaders = {};
awsHeaders['X-Amz-Date'] = '20161127T171734';

$('#add, #cloud').click(function() {

    $.ajax({

        type: 'GET',
        headers: awsHeaders,
        dataType : "json",
        url: '...',
        success: function (res) {

            console.log('response in GET:');
            console.log(res);

        },
        error: function(data) {
            console.log('in error');
            console.log(data);
        }

    });

});

Can anyone shed light on what i might be missing?

Many thanks

Update See answer below regarding how I solved this as per DigitalKapteain comments - by setting the 'Access-Control-Allow-Origin':'*' header in the response from my Lambda function. I looked for this in the AWS docs but couldn;t find it. This link describes the difference between Lambda and Lambda Proxy and explains what to do when using CORS https://serverless.com/framework/docs/providers/aws/events/apigateway/

user12345
  • 675
  • 1
  • 8
  • 17
  • it could be you are not missing anything, and it could be an ISP caching issue, try different internet connection and a fresh browser, if not, then CORS might be the issue – Waheed Nov 27 '16 at 18:31
  • Thanks. I dont think its an ISP issue as I've tried this on different network connections (cafe, hotel etc...). Have also cleared cached etc and tried different browsers – user12345 Nov 27 '16 at 19:08
  • Then its CORS @user12345 I think you have already seen this documentation but try to double check you went through the steps again, http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html – Waheed Nov 28 '16 at 05:52
  • Thanks @Waheedi. I've followed that doc but I'll go through it again to double check – user12345 Nov 28 '16 at 11:26
  • Hi @Waheedi. Re-reading the docs and according to Digitalkapitean anwser below, the docs say 'In addition, the actual CORS-enabled methods must also return the Access-Control-Allow-Origin:'*' header in at least its 200 response.' So I have added Access-Control-Allow-Origin header to the GET Method Response (the 200 response) in API gateway and redeployed the API but still do not see this header returned by the GET. Any idea where else this needs to be set? – user12345 Nov 28 '16 at 11:39

6 Answers6

70

The response for the GET request to the Lambda function must also contain the Access-Control-Allow-Originheader.

Digitalkapitaen
  • 2,353
  • 13
  • 17
  • 2
    Thanks @Digitalkapitaen. Looking at the response headers returned from the OPTIONS request it contains Access-Control-Allow-Methods:GET,OPTIONS and Access-Control-Allow-Origin:*, but i do not see the Access-Control-Allow-Origin response header for the GET request. I have set it in the GET method Method Response of API gateway and re-deployed the API but i do not see it returned. Any idea where else this needs to be set? Many thanks – user12345 Nov 28 '16 at 11:33
  • 4
    @user12345 Are you using Lambda Proxy Integration or did you configure the lambda invocation manually in API Gateway? If you use Proxy Integration, you would set the header in your Lambda functions code. – Digitalkapitaen Nov 28 '16 at 11:37
  • 4
    I am using Lambda Proxy integration. So i changed the Lambda function to also return the header 'Access-Control-Allow-Origin':'*' and voila the Access control error is gone. Many thanks for your help. So when using Proxy integration, the headers are set by the Lambda function and not the Method Response in API gateway? – user12345 Nov 28 '16 at 11:52
  • 5
    @user12345 I have not found this stated in any documentation, but actual behavior shows that headers should be set in the Lambda function. – Digitalkapitaen Nov 28 '16 at 13:21
  • The Lambda function must return the CORS headers, in addition to the API Gateway. – Doug Apr 07 '17 at 02:25
  • 1
    I wasted hours trying to figure this out, this needs to be front and center in the documentation for lambda/api gateway. – Adrian Seeley Jul 19 '17 at 14:11
  • 3
    To be honest, using integration shouldn't break the CORS configuration, lost a lot of time because of this, they should show a warning or something. – Carlos Lopez Dec 09 '17 at 03:04
  • Thanks! Saved me hours! I think, you should really make "If you use Proxy Integration, you would set the header in your Lambda functions code." a part of your answer PLUS mark it in BOLD. – Igor Soloydenko Feb 06 '18 at 22:55
  • THANK YOU! I'm rather new to AWS and with building a react-native app and react js dashboard to do the admin, I'm using the aws api gateway client in both native and react js https://github.com/kndt84/aws-api-gateway-client Setting this header directly in the lambda function solved this immediately, I was slightly confused but I'm pleased its working now - thank you! I do have a question, is setting this safe for production environment? – Michael Apr 28 '18 at 13:56
  • @MichaelStokes In general, this is safe for a production environment. However, the header is called access-control-allow-origin for a reason: You should carefully select the origin that you want to allow. Using '*' may not be correct for your situation. – Digitalkapitaen Apr 29 '18 at 17:16
  • This totally works ! In java you need to return a been that has a field called "headers" of type Map and you need to have one of the entries of that map be "headers.put("Access-Control-Allow-Origin", "*")" – sashok_bg May 15 '18 at 15:03
  • Thanks very much @Digitalkapitaen Yes I imagine so. Typically it will be from a mobile app, but otherwise an administrative dashboard. Brilliant, though! – Michael May 17 '18 at 07:16
  • The docs also say to add ' "Access-Control-Allow-Credentials" : true ' for cookies, authorization headers with HTTPS – Eric D'Souza Jun 19 '18 at 22:11
37

Digitalkapitaen's answer is correct; here is the code to save someone the trouble of looking up how to set an HTTP response header in Lambda:

exports.handler = function(event, context, callback) {
    callback(null, {
        "statusCode": 200,
        "headers": { 
            "Access-Control-Allow-Origin": "*" 
        }
    });
};
Jesus is Lord
  • 14,971
  • 11
  • 66
  • 97
7

If this is still not working for you, be sure to JSON.stringify() your json object if you are using $.ajax. If not, you will still be returned an error that claims to be a CORS-related error. But if you send the same json object using Postman, the request will succeed. Try it out...

rickfarina
  • 116
  • 1
  • 3
  • This. I was seeing status code 415 because I was posting form data instead of JSON. While it looked like a CORS-related issue in the console, the problem was this. – Jan Klimo Jan 30 '18 at 17:21
0

For someone looking to integrate @Digitalkapitaen's solution in Flask, here's the code below:

app = Flask(__name__)
cors = CORS(app, resources={r"/*": {"origins": "*"}})

@app.route("/")
def helloWorld():
  return "Hello, cross-origin-world!"

Do install the flask-cors module by doing a:

pip install -U flask-cors
captainblack
  • 4,107
  • 5
  • 50
  • 60
0

In case you want to change only one header instead of replacing all headers as is shown in Words Like Jared answer. You can use this code:

'use strict';

module.exports.handler = (event, context, callback) => {
  const response = event.Records[0].cf.response;
  const headers = response.headers;

  headers['access-control-allow-origin'] = [{ key: 'Access-Control-Allow-Origin', value: "*" }];

  return callback(null, response);
};

Another examples can be found on Adding HTTP Security Headers Using Lambda@Edge and Amazon CloudFront. It works same for normal Lambda function.

Black
  • 9,541
  • 3
  • 54
  • 54
0

I had the same issue: configured a website in S3 and try to do ajax call in API gateway. Enabled and Deployed CORS in API gateway and got the same issue, after setting the response header in Lambda code problem was solved.

Here's a snippet of my sample Lambda code:

 let response = {
        statusCode: responseCode,
        headers: {
            "x-custom-header" : "my custom header value",
            "Access-Control-Allow-Origin": "*"
        },
        body: JSON.stringify(responseBody)
    };
    console.log("response: " + JSON.stringify(response))
    return response;
Joe Kdw
  • 2,245
  • 1
  • 21
  • 38