I'm using an AWS HTTP API configured with CDK. It routes several endpoints to Lambda functions, and one POST endpoint to a state-machine integration that starts an ephemeral ECS task. The API has CORS preflight settings allowing all origins, common methods including PUT, and all headers. Some routes use a Cognito authorizer, while others are unauthenticated.
When the browser tries to send a PUT request to `/foo/xyz`, it first sends an OPTIONS preflight request. That OPTIONS request reaches my Lambda function, which does not handle OPTIONS and returns a 405. The browser then reports a CORS error. The response includes the expected `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, and `Access-Control-Allow-Headers` values, but the request is still routed incorrectly.
I expected the API Gateway CORS configuration to handle the preflight request directly rather than forwarding it to Lambda. My original HTTP API configuration included `defaultIntegration: lambdaIntegration`, along with an explicit catch-all route. Removing `defaultIntegration` fixed the problem. Why did that setting cause the OPTIONS request to reach Lambda?
3 Answers
The headers shown in the preflight response looked broadly correct: the requested method was allowed, Authorization and Content-Type were allowed, and the origin wildcard was present. That points away from an origin or header whitelist problem and toward the 405 response being generated by the Lambda route. Check which integration handles OPTIONS in the API’s deployed route configuration, not just whether the CORS headers exist.
The important part was `defaultIntegration: lambdaIntegration`. On an HTTP API, setting a default integration can cause otherwise-unmatched requests—including the preflight OPTIONS request—to use that integration. Since the Lambda only handled the application methods and not OPTIONS, it returned 405. Removing the default integration allowed the API’s CORS preflight handling to take precedence, which resolved the issue.
The browser is doing the correct thing: because the request uses PUT and sends headers such as Authorization and Content-Type, it must perform an OPTIONS preflight first. API Gateway should answer that request itself using the configured CORS preflight response; it should not need to invoke the Lambda just to handle OPTIONS.
That matches what I expected. The OPTIONS response had the CORS headers, but it was still returning 405 because the request was reaching the function.

The catch-all route can make routing harder to reason about too, but the confirmed fix here was removing the default integration. Keep integrations explicit on each route instead of relying on a default when configuring CORS this way.