Skip to content

CORS Error on API GET request

0

Hi,

I am getting a CORS Error: Access to fetch at 'https://api.xxxxxx.io/root/' from origin 'https://client.xxxxxx.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource when I try to access application from the browser. When I test the api in postman, I am not getting any CORS related errors.

I believe the issue is rooted somewhere in API Gateway but I have not been able find the root cause of the issues.

I hope you can help

asked a month ago107 views

3 Answers
1

The agent's answer covers the basics correctly. Here are the common reasons this issue persists even after enabling CORS in API Gateway, particularly for REST APIs with proxy integrations.

Why it works in Postman but not the browser

Postman does not enforce the Same-Origin Policy. Browsers do. The browser sends a preflight OPTIONS request before the actual GET, and blocks the response if CORS headers are absent. Postman skips this entirely.

The most common cause of "I enabled CORS but still get the error"

If you are using a REST API with Lambda proxy integration (or HTTP proxy integration), API Gateway passes the response directly from your backend without modification. Your backend must return the CORS headers itself:

return {
    "statusCode": 200,
    "headers": {
        "Access-Control-Allow-Origin": "https://client.xxxxxx.io",
        "Access-Control-Allow-Methods": "GET, OPTIONS",
        "Access-Control-Allow-Headers": "Content-Type,Authorization"
    },
    "body": json.dumps(response_data)
}

If your Lambda throws an unhandled exception or times out, API Gateway generates a 5xx response that does NOT include your CORS headers. The browser sees the missing Access-Control-Allow-Origin header and reports a CORS error, masking the actual backend failure.

Gateway Responses (the commonly missed step)

For REST APIs, errors generated by API Gateway itself (throttling, authorization failures, invalid resource path, WAF blocks) bypass your integration entirely. These responses will not have CORS headers unless you explicitly configure Gateway Responses:

  1. In the API Gateway console, select your API
  2. Go to Gateway responses in the left panel
  3. Configure DEFAULT_4XX and DEFAULT_5XX responses to include:
    • Access-Control-Allow-Origin: 'https://client.xxxxxx.io'
    • Access-Control-Allow-Headers: 'Content-Type,Authorization'
  4. Deploy the API to apply changes

Without this, any 4xx/5xx generated before the request reaches your Lambda (e.g., 403 from a missing auth token, 429 from throttling) will appear as a CORS error in the browser.

Troubleshooting steps

  1. Open your browser's Developer Tools, go to the Network tab
  2. Look for TWO requests: an OPTIONS preflight and the actual GET
  3. Check which one is failing:
    • If the OPTIONS request fails: the CORS configuration on the OPTIONS method is incorrect
    • If the OPTIONS succeeds but the GET fails: your backend is not returning CORS headers, or a Gateway Response is generating an error without CORS headers
  4. Test the OPTIONS method directly:
curl -v -X OPTIONS https://api.xxxxxx.io/root/ \
  -H "Origin: https://client.xxxxxx.io" \
  -H "Access-Control-Request-Method: GET"

Check that the response includes Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers.

  1. Test the actual GET with the Origin header:
curl -v https://api.xxxxxx.io/root/ \
  -H "Origin: https://client.xxxxxx.io"

If this returns a 5xx without CORS headers, your backend is failing and Gateway Responses are not configured.

If using an HTTP API (not REST API)

HTTP APIs handle CORS differently. Configure it under your API's CORS settings, and HTTP APIs will automatically add CORS headers to both success and error responses, including preflight handling. This is simpler than REST APIs where you must handle it in multiple places.

References:

AWS
SUPPORT ENGINEER

answered a month ago

  • Thank you for your response. I have tried the steps you suggested, but I am still experiencing CORS errors. After further investigation I noticed that the browser request is only sending the standard headers (Host/:authority, Origin, and Authorization) and is not sending "X-Forwarded-Proto" or "X-Forwarded-Host" which I believe is expected.

    I am currently using an API Gateway REST API with a VPC Link proxy integration to an internal ALB. The API Gateway integration request is currently mapping:

    • "X-Forwarded-Proto" --> "method.request.header.X-Forwarded-Proto"
    • "X-Forwarded-Host" --> "method.request.header.X-Forwarded-Host"

    Since these headers are not present in the browser request, I am thinking of changing the mappings to:

    • "X-Forwarded-Proto " --> static value "https"
    • "X-Forwarded-Host" --> "method.request.header.Host"

    My understanding is that this would allow the backend application to receive the original public request context and generate HTTPS urls correctly

0

You're on the right track, and I think you've almost found it.

First thing to confirm: browsers never send X-Forwarded-Proto or X-Forwarded-Host. Those headers are added by proxies, not by the browser. So your integration mapping that reads them from method.request.header.X-Forwarded-Proto and method.request.header.X-Forwarded-Host was always coming back empty. Your read on that is correct.

Now, here's how that causes the CORS error, which is the part that's easy to miss. When your backend doesn't receive a proper protocol and host, many app frameworks fall back to what they see locally — plain HTTP and the internal ALB hostname. The app then thinks the request came in on the "wrong" URL and sends back a redirect to what it believes is the correct one (http to https, a host fix, or a trailing-slash fix). If that redirect points to a different scheme or host than your client origin (for example https://client.example.com), the browser refuses to follow it. Per MDN, a CORS request that gets answered with a 301/307/308 redirect to a different origin is "not permitted during CORS requests" — and the browser reports it as the generic "No 'Access-Control-Allow-Origin' header" message. That's why it looks like a CORS setup problem even though your CORS config is fine. Postman doesn't follow that redirect rule, so it works there. So this isn't really a CORS misconfiguration — it's a redirect the browser won't follow.

So yes, your proposed mapping change is the right idea. Two small notes on it:

  • X-Forwarded-Proto to static value 'https' — correct. Make sure you include the single quotes ('https') so API Gateway treats it as a literal value. Also drop the extra space in the header name from your snippet ("X-Forwarded-Proto " should be "X-Forwarded-Proto").
  • X-Forwarded-Host — this works, but instead of method.request.header.Host I'd use the context variable context.domainName. The docs describe it as "the full domain name used to invoke the API… the same as the incoming Host header," so it's the documented, reliable source for your public API host. In the integration request mapping field, type it as context.domainName (no $ at the start).

Before and after the change, check the actual behavior with curl so you know you fixed the real cause and didn't just move it around:

curl -v https://api.example.com/root/ -H "Origin: https://client.example.com"

If you see a 3xx response with a Location header pointing to http:// or the internal ALB host, that's the redirect the browser is blocking. Once the backend receives proto=https and the correct public host, it should stop sending that redirect and return your 200 (with the CORS headers) directly. You'll see the same thing in the browser Network tab — a redirected or failed request instead of a clean 200.

References:

AWS

answered a month ago

EXPERT

reviewed a month ago

  • Thank you for getting back to me. I have attempted to change the headers to add in the domainName/Host and static Forwarded-Proto, however I am still runnign into the same error. When I run it in Postman, without Headers the URI I am receiving is of my ALB. If I add in the two Headers, its returning the correct URI. I might have missed a key bit of detail, all this is in connection with moving from AppMesh to Service Connect. You mentioned headers are added by proxies, would this be something the Envoy Proxy would have been doing with App Mesh?

    This is the response I am receiving:

    Request URL: https://api.example.io/root/ Request Method: GET Status Code: 301 Moved Permanently

0

The CORS error you're experiencing is a browser-specific security feature, which explains why your API works in Postman but fails in the browser. Postman doesn't enforce CORS policies the way browsers do, so it won't show these errors.

The error message indicates that your API response is missing the required Access-Control-Allow-Origin header. To resolve this in API Gateway, you need to configure CORS properly:

For HTTP APIs: You can enable CORS configuration in API Gateway, which will automatically handle preflight OPTIONS requests and add the necessary CORS headers to responses. You'll need to configure:

  • Access-Control-Allow-Origin: Set this to your specific origin (https://client.xxxxxx.io) or use * to allow all origins
  • Access-Control-Allow-Methods: Include the HTTP methods your API uses (GET, POST, etc.)
  • Access-Control-Allow-Headers: Specify any custom headers your requests use

For REST APIs: The approach depends on your integration type:

  • For non-proxy integrations: API Gateway can create an OPTIONS method and add the necessary headers, though you may need to manually configure the Access-Control-Allow-Origin header in your method responses
  • For proxy integrations (like Lambda proxy or HTTP proxy): Your backend is responsible for returning the CORS headers in the response

Important note: Your API must receive requests with an Origin header for CORS to work properly. For OPTIONS requests, both an Origin header and an Access-Control-Request-Method header are required.

Make sure to test your changes in the browser after configuring CORS, as the behavior will differ from Postman's non-browser environment.
Sources
Configure CORS for HTTP APIs in API Gateway - Amazon API Gateway
CORS for REST APIs in API Gateway - Amazon API Gateway

answered a month ago

AWS
SUPPORT ENGINEER

reviewed a month ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.