Fixing the AWS API Gateway CORS Error: 'No Access-Control-Allow-Origin' Header

beginner☁️ AWS2026-03-31| AWS API Gateway (REST API), AWS Lambda, Modern Browsers (Chrome/Firefox/Safari)

Error Message

Access to fetch at 'https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/resource' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
#api-gateway#cors#lambda#aws#http

The Frustration of the CORS Block

You’ve just deployed a shiny new API using AWS API Gateway and Lambda. It works perfectly in Postman. The JSON comes back clean, the status code is a beautiful 200 OK, and you're ready to celebrate. But as soon as you call it from your React or Vue frontend at https://example.com, the browser shuts you down.

Your console suddenly screams in red text:

Access to fetch at 'https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/resource' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

This happens because of Cross-Origin Resource Sharing (CORS). Think of it as a security guard in the browser. Unless your server explicitly hands over a "permission slip" via HTTP headers, the browser won't let your frontend touch the data.

Step 1: Check the Network Tab

Before you start hacking at your code, open your browser's DevTools (F12) and head to the Network tab. Trigger your API call again. You’ll usually see two distinct requests:

  • OPTIONS Request (The Preflight): The browser is essentially asking the server, "Is it safe for me to send a POST request from this domain?"
  • The Actual Request: This is your real GET or POST call. It only fires if the server gives the green light to the OPTIONS request.

If that OPTIONS request fails with a 403 or 404, or is missing the Access-Control-Allow-Origin header, the second request will never even happen.

Step 2: Configure CORS in the API Gateway Console

The first step is telling API Gateway how to respond to those Preflight requests automatically.

  • Open the AWS Management Console and go to API Gateway.
  • Select your API and navigate to the specific Resource (e.g., /resource).
  • In the Actions menu (or the dedicated CORS button in newer UI versions), select Enable CORS.
  • Set Access-Control-Allow-Origin to '*' for quick testing, but use 'https://example.com' for production security.
  • Verify that OPTIONS is selected in the methods list.
  • Click Enable CORS and replace existing CORS headers.

Don't skip the deployment. This is the #1 mistake engineers make. Changes in the console aren't live until you click Actions > Deploy API and select your stage (like prod or dev). If you don't deploy, your API is still running the old, broken configuration.

Step 3: Fix Lambda Proxy Integration Headers

Are you using Lambda Proxy Integration? If so, the console settings you just changed only fix the OPTIONS request. They don't touch the actual data response from your Lambda function.

In a Proxy Integration setup, your Lambda is in total control. If your code doesn't explicitly return the CORS headers in its response object, the browser will still block you. It doesn't matter how many buttons you clicked in the AWS console.

Example: Node.js Fix

exports.handler = async (event) => {
    return {
        statusCode: 200,
        headers: {
            "Access-Control-Allow-Origin": "https://example.com",
            "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
            "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key"
        },
        body: JSON.stringify({ message: "It works!" }),
    };
};

Example: Python Fix

import json

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'headers': {
            'Access-Control-Allow-Origin': 'https://example.com',
            'Access-Control-Allow-Headers': 'Content-Type',
            'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
        },
        'body': json.dumps({'message': 'Success!'})
    }

Step 4: Handling Error States

One sneaky trap: what happens when your Lambda crashes? If your catch block returns a 500 error but forgets the CORS headers, the browser will hide the 500 and show a "CORS Error" instead. This sends developers on a wild goose chase looking at API Gateway settings when the real problem is a bug in their code.

Always wrap your logic in a try/catch and ensure your error responses include the same Access-Control-Allow-Origin header. If you use Cognito or API Keys, you also need to check the "Gateway Responses" section in API Gateway to ensure 401 or 403 errors pass through the CORS headers.

Verification with cURL

Stop refreshing the browser to test. Use curl in your terminal to see exactly what the server is sending back:

# Test the Preflight (OPTIONS)
curl -v -X OPTIONS https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/resource \
-H "Origin: https://example.com" \
-H "Access-Control-Request-Method: POST"

# Check the output for this specific line:
# < Access-Control-Allow-Origin: https://example.com

Summary Checklist

  • Postman lies: It doesn't enforce CORS, so it's a poor test for browser compatibility.
  • Proxy Integration is manual: Your Lambda code must return the headers for GET/POST.
  • Deployment is key: No deployment, no fix. Period.
  • Errors are CORS in disguise: If your Lambda fails, a missing header will mask the real status code.

Related Error Notes