CORS in dev and prod

If you don't know what is CORS - in short, by default, if you are on the http://website.com this website can only make requests to that website. In order to make a request to http://otherwebsite.com, first website has to set CORS headers in the response. More theory can be found here.

How to set up CORS with Serverless

If you are using Serverless the enabling CORS on your endpoints is a straightforward process. When editing the yaml file just set cors: true on an http events.

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
          cors: true

 

You could do it manually but Serverless will save you some time and do it automatically during the deployment.

There is one more thing that has to be done for CORS to work. In every λ function you have to specify headers. Just set Access-Control-Allow-Origin to '*' to accept requests coming from all domains or 'your-domain-name.com' for requests coming from one domain.

module.exports.hello = (event, context, c) => {
  const response = {
    statusCode: 200,
    headers: {
      'Access-Control-Allow-Origin': '*', // Required for CORS support to work
      // try swapping '*' for an environment based variable
    },
    body: 'hello'
  };
  c(null, response)
}

 

What does CORS:true do behind the scenes

Settings cors to true sets the default configuration of Serverless to be:

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
          cors:
            origin: '*'
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
            allowCredentials: false

This lets our lambda serverless function to know that we can safely accept requests from any website ('*' - means any). If being precise, the browsers regulate this behaviour for security reasons. If you include an AJAX call from youdomain.com to our lambda function which will reside at domain looking something like this: https://q3o4tjwe0dgj.execute-api.us-east-1.amazonaws.com/dev/hello then before doing GET request to this endpoint, the browser will do so called Preflight request. This is a request of HTTP method OPTIONS with three HTTP headers: Origin, Access-Control-Request-Method and Access-Control-Request-Headers.

 

As it is usually handled by the browsers automatically, the developers don't need to handle the logic around it. However, if you are curious, you can do the request to your newly generated lambda endpoint:

OPTIONS https://q3o4tjwe0dgj.execute-api.us-east-1.amazonaws.com/dev/hello

 

The header results should look like this:

access-control-allow-credentials: false
access-control-allow-headers: Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent
access-control-allow-methods: OPTIONS,GET
access-control-allow-origin: *

which means that two methods are allowed on this endpoint - OPTIONS and GET

 

What to do in production

The best practice would be to have the same domain for your lambdas and the website you are querying them with. You don't have to set CORS headers in this case.

 

If your lambdas and the domain you are calling lambdas from are on the different domains, the recommended approach is to set Access-Control-Allow-Origin header to that domain name. This header supports either one single domain name (can be pattern) or '*' If you are just using one CloudFormation stack for lambdas (one lambda functions group) and you use one domain name there is another approach.

 

You can set custom domain name for your API Gateway. Details instruction on how to do it can be found here