Reputation: 3826
Although CORS has been set up through API Gateway and the Access-Control-Allow-Origin
header is set, I still receive the following error when attempting to call the API from AJAX within Chrome:
XMLHttpRequest cannot load http://XXXXX.execute-api.us-west-2.amazonaws.com/beta/YYYYY. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.
I attempted to GET the URL through Postman and it shows the above header is successfully passed:
And from the OPTIONS reponse:
How can I call my API from the browser without reverting to JSON-P?
Upvotes: 186
Views: 269989
Reputation: 351
I tried every solution I could possibly find online and nothing worked for me. Here was the required fix in my case:
In the API Gateway Resources tab, I had to click on ANY, and for Integration Response, and Method Response, I had to make the header mappings EXACTLY match the exact setup of the OPTIONS. That's what made it work.
(No clue why it doesn't set that up by default when you Enable CORS)
I had to do that in addition to overtly returning the headers inside the code of my Lambda functions as many others are advising.
Upvotes: 0
Reputation: 940
I landed here at one point so I'm going to add this:
My problem: I want to create an AWS API Gateway from my domain so that I can proxy requests to a foreign API such that the browser believes (and is) calling my domain's API. E.g. jackhole.com will call api.jackhole.com therefore api.jacklhole.com should have a CORS policy such that it only allows jackhole.com to call it (and possibly others)
So for my API Gateway, I set a CORS policy:
Sure enough, my API Gateway does indeed have a CORS policy for jackhole.com!
Yet when I attempt to call from my domain, I get CORS: no 'Access-Control-Allow-Origin' header Why? How?
The tipoff for me (in hindsight) was that I was not able to create an A record alias in Route 53 by click-selecting my API (which was definitely there). So why? Well simple: I was calling an api that didn't exist and thus the browser never received a CORS "OK" and refused to even send. It's not that it wasn't there, it's that I wasn't calling the "right" thing.
So solution was in the Panel Custom domain names:
If you've "set everything else up right", you probably haven't set a custom domain name for your API and that's why it's not showing up in Route 53.
You won't see the API available in Route 53 until you Create the Custom domain name within API Gateway.
EDIT: One thing I forgot that bit me in the ass after trying to add another HTTP endpoint: add /{proxy+} to your route and add {proxy} to the path of the HTTP endpoint you're proxying.
Upvotes: -1
Reputation: 3712
I was using a default DynamoDB lambda set up for basic CRUD access.
After correctly setting up the API Gateway (e.g., needed to accept * origin), it was set up to pass ALL request methods to the lambda.
The default lambda didn't have OPTION handling, so while it was sending the right headers, the lambda was sending a 400 error.
I had to add OPTIONS to its switch case, like so:
try {
switch (event.httpMethod) {
case 'OPTIONS':
// adding to allow "ALL" integration with API Gateway
body = {};//await dynamo.delete(JSON.parse(event.body));
break;
case 'DELETE':
body = await dynamo.delete(JSON.parse(event.body));
break;
case 'GET':
body = await dynamo.scan({ TableName: event.queryStringParameters.TableName });
break;
case 'POST':
body = await dynamo.put(JSON.parse(event.body));
break;
case 'PUT':
body = await dynamo.update(JSON.parse(event.body));
break;
default:
throw new Error(`Unsupported method "${event.httpMethod}"`);
}
} catch (err) {
statusCode = '400';
body = err.message;
} finally {
body = JSON.stringify(body);
}
et voila. Took way too long to sort this.
Upvotes: 0
Reputation: 3182
I got into the same problem. I have spent 10hrs to find out the below solution.
https://serverless.com/framework/docs/providers/aws/events/apigateway/
// handler.js
"use strict";
module.exports.hello = function (event, context, callback) {
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*", // Required for CORS support to work
"Access-Control-Allow-Credentials": true, // Required for cookies, authorization headers with HTTPS
},
body: JSON.stringify({ message: "Hello World!" }),
};
callback(null, response);
};
Upvotes: 191
Reputation: 406
Make sure that the headers you are returning from lambda and headers you setup while enabling CORS on resource are equal.
In my case value of "Access-Control-Allow-Headers" returned from lambda function and "Access-Control-Allow-Headers" returned when hit the endpoint with OPTIONS method were not same.
while enabling CORS the default value is "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token" but in lambda I was only returning "Content-Type". Making them equal resolved the issue.
Upvotes: 0
Reputation: 31
I have been having this issue for quite sometime. I endend up putting this to my python lambda function
response ={
'statusCode': statusCode,
'headers':{
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
},
'body':json.dumps(body)
}
return response
I hope this helps someone out there
Upvotes: 2
Reputation: 7855
For future sufferers:
This cursed problem haunted me once again and this time it was because I was sending a custom header:
let headers = {
'Content-Type': 'application/json',
'Is-Web': true,
Authorization: `Bearer ${accessToken}`,
};
This "Is-Web" custom header made API Gateway block my requests and masked it as a CORS error. If you're sending one, just remove it and test it. Almost lost a whole day of work because of this.
Upvotes: 0
Reputation: 905
For Python, as @riseres mentioned, after importing json, etc...
// lambda handler
def hello(event, context, callback):
response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin" : "*", # Required for CORS support, to work, also you should instead specify the proper origin if credentials are mandatory
"Access-Control-Allow-Credentials" : True # Required for cookies, authorization headers with HTTPS
},
body: json.dumps({ "message": "Hello World!" })
}
callback(null, response);
}
Upvotes: 1
Reputation: 161
For those using Cognito authorizers in API Gateway, there's actually no need to set custom Gateway Responses. The API Gateway blocks pre-flight because they're "unauthorized" by default AWS logic.
Fortunately, there's a built-in parameter to fix this. Simply add AddDefaultAuthorizerToCorsPreflight: False
to your API Authorizer and API Gateway will disable authentication for pre-flight requests. Here's the documentation, and an example setup:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Cors:
AllowHeaders: "'*'"
AllowMethods: "'*'"
AllowOrigin: "'*'"
Auth:
DefaultAuthorizer: MyCognitoAuthorizer
AddDefaultAuthorizerToCorsPreflight: False
Authorizers:
MyCognitoAuthorizer:
UserPoolArn: !GetAtt MyCognitoUserPool.Arn
Upvotes: 2
Reputation: 2695
Make sure you're calling the right path.
Hitting a non-existing path, may cause CORS related errors, for whatever reason. Probably due to the fact that the 404
doesn't include CORS headers in its response.
Thanks to @jackko's comment on the initial question. This was my problem. Sounds silly but can happen to anyone.
Upvotes: 2
Reputation: 2415
In my case I enabled all the methods and gateway responses. Then it worked like a charm. Don't forget to deploy.
Upvotes: 3
Reputation: 41
For me, as I was using pretty standard React fetch calls, this could have been fixed using some of the AWS Console and Lambda fixes above, but my Lambda returned the right headers (I was also using Proxy mode) and I needed to package my application up into a SAM Template, so I could not spend my time clicking around the console.
I noticed that all of the CORS stuff worked fine UNTIL I put Cognito Auth onto my application. I just basically went very slow doing a SAM package / SAM deploy with more and more configurations until it broke and it broke as soon as I added Auth to my API Gateway. I spent a whole day clicking around wonderful discussions like this one, looking for an easy fix, but then ended up having to actually read about what CORS was doing. I'll save you the reading and give you another easy fix (at least for me).
Here is an example of an API Gateway template that finally worked (YAML):
Resources:
MySearchApi:
Type: AWS::Serverless::Api
Properties:
StageName: 'Dev'
Cors:
AllowMethods: "'OPTIONS, GET'"
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
Auth:
DefaultAuthorizer: MyCognitoSearchAuth
Authorizers:
MyCognitoSearchAuth:
UserPoolArn: "<my hardcoded user pool ARN>"
AuthType: "COGNITO_USER_POOLS"
AddDefaultAuthorizerToCorsPreflight: False
Note the AddDefaultAuthorizerToCorsPreflight at the bottom. This defaults to True if you DON'T have it in your template, as as far as I can tell from my reading. And, when True, it sort of blocks the normal OPTIONS behavior to announce what the Resource supports in terms of Allowed Origins. Once I explicitly added it and set it to False, all of my issues were resolved.
The implication is that if you are having this issue and want to diagnose it more completely, you should visit your Resources in API Gateway and check to see if your OPTIONS method contains some form of Authentication. Your GET or POST needs Auth, but if your OPTIONS has Auth enabled on it, then you might find yourself in this situation. If you are clicking around the AWS console, then try removing from OPTIONS, re-deploy, then test. If you are using SAM CLI, then try my fix above.
Upvotes: 4
Reputation: 1028
For me, the answer that FINALLY WORKED, was the comment from James Shapiro from Alex R's answer (second most upvoted). I got into this API Gateway problem in the first place, by trying to get a static webpage hosted in S3 to use lambda to process the contact-us page and send an email. Simply checking [ ] Default 4XX fixed the error message.
Upvotes: 14
Reputation: 1668
I just added headers to my lambda function response and it worked like a charm
exports.handler = async (event) => {
const response = {
statusCode: 200,
body: JSON.stringify('Hey it works'),
headers:{ 'Access-Control-Allow-Origin' : '*' }
};
return response;
};
Upvotes: 11
Reputation: 5838
For Googlers:
Here is why:
GET
/POST
with no cookies do not trigger preflightOPTIONS
method for that path, then send Allow-Origin
headers using mock responses when user calls OPTIONS
, but GET
/ POST
will not get Allow-Origin
automaticallyAllow-Origin
headerOPTIONS
To sum it up:
OPTIONS
will be generated by API Gateway automaticallyOPTIONS
are only used by browser as a precautious measure to check possibility of CORS on a pathGET
/ POST
Upvotes: 12
Reputation: 41
Deploying the code after enabling CORS for both POST
and OPTIONS
worked for me.
Upvotes: 3
Reputation: 4335
In my case, since I was using AWS_IAM as the Authorization method for API Gateway, I needed to grant my IAM role permissions to hit the endpoint.
Upvotes: 1
Reputation: 670
In addition to others comments, something to look out for is the status returned from your underlying integration and if the Access-Control-Allow-Origin header is returned for that status.
Doing the 'Enable CORS' thing only sets up 200 status. If you have others on the endpoint, e.g 4xx and 5xx, you need to add the header yourself.
Upvotes: 0
Reputation: 7
In Python you can do it as in the code below:
{ "statusCode" : 200,
'headers':
{'Content-Type': 'application/json',
'Access-Control-Allow-Origin': "*"
},
"body": json.dumps(
{
"temperature" : tempArray,
"time": timeArray
})
}
Upvotes: -4
Reputation: 24488
I found a simple solution within
API Gateway > Select your API endpoint > Select the method (in my case it was the POST)
Now there is a dropdown ACTIONS > Enable CORS .. select it.
Now select the dropdown ACTIONS again > Deploy API (re-deploy it)
It worked !
Upvotes: 9
Reputation: 11
Another root cause of this problem might be a difference between HTTP/1.1 and HTTP/2.
Symptom: Some users, not all of them, reported to get a CORS error when using our Software.
Problem: The Access-Control-Allow-Origin
header was missing sometimes.
Context: We had a Lambda in place, dedicated to handling OPTIONS
request and replying with the corresponding CORS headers, such as Access-Control-Allow-Origin
matching a whitelisted Origin
.
Solution: The API Gateway seems to transform all headers to lower-case for HTTP/2 calls, but maintains capitalization for HTTP/1.1. This caused the access to event.headers.origin
to fail.
Check if you're having this issue too:
Assuming your API is located at https://api.example.com
, and your front-end is at https://www.example.com
. Using CURL, make a request using HTTP/2:
curl -v -X OPTIONS -H 'Origin: https://www.example.com' https://api.example.com
The response output should include the header:
< Access-Control-Allow-Origin: https://www.example.com
Repeat the same step using HTTP/1.1 (or with a lowercase Origin
header):
curl -v -X OPTIONS --http1.1 -H 'Origin: https://www.example.com' https://api.example.com
If the Access-Control-Allow-Origin
header is missing, you might want to check case sensitivity when reading the Origin
header.
Upvotes: 1
Reputation: 1786
If anyone else is running into this still - I was able to track down the root cause in my application.
If you are running API-Gateway with custom Authorizers - API-Gateway will send a 401 or 403 back before it actually hits your server. By default - API-Gateway is NOT configured for CORS when returning 4xx from a custom authorizer.
Also - if you happen to be getting a status code of 0
or 1
from a request running through API Gateway, this is probably your issue.
To fix - in the API Gateway configuration - go to "Gateway Responses", expand "Default 4XX" and add a CORS configuration header there. i.e.
Access-Control-Allow-Origin: '*'
Make sure to re-deploy your gateway - and voila!
Upvotes: 164
Reputation: 7855
In my case, I was simply writing the fetch request URL wrong. On serverless.yml
, you set cors
to true
:
register-downloadable-client:
handler: fetch-downloadable-client-data/register.register
events:
- http:
path: register-downloadable-client
method: post
integration: lambda
cors: true
stage: ${self:custom.stage}
and then on the lambda handler you send the headers, but if you make the fetch request wrong on the frontend, you're not going to get that header on the response and you're going to get this error. So, double check your request URL on the front.
Upvotes: -3
Reputation: 14535
I am running aws-serverless-express
, and in my case needed to edit simple-proxy-api.yaml
.
Before CORS was configured to https://example.com
, I just swapped in my site's name and redeployed via npm run setup
, and it updated my existing lambda/stack.
#...
/:
#...
method.response.header.Access-Control-Allow-Origin: "'https://example.com'"
#...
/{proxy+}:
method.response.header.Access-Control-Allow-Origin: "'https://example.com'"
#...
Upvotes: 1
Reputation: 2634
If you have tried everything regarding this issue to no avail, you'll end up where I did. It turns out, Amazon's existing CORS setup directions work just fine... just make sure you remember to redeploy! The CORS editing wizard, even with all its nice little green checkmarks, does not make live updates to your API. Perhaps obvious, but it stumped me for half a day.
Upvotes: 48
Reputation: 393
I got mine working after I realised that the lambda authoriser was failing and for some unknown reason that was being translated into a CORS error. A simple fix to my authoriser (and some authoriser tests that I should have added in the first place) and it worked. For me the API Gateway action 'Enable CORS' was required. This added all the headers and other settings I needed in my API.
Upvotes: 3
Reputation:
Got my sample working: I just inserted 'Access-Control-Allow-Origin': '*', inside headers:{} in the generated nodejs Lambda function. I made no changes to the Lambda-generated API layer.
Here's my NodeJS:
'use strict';
const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();
exports.handler = ( event, context, callback ) => {
const done = ( err, res ) => callback( null, {
statusCode: err ? '400' : '200',
body: err ? err.message : JSON.stringify(res),
headers:{ 'Access-Control-Allow-Origin' : '*' },
});
switch( event.httpMethod ) {
...
}
};
Here's my AJAX call
$.ajax({
url: 'https://x.execute-api.x-x-x.amazonaws.com/prod/fnXx?TableName=x',
type: 'GET',
beforeSend: function(){ $( '#loader' ).show();},
success: function( res ) { alert( JSON.stringify(res) ); },
error:function(e){ alert('Lambda returned error\n\n' + e.responseText); },
complete:function(){ $('#loader').hide(); }
});
Upvotes: 14
Reputation: 5175
1) I needed to do the same as @riseres and some other changes.This are my response headers:
headers: {
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Headers':'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
'Access-Control-Allow-Credentials' : true,
'Content-Type': 'application/json'
}
2) And
According to this documentation:
http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html
When you use proxy for lambda functions on API Gateway config, the post or get methods have no added headers, only the options does. You must do it manually in the response(server or lambda response).
3) And
Beside that, I needed to disable the 'API Key Required' option in my API gateway post method.
Upvotes: 28