The core issue is that your browser, acting as a client, is refusing to process a response from your API Gateway because the origin of the request (your frontend domain) isn’t explicitly allowed in the API Gateway’s Cross-Origin Resource Sharing (CORS) configuration. This is a security feature designed to prevent malicious websites from making requests to your API on behalf of a logged-in user.

Here are the common culprits and how to fix them:

1. Missing CORS Configuration on API Gateway Resources

  • Diagnosis: You haven’t enabled CORS for the specific API Gateway methods (e.g., GET, POST on /users) that your frontend is trying to access.
  • Fix: In the AWS Console, navigate to your API Gateway, select your API, and then choose "Resources." For each method (GET, POST, PUT, DELETE, OPTIONS, PATCH, ANY) that your frontend uses, select it, click "Actions," and then "Enable CORS." AWS will automatically create an OPTIONS method and configure the necessary headers.
  • Why it works: Enabling CORS tells API Gateway to respond to OPTIONS preflight requests with headers that explicitly permit requests from your frontend’s origin, along with allowed HTTP methods and headers.

2. Incorrect Access-Control-Allow-Origin Header

  • Diagnosis: API Gateway is configured for CORS, but the Access-Control-Allow-Origin header in the response doesn’t match the origin of your frontend’s request. This often happens when using wildcards (*) in development and then forgetting to restrict it in production, or vice-versa.
  • Fix:
    • Specific Origin: In the API Gateway console, under your API’s "Resources," select the OPTIONS method for the resource you’re trying to access. Under "Integration Response," find the OPTIONS method response, expand "Header Mappings." Ensure Access-Control-Allow-Origin is mapped to your frontend’s exact domain (e.g., https://your-frontend.com).
    • Multiple Origins (if applicable): If you need to allow multiple specific origins, you’ll need a Lambda authorizer or a Lambda function behind your API Gateway to dynamically set this header based on the incoming Origin header. You cannot specify multiple origins directly in the API Gateway console’s static mapping.
  • Why it works: The browser strictly checks if the Access-Control-Allow-Origin header in the API Gateway’s response exactly matches the Origin header sent by the browser. A mismatch, or a wildcard that doesn’t cover the requesting origin, causes the browser to block the request.

3. Missing Access-Control-Allow-Methods Header

  • Diagnosis: The OPTIONS preflight response from API Gateway doesn’t include the HTTP method your frontend is trying to use (e.g., POST is missing).
  • Fix: When you "Enable CORS" on a resource in API Gateway, it automatically populates Access-Control-Allow-Methods with the methods enabled for that resource. Double-check that the methods used by your frontend (e.g., GET,POST,PUT,DELETE,OPTIONS) are listed in the Access-Control-Allow-Methods header mapping for the OPTIONS method’s integration response.
  • Why it works: The browser checks if the requested HTTP method is permitted by the Access-Control-Allow-Methods header. If it’s not present, the request is blocked.

4. Missing Access-Control-Allow-Headers Header

  • Diagnosis: Your frontend is sending custom headers (like Authorization for tokens, or Content-Type with values other than application/json, application/x-www-form-urlencoded, multipart/form-data), and these aren’t allowed by API Gateway.
  • Fix: In the OPTIONS method’s integration response header mappings, ensure Access-Control-Allow-Headers includes all the custom headers your frontend sends. A common set is Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token. If you have custom headers, add them to this comma-separated list.
  • Why it works: The browser verifies that any custom headers sent in the actual request are permitted by the Access-Control-Allow-Headers header in the OPTIONS preflight response.

5. Incorrect Deployment of API Gateway Changes

  • Diagnosis: You’ve made CORS configuration changes in the API Gateway console, but you haven’t deployed them to a stage.
  • Fix: After making any changes to your API Gateway configuration, you must deploy it. In the API Gateway console, click "Actions," then "Deploy API." Select the stage you want to deploy to (e.g., dev, prod).
  • Why it works: API Gateway operates on deployed stages. Changes made in the editor are not live until they are explicitly deployed to a stage, making them available to clients via the stage’s URL.

6. Browser Cache Issues

  • Diagnosis: Sometimes, the browser aggressively caches the CORS preflight (OPTIONS) response. Even after you fix the API Gateway configuration, the browser might still be using an old, invalid response.
  • Fix:
    • Hard Refresh: In Chrome, open Developer Tools (F12), go to the "Network" tab, check "Disable cache," and then perform a hard refresh (Ctrl+Shift+R or Cmd+Shift+R).
    • Clear Site Data: In your browser’s settings, clear cache and cookies for the specific domain.
    • Incognito/Private Window: Test in an incognito or private browsing window, which typically doesn’t use existing cache.
  • Why it works: This forces the browser to re-request the OPTIONS preflight and the actual API request, ensuring it uses the latest CORS headers from your API Gateway.

7. Misconfigured Lambda Authorizer (if used)

  • Diagnosis: If you’re using a Lambda authorizer to control access and dynamically set CORS headers, the authorizer itself might be misconfigured or not returning the correct CORS headers.
  • Fix: Ensure your Lambda authorizer function returns a policy object and a headers object within its response. This headers object must contain the correct Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers values. For example:
    {
        "principal": "user|role",
        "policyDocument": { ... },
        "context": { ... },
        "headers": {
            "Access-Control-Allow-Origin": "https://your-frontend.com",
            "Access-Control-Allow-Methods": "GET,POST,OPTIONS",
            "Access-Control-Allow-Headers": "Content-Type,Authorization"
        }
    }
    
    Also, ensure the API Gateway method integration is configured to pass through these headers from the authorizer.
  • Why it works: When a Lambda authorizer is used, it can dynamically generate the CORS headers. If these headers are missing or incorrect in the authorizer’s response, the browser will still reject the request.

After addressing these, the next error you’re likely to encounter is a 403 Forbidden, indicating that while CORS is now allowed, the underlying API resource or method is not accessible due to IAM permissions, authorizer logic, or resource policy restrictions.

Want structured learning?

Take the full Apigateway course →