401 return from an API Gateway Custom Authorizer is missing 'Access-Control-Allow-Origin' header
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
When an API Gateway custom authorizer rejects a request, the 401 response is usually generated by API Gateway itself before your backend integration runs. That is why the normal CORS headers from your Lambda or HTTP backend never appear. To include Access-Control-Allow-Origin on those 401 responses, you need to configure the gateway-level error responses, not just the integration response.
Why the Header Is Missing
In a browser, a failed authenticated request still has to satisfy CORS rules. If API Gateway returns a 401 without Access-Control-Allow-Origin, the browser often reports a CORS failure instead of exposing the real authorization error to your frontend.
This happens because the flow looks like this:
- the browser sends the request
- API Gateway runs the custom authorizer
- authorization fails before the integration is invoked
- API Gateway generates the 401 directly
Since your Lambda or upstream service never runs, any CORS headers you set there are irrelevant for this response path.
Configure Gateway Responses for 401 Errors
For REST APIs in API Gateway, the usual fix is to customize the UNAUTHORIZED and often ACCESS_DENIED gateway responses. Add the CORS headers there so API Gateway includes them on authorizer-generated failures.
In the console, the relevant path is typically:
- API Gateway
- your REST API
- '
Gateway Responses' - edit
UNAUTHORIZED - add
Access-Control-Allow-Origin - usually also add
Access-Control-Allow-HeadersandAccess-Control-Allow-Methods
If your frontend sends credentials, do not use * blindly for the origin. Use the exact allowed origin instead.
Example With CloudFormation or SAM
A configuration example looks like this:
The extra quoting on the header values is important because API Gateway expects mapping-expression-style string literals in these response parameters.
Do Not Forget Preflight
Even after fixing the 401 path, your OPTIONS responses still need proper CORS headers. A browser may send a preflight request before the real authenticated request, especially when custom headers such as Authorization are involved.
So the full CORS setup usually requires:
- an
OPTIONSmethod or automatic CORS handling - allowed origin headers on success responses
- allowed origin headers on gateway-generated error responses
If any one of those pieces is missing, the frontend may still see a generic CORS failure.
Lambda Authorizer Output Does Not Solve This
A common misconception is that the custom authorizer can simply return extra headers with its deny result. For REST API custom authorizers, the authorizer mainly returns an IAM policy document and optional context, not the final HTTP response headers for the 401 generated by API Gateway.
That is why the fix belongs at the API Gateway response layer.
Common Pitfalls
The biggest pitfall is adding CORS headers only in the backend integration response. That helps for normal application responses, but not for authorizer-generated 401 responses.
Another pitfall is configuring only UNAUTHORIZED and forgetting ACCESS_DENIED or the default 4XX path. Different failure modes may return different gateway response types.
Developers also use Access-Control-Allow-Origin: * together with credentialed requests. Browsers reject that combination, so use an explicit origin when credentials are involved.
Finally, after changing gateway responses, redeploy the API if your deployment model requires it. Many CORS fixes fail simply because the updated gateway configuration was never deployed.
Summary
- A custom authorizer 401 is usually generated by API Gateway before your backend runs.
- Because of that, backend CORS headers do not appear on the 401 response.
- Configure API Gateway
Gateway Responses, especiallyUNAUTHORIZEDand oftenACCESS_DENIED, to add CORS headers. - Make sure preflight
OPTIONShandling is correct as well. - If credentials are involved, use a specific allowed origin instead of
*.
Related reading
- How do I find out which process is listening on a TCP or UDP port on Windows?
- Kafka: Consumer API vs Streams API
- What exactly is RESTful programming?
- A3C in Tensorflow - Should I use threading or the distributed Tensorflow API
- A certificate chain could not be built to a trusted root authority
- A complete solution to LOCALLY validate an in-app receipts and bundle receipts on iOS 7
- A clean, lightweight alternative to Python''s twisted?
- A single threaded client/server in C# with file upload, download

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.