How to set up CORS for an AWS Lambda Proxy REST API resource

Let’s see how you set up CORS using a Lambda Proxy API and AWS SAM. Heads up! This guide will also help fix a CORS error that fails in the browser but works in CURL.

About CORS

Cross-Origin Resource Sharing (CORS) is a way to tell browsers which domain, HTTP methods, and HTTP header(s) to trust. Therefore, this mechanism allows a server to indicate any origins (domain, scheme, or port) (and more) other than its own from which a browser should permit loading resources.

This header-based mechanism relies on pre-flight requests made by clients before actually sending the intended request off. The pre-flight request sends a request to the OPTIONS method of the API to achieve this.

When do you need CORS?

You can group CORS requests into two types: simple requests and non-simple requests.

A CORS request is simple if all the following are true:

  • The API Only accepts GET, HEAD, and POST requests.
  • The request includes the Origin header for the POST method request.
  • The request payload content type is: text/plain, multipart/form-data, or application/x-www-form-urlencoded.
  • The API resource does not contain custom headers.
  • And lastly, any additional requirements are listed in the Mozilla CORS documentation.

All other CORS requests are non-simple requests and require your API to enable CORS support.

About AWS SAM

The Serverless Application Model (SAM) is a framework where you can build serverless applications. It provides shorthand syntax for defining functions, APIs, and more in one document using YAML – without having any setup or maintenance hassle!

Let’s Get Started!

First, you need to set up the CORS config in the globals section of your AWS SAM template.

Consequently, your globals section should look like this:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: CORS via Globals

Globals:
  Api:
    Cors:
      AllowOrigin: "'*'"
      AllowHeaders: "*"
      AllowMethods: "'*'"

Resources:
  MyFunction:
...

Next, you need the CORS config to REST API resource.

This setting means your API section should look like this:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: CORS via resource

Resources:
  MyRestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Cors:
        AllowOrigin: "'*'"
        AllowHeaders: "'*'"
        AllowMethods: "'*'"
...

Finally, you must add the CORS config to the REST API response.

Therefore, your Lambda handler should respond with something like this:

{
        "statusCode": status_code,
        "headers": {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Headers": "*",
            "Access-Control-Allow-Methods": "*",
        },
        "body": json_str,
    }

Beware of The ApiKeyRequired Parameter!

In some cases, the ApiKeyRequired can cause problems for JavaScript frameworks. For example, setting this parameter in the “AWS::Serverless::Api” section enables the “x-api-key” header for preflight checks. However, the JavaScript framework may not always include the “x-api-key” in the pre-flight checks.

The example below sets the ApiKeyRequired parameter in the API Resource block:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: CORS via resource

Resources:
  MyRestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Auth:
        ApiKeyRequired: true   # sets for all methods
      Cors:
        AllowOrigin: "'*'"
        AllowHeaders: "'*'"
        AllowMethods: "'*'"
...

Example output in API Gateway:

You can avoid this error by setting ApiKeyRequired to false and then applying this setting manually on the “AWS::Serverless::Function” resource.

Consequently, your Function resource should look like this:

  ApiPostCustomer:
    Type: "AWS::Serverless::Function"
    Properties:
...
      Events:
        apiPostCustomerEvent:
          Type: "Api"
          Properties:
...
            Auth:
              ApiKeyRequired: true
...

As a result, your API Gateway will look like this:

Best Practice Considerations

The examples mentioned above set the access-control-allow-origin, access-control-allow-headers, and access-control-allow-methods to “*” or any. However, it would be best never to allow “any” as this is a terrible security practice. Thus, you need to be more specific and avoid “any”.

Be careful when using * as this could allow the browser access to unintended resources.

How can you test the CORS configuration?

CURL is a great tool to test your CORS configuration:

curl – verbose – location – request <method> '<url>' 

But, of course, you must change the method and the URL parameters accordingly.

For example, here you need to:

  • Set the method to “OPTIONS” or the intended HTTP method like “GET”.
  • URL is the URL of your REST API

You will, as a result, see an output like this:

> curl – verbose – location – request OPTIONS 'https://czr4jjjj.execute-api.us-east-1.amazonaws.com/v1/customer' 
*   Trying 3.236.158.2:443...
* TCP_NODELAY set
...
...
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200 
< date: Tue, 10 May 2022 01:01:31 GMT
< content-type: application/json
< content-length: 3
< x-amzn-requestid: 4c3aaa9d-5b98-48ad-8c98-d7202c0fce97
< access-control-allow-origin: *
< access-control-allow-headers: *
< x-amz-apigw-id: R4noxGd_oAMFoVA=
< access-control-allow-methods: *
< x-amzn-trace-id: Root=1-6279b96b-4df2413134950b6753c093f4
< 
{}
* Connection #0 to host czr4jjjj.execute-api.us-east-1.amazonaws.com left intact

If the ApiKeyRequired parameter were set on the API resource, you would see a forbidden response:

...
< HTTP/2 403 
< date: Mon, 09 May 2022 22:37:59 GMT
< content-type: application/json
< content-length: 23
< x-amzn-requestid: 24b15573-feb7-4c8f-b4d2-00d6ef7129c9
< x-amzn-errortype: ForbiddenException
< x-amz-apigw-id: R4SnJHlMoAMFwgw=
< x-amzn-trace-id: Root=1-627997c7-1dc99815469066d339ab5b67
< 
* Connection #0 to host czr4jjss0e.execute-api.us-east-1.amazonaws.com left intact
{"message":"Forbidden"}

Wrapping Up

You have learned how to enable CORS when using AWS Lambda Proxy API and AWS SAM. In addition, you have learned how to enable the API Key required parameter for all methods except OPTIONS, which can cause a “forbidden” error. Therefore, you should now be able to access your API resources by CURL and the browser.

You May Also Be Interested In

Sources:



About the Authors

Anto's editorial team loves the cloud as much as you! Each member of Anto's editorial team is a Cloud expert in their own right. Anto Online takes great pride in helping fellow Cloud enthusiasts. Let us know if you have an excellent idea for the next topic! Contact Anto Online if you want to contribute.

Support the Cause

Support Anto Online and buy us a coffee. Anything is possible with coffee and code.

Buy me a coffee



About Anto Online

Having started his career in 1999 as a Desktop Support Engineer, Anto soon changed paths and became a developer. After several years of development experience, he transitioned into a consultant. As an enterprise application consultant for a leading SaaS software provider, Anto specializes in AWS's serverless technologies. By day, Anto focuses on helping customers leverage the power of serverless technologies. By night, he indulges his passion for cloud computing by playing with Python and trying out things that are currently beyond the scope of his work. Sometimes Anto needs help as there are not enough hours at night. So Anto relies on a team of fellow Cloud enthusiasts to help him out. Each one is a Cloud expert in their own right, and Anto takes great pride in helping them learn and grow.

View all posts by Anto Online →

Leave a Reply

Your email address will not be published.