Enable CORS with custom headers for an AWS Lambda function behind API Gateway in Serverless framework
Tags: serverless, aws
Published: 2020-04-09 06:37:58

To properly enable CORS with custom headers for a Lambda function deployed behind API Gateway using Serverless framework, you need to do **three separate things**:

### Add `cors` configurations to HTTP points of the function definitions in your `serverless.yml`
This includes CORS headers to *preflight* `OPTIONS` requests to your API:
```
functions:
  getProduct:
    handler: bin/get_product
    events:
      - http:
          path: product/{id}
          method: get
          cors:
            origin: "*"
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
              - <your-custom-header-goes-here>
```

### Add `resources` section to `serverless.yml` to include CORS headers in API Gateway-level error responses
Such as due to expired authentification token, unauthorized access, or 404:
```
resources:
  Resources:
    GatewayResponseDefault4XX:
      Type: 'AWS::ApiGateway::GatewayResponse'
      Properties:
        ResponseParameters:
          gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
          gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent,<your-custom-header-goes-here>'"
        ResponseType: DEFAULT_4XX
        RestApiId:
          Ref: 'ApiGatewayRestApi'
    GatewayResponseDefault5XX:
      Type: 'AWS::ApiGateway::GatewayResponse'
      Properties:
        ResponseParameters:
          gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
          gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent,<your-custom-header-goes-here>'"
        ResponseType: DEFAULT_5XX
        RestApiId:
          Ref: 'ApiGatewayRestApi'
```
`ResponseType` fields control what type of responses the corresponding resource section applies to. If you don't want to include CORS headers in every 4XX or 5XX error response, you can find some more specific ResponseTypes [here](https://gist.github.com/jonatassaraiva/4c33dd8225605c02318cd71a55b2335d).

Note: `GatewayResponseDefault4XX` and `GatewayResponseDefault5XX` just resource names and have no significance.

### Include CORS headers in the normal responses in your lambda handler
Example in Go:
```go
func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	rsp, err := apiGatewayAdapter.ProxyWithContext(ctx, req)
	rsp.Headers["access-control-allow-origin"] = "*"
	rsp.Headers["access-control-allow-headers"] = "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent,<your-custom-header-goes-here>"
	return rsp, err
}
```

Resources:
 - [Custom header in OPTIONS preflight for CORS](https://forum.serverless.com/t/custom-header-in-options-preflight-for-cors/3122)
 - [chrisoverzero's comment on Github](https://github.com/serverless/serverless/issues/3896#issuecomment-478945726)