I've been struggling so long with the error below. I've tried so many tutorials and stackoverflow answers and none of the solutions fixes my problem.
Access to XMLHttpRequest at 'https://xxx' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I'm using SAM serverless to create my api.
template.yaml:
Globals:
Function:
Timeout: 10
Api:
Cors:
AllowMethods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
My lambda function: Both my GET response and OPTIONS response has the following headers that is returned:
headers: {
"Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
"Access-Control-Allow-Origin": "'*'",
"Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
}
My API get in my ReactJs application using amplify:
API.get(apiName, path, {
headers: {
"Access-Control-Allow-Origin": "*",
// "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with",
// "Access-Control-Allow-Methods": "OPTIONS,POST,GET,PUT,DELETE",
// 'Content-Type': 'application/json'
}
})
I have tried every combination of Access-Control-Allow-Headers, Access-Control-Allow-Methods in my template.yaml, my lambda function and my reactJs project.
Here is what my result is when I call options in postman on my API endpoint. Thus I do get the correct headers back so per my understanding my API is allowing CORS.
"Access-Control-Allow-Headers": "'Content-Type'"
but I still get the 403 Forbidden. I'm not sure if that is what you mean? I am including this response in case anyone is still pulling their hair out with this error. I spent a day trying to figure this out - reading everything I could about CORS and even migrating my code to the new AWS HttpAPI (which is probably a good thing anyway). Bottom line, I was fixated on the error message:
Access to fetch at 'https://api.example.com/user/list' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Which could point you in the wrong direction. My issue had nothing to do with the "Access-Control-Allow-Origin" header. In fact the answer to my issue was very simple, I was sending a JWT Token as part of the "Authorization" header, and I was not including a "Access-Control-Allow-Headers" entry in my response - duh! (but it would have been nice if the error message had been more accurate...)
If you are reading this you have probably already found many good sources of information on the web, but this site was very useful to me: www.test-cors.org
So after a very helpfull discussion with @Jannes Botis I found the solution.
In template.yaml I changed my values to:
Globals:
Function:
Timeout: 10
Api:
Cors:
AllowMethods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
AllowHeaders: "'Content-Type,X-Amz-Date,X-Amz-Security-Token,Authorization,X-Api-Key,X-Requested-With,Accept,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Access-Control-Allow-Headers'"
AllowOrigin: "'*'"
MyAPIFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: myendpoint/
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events:
GetMyData:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /myendpoint
Method: get
Options:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /myendpoint
Method: options
Auth:
ApiKeyRequired: false
Note: You will get error "No 'xxx' header is present on the requested resource." where xxx is either Access-Control-Allow-Methods, Access-Control-Allow-Origin and Access-Control-Allow-Headers, thus you need to add them in your AllowHeaders. Also note that you have to add an Options resource with ApiKeyRequired: false.
Then your response from your options and get request should have the same headers:
headers: {
"Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,X-Amz-Security-Token,Authorization,X-Api-Key,X-Requested-With,Accept,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Access-Control-Allow-Headers",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT",
"X-Requested-With": "*"
}
Note: 'Accept' MUST BE PRESENT otherwise you will get "Response to preflight request doesn't pass access control check: It does not have HTTP ok status.".
Your preflight must be able to pass a 200 OK when you ommit the x-api-key in postman.
allowHeaders:['Content-Type', 'X-Amz-Date', 'X-Amz-Security-Token', 'Authorization', 'X-Api-Key', 'X-Requested-With', 'Accept', 'Access-Control-Allow-Methods', 'Access-Control-Allow-Origin', 'Access-Control-Allow-Headers']
in your corsPreflight
parameter. This error can also occur if you are trying to access the wrong URL. D'oh. Before you descend into CORS madness, confirm that the request URL is correct and the resource is available.
In my case, I was trying to access the backend at a wrong Lightsail URL. Sending a POST request to this incorrect URL triggered the CORS error. With the correct URL everything worked fine.
If Someone using serverless with nodejs and facing this issue of prelight with cors-policy, here is the simple solution ...
functions:
externalHandler:
handler: handler.handlerExport
events:
- http:
path: handlerPath
method: post
cors:
origin: '*' # <-- Specify allowed origin
headers: # <-- Specify allowed headers
- Content-Type
- X-Amz-Date
- Accept
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
allowCredentials: false
inside the handler file
if you are using express
...
const app = express();
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Credentials", true);
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
res.header(
"Access-Control-Allow-Headers",
"x-www-form-urlencoded, Origin, X-Requested-With, Content-Type, Accept, Authorization"
);
next();
});
...
or in general
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
product: product
}),
};
for more information refer serverless article