Handle API Gateway CORS Errors

Link to chapter - http://serverless-stack.com/chapters/handle-api-gateway-cors-errors.html

Hi guys,

It seems like this chapter is accidentally being skipped in your tutorial.

Clicking ‘next’ from What is Infrastructure links to Configure DynamoDB. The DynamoDB chapter’s serverless.yml Resources section references resources/api-gateway-errors.yml. I think What is Infrastructure should link this chapter instead, and this chapter’s ‘next’ should link to DynamoDB.

By the way, this tutorial is excellent, thank you.

Oh this chapter is a new addition to Part 1 of the tutorial. Did you by any change complete Part 1 some time ago?

Oh, interesting, I see what you mean. When I worked through the tutorial, it had me going into API gateway and manually setting this up. I have been focused on just the infra as code section recently, which assumes resources/api-gateway-errors.yml is already hooked up correctly.

Thanks, nevermind!

1 Like


So far the tutorial has been wonderful. I’m now splitting up my apis into separate services following your tutorial as it hit the 200 limit error yesterday! (something similar to serverless-stack-demo-mono-api but the db and auth will be separate repos). Wondering where to include the API Gateway errors resource in this architecture? Couldnt find anything related to this in your github sample.

Any clue?



Sorry which API Gateway errors resource are you talking about? Is it this one - https://github.com/AnomalyInnovations/serverless-stack-demo-api/blob/master/resources/api-gateway-errors.yml?

If that is the case then you should be able to reference it just as before.

Hi Jay,

Thanks for your reply. Yes that’s the resource I was referring to. If there are more than one services, should each service refer this api gateway errors resource? I guess it should! Just wanted to confirm.


So this isn’t super obvious but this errors resource is tied to a API Gateway project. In the case of the serverless-stack-demo-mono-api, we add all our APIs to the same project. So we only need to have this resource once. We talk about using the same project at the bottom of this chapter:

Have you ever had some requests working and some failing with CORS issues targeting the same backend? I’m having this weird thing happening:

  • my requests go through except for one with query string parameters
  • this one request works in chrome but not firefox
  • in firefox, I get a vague network error and the browser logs that cors failed (Reason: CORS request did not succeed)

I’ve asked my question there but didn’t get much luck: https://forums.aws.amazon.com/thread.jspa?messageID=891922&tstart=0#891922

I’ve not had that specific issues but we do have a few requests fail every once in a while.

From reading your issue it seems like you are able to replicate it consistently. Can you put your code up in a repo?

Hi everyone,

I’m trying to deploy but it’s not going through! The error has so many lines that I cant reach the beginning of the statement so I can’t really read much.

Everything before this works like a charm!

I’m pretty sure it has to be with either the serverless.yml or the api-gateway-errors.yml - indentation specifically!

Here’s my serverless.yml

# NOTE: update this with your service name
service: d2as-hiring-api

# Create an optimized package for our functions 
  individually: true

  - serverless-bundle # Package our functions with Webpack
  - serverless-offline

  name: aws
  runtime: nodejs10.x
  stage: prod
  region: us-east-1

  # 'iamRoleStatements' defines the permission policy for the Lambda function.
  # In this case Lambda functions are granted with permissions to access DynamoDB.
    - Effect: Allow
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:us-east-1:*:*"

  # Defines an HTTP API endpoint that calls the main function in create.js
  # - path: url path is /interviews
  # - method: POST request
  # - cors: enabled CORS (Cross-Origin Resource Sharing) for browser cross
  #     domain api call
  # - authorizer: authenticate using the AWS IAM role
    handler: create.main
      - http:
          path: interviews
          method: post
          cors: true
          authorizer: aws_iam

    # Defines an HTTP API endpoint that calls the main function in get.js
    # - path: url path is /interviews/{id}
    # - method: GET request
    handler: get.main
      - http:
          path: interviews/{id}
          method: get
          cors: true
          authorizer: aws_iam

    # Defines an HTTP API endpoint that calls the main function in list.js
    # - path: url path is /interviews
    # - method: GET request
    handler: list.main
      - http:
          path: interviews
          method: get
          cors: true
          authorizer: aws_iam
    # Defines an HTTP API endpoint that calls the main function in update.js
    # - path: url path is /notes/{id}
    # - method: PUT request
    handler: update.main
      - http:
          path: interviews/{id}
          method: put
          cors: true
          authorizer: aws_iam
    # Defines an HTTP API endpoint that calls the main function in delete.js
    # - path: url path is /notes/{id}
    # - method: DELETE request
    handler: delete.main
      - http:
          path: interviews/{id}
          method: delete
          cors: true
          authorizer: aws_iam
# Create our resources with separate CloudFormation templates

  # API Gateway Errors
  - ${file(resources/api-gateway-errors.yml)}

As you can see,the resources indentation matches the functions, provider, service level.

Here’s api-gateway-errors.yml

    Type: 'AWS::ApiGateway::GatewayResponse'
         gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
         gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
      ResponseType: DEFAULT_4XX
        Ref: 'ApiGatewayRestApi'
    Type: 'AWS::ApiGateway::GatewayResponse'
         gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
         gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
      ResponseType: DEFAULT_5XX
        Ref: 'ApiGatewayRestApi'

Hmmm it’s going to be really hard to debug this without the error message though. Can you save the output of the error to a file and then look up what the error message was?

Thanks for your hint on adding the api gateway errors resource to just the root-api when you are splitting the services. Prior to making that change, I was getting CORS errors when attempting to call the API from my front end app. After implementing the change and adding the resource to only the root-api, the CORS error went away but then I got a 403 Missing Authentication Token. Do I need to set the authorizer in the root-api handler route to authorizer: aws_iam ? Or something else?

Hmmm so the authorizer needs to be applied to every single endpoint and Lambda function you have.

However, this error can be very misleading at times. Sometimes you’ll get this error if you are hitting an endpoint that you are not handling.

Hi all.
I can’t upload a file, but a single note if

This looks like a CORS error, have you configured your IAM roles to allow your users access to the S3 bucket?

When I try to use the test-api I get a http 500 error. I tried running serverless invoke --function create --path mocks/create-event.json and got the same 500 error there. When I run the test api utility I get the following

Making API request

   status: 403,
   statusText: 'Forbidden',
   data: '{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n' +
   '\n' +
   'The Canonical String for this request should have been\n' +
   "'POST\n" +
   '/prod/notes\n' +
   '\n' +
   'accept:application/json\n' +
   'content-type:application/json\n' +
   'host:unbtq4ium0.execute-api.eu-west-2.amazonaws.com\n' +
   'x-amz-date:20191227T175028Z\n' +
   '\n' +
   'accept;content-type;host;x-amz-date\n' +
   "3a99f7c41ea871222ce9eb05cc8c7a5bbfc8e141bbb3c3999cff381d1462d448'\n" +
   '\n' +
   'The String-to-Sign should have been\n' +
   "'AWS4-HMAC-SHA256\n" +
   '20191227T175028Z\n' +
   '20191227/eu-west-2/execute-api/aws4_request\n' +
  "614c1776e4a9a523adf669a111e77ecfe9a486c9fa83fa3e59a56a2e5d956620'\n" +

Any advice will be much appreciated

Hmm the error from the test utility is a little different from the other 500 errors I’m guessing.

For the 500 errors, they are generated in our code. So you can add a console.log before returning an error and check the logs.