FREE TOOL

AWS CORS Configuration Generator

Fix "No 'Access-Control-Allow-Origin' header" and other CORS errors on AWS, and generate the CORS configuration for S3, API Gateway, Lambda function URLs and CloudFront.

  • Your data never leaves your browser: everything is calculated by JavaScript on this page, not on a server.
  • Nothing you enter is uploaded, processed on a server or stored. Check it in your browser's developer tools (Network tab).
  • Once the page has loaded, the tool works without an internet connection.

Error or Lambda response

The CORS error from the browser console (Chrome, Firefox or Safari), with the failed request's line if it shows one; an API Gateway error body; or the JSON your Lambda function returns.

Your setup

What serves the request, and what the page calling it needs. Filled in from the error where it says.

METHODS
See result ↓

CORS configuration · API Gateway REST API

INDEX.MJS
const allowedOrigins = ['https://app.example.com', 'http://localhost:3000'];

const corsHeaders = (event) => {
  const origin = event.headers?.origin ?? event.headers?.Origin;
  if (!allowedOrigins.includes(origin)) {
    return {};
  }
  return {
    'Access-Control-Allow-Origin': origin,
    'Vary': 'Origin',
  };
};

export const handler = async (event) => {
  if (event.httpMethod === 'OPTIONS') {
    return {
      statusCode: 204,
      headers: {
        ...corsHeaders(event),
        'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type,Authorization',
        'Access-Control-Max-Age': '3600',
      },
      body: '',
    };
  }
  try {
    const result = {message: 'Hello'}; // your code
    return {
      statusCode: 200,
      headers: {...corsHeaders(event), 'Content-Type': 'application/json'},
      body: JSON.stringify(result),
    };
  } catch (error) {
    console.error(error);
    // Errors need the CORS headers too, or the browser shows a CORS error instead of this one.
    return {
      statusCode: 500,
      headers: corsHeaders(event),
      body: JSON.stringify({message: 'Internal server error'}),
    };
  }
};
GATEWAY-RESPONSES.JSON - CORS HEADERS ON API GATEWAY'S OWN ERRORS
{
  "gatewayresponse.header.Access-Control-Allow-Origin": "method.request.header.origin",
  "gatewayresponse.header.Access-Control-Allow-Headers": "'Content-Type,Authorization'"
}
APPLY THEM AND DEPLOY
aws apigateway put-gateway-response --rest-api-id abc123defg --response-type DEFAULT_4XX --response-parameters file://gateway-responses.json
aws apigateway put-gateway-response --rest-api-id abc123defg --response-type DEFAULT_5XX --response-parameters file://gateway-responses.json
aws apigateway create-deployment --rest-api-id abc123defg --stage-name prod
  • With a Lambda proxy integration the function returns the CORS headers itself, in every response - errors included. The console's "Enable CORS" only adds an OPTIONS method; if the resource has one, it answers preflight requests, otherwise OPTIONS reaches the function, which the code handles.
  • OPTIONS must not require an authorizer or API key: browsers send preflight requests without them.
  • The gateway responses return the request's Origin for any origin - they carry only API Gateway's error messages. List one origin to return it instead.
API Gateway, Lambda and CORS are core AWS Developer Associate exam topicsTry free DVA-C02 practice questions with answers and explanations.DVA-C02 questions →

How CORS works on AWS

A browser lets a page read a response from another origin - another scheme, host or port - only if the response says so with Access-Control-Allow-Origin. Requests with JSON, custom headers such as Authorization, or methods other than GET, HEAD and POST first send a preflight OPTIONS request, and the real request follows only if the preflight response allows its origin, method and headers. CORS is enforced by the browser alone: curl, Postman and server-side code never see a CORS error, which is why "it works in Postman" says nothing about it.

Each AWS service that answers browser requests configures CORS in its own way:

ServiceWhere CORS is setWatch out for
API Gateway REST APIAn OPTIONS method, plus headers in every response - from the Lambda function with a proxy integration"Enable CORS" does not change what a Lambda proxy returns; deploy after every change; API Gateway's own errors need gateway responses
API Gateway HTTP APIThe API's CORS configurationBackend CORS headers are ignored once it is set; an authorizer on $default or ANY catches OPTIONS
Lambda function URLThe function URL's CORS configurationHeaders from the code are added on top: duplicate Access-Control-Allow-Origin
Amazon S3The bucket's CORS configuration (up to 100 rules)Methods only GET, PUT, POST, DELETE, HEAD; no match means no CORS headers at all
CloudFrontA response headers policy, or the origin's own headers with Origin forwardedResponses cached without the Origin header; OPTIONS must be an allowed method

CORS errors are often other errors

When a request fails, the error response usually carries no CORS headers - API Gateway's Missing Authentication Token, an authorizer's 401, a Lambda function's crash, an S3 access denial. The browser then reports "No 'Access-Control-Allow-Origin' header" and hides the real error. Before changing any CORS setting, look at the status code in the Network tab of the developer tools, or send the request without a browser:

curl -i -X OPTIONS https://abc123defg.execute-api.us-east-1.amazonaws.com/prod/items \
  -H "Origin: http://localhost:3000" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: content-type,authorization"

A 2xx answer with Access-Control-Allow-* headers means the preflight passes; then repeat it with the real method. Paste the browser's message into the tool above: it reads the status code, the origin and the rejected header or method from it.

ErrorMost common cause on AWS
No 'Access-Control-Allow-Origin' header is presentThe request failed (look at the status), or a Lambda proxy integration does not return the header
Response to preflight request doesn't pass access control check: It does not have HTTP ok statusAn authorizer or API key on OPTIONS, or no OPTIONS method
Request header field ... is not allowed by Access-Control-Allow-HeadersThe header (often Authorization or x-api-key) is missing from the allowed headers
The 'Access-Control-Allow-Origin' header contains multiple valuesBoth a function URL or API Gateway and the code add the header
must not be the wildcard '*' when the request's credentials mode is 'include'Cookies are sent; list the origins and allow credentials
Missing Authentication TokenWrong path, method or stage in a REST API, or the API was not deployed
Internal server error (502)The Lambda function failed or returned a malformed proxy response

The Lambda proxy response format

With a Lambda proxy integration, API Gateway passes the function's return value to the browser, and it has to have this shape - anything else is a 502 Malformed Lambda proxy response:

{
  "statusCode": 200,
  "headers": {"Access-Control-Allow-Origin": "https://app.example.com", "Content-Type": "application/json"},
  "body": "{\"message\":\"Hello\"}",
  "isBase64Encoded": false
}

body is a string - the result of JSON.stringify or json.dumps, not the object itself - and every response, errors included, needs the CORS headers. Paste what your function returns into the tool to check it.

Frequently asked questions

Why does my API work in Postman or curl but not in the browser?

Only browsers enforce CORS. Postman and curl send the request and show the response whatever its headers say; the browser sends the same request and then refuses to give the response to your page.

I enabled CORS in API Gateway and still get the error. Why?

For a REST API with a Lambda proxy integration, "Enable CORS" only creates the OPTIONS method - the function has to return Access-Control-Allow-Origin itself. The change also takes effect only after you deploy the API. And if the request fails (401, 403, 502), the error response has no CORS headers unless gateway responses add them.

Can I allow several origins?

Access-Control-Allow-Origin holds one origin or *. S3, HTTP APIs, function URLs and CloudFront take a list and answer with the matching one; in Lambda code, compare the request's Origin with your list, return it when it matches and add Vary: Origin - the generated code does.

Is it safe to paste my error here?

Yes. Everything runs in your browser: nothing you enter is uploaded, processed on a server or stored. The page only counts that it was used and which error it recognized, never the text.

References

CORS for REST APIs in API Gateway
Configure CORS for HTTP APIs in API Gateway
CORS for Lambda function URLs
Elements of an S3 CORS configuration
CloudFront response headers policies: CORS headers
Output format of a Lambda function for proxy integration