Incorrect Access-Control-Allow-Origin being added automatically to POST & DELETE endpoints

I have an app built with FastAPI hosted on API Gateway using serverless.

The API: https://xxxxx.execute-api.xx-xxxxxx-x.amazonaws.com/dev/{proxy+}

Since most of my endpoints are proxy endpoints, I am adding to the response headers as follows:

response.headers['Access-Control-Allow-Origin'] = "*"
response.headers['Access-Control-Allow-Credentials'] = "true"
response.headers['Access-Control-Allow-Headers'] = "Origin, X-Requested-With, Content-Type, Accept, x-access-token"

I have 3 different types of endpoints: GET, POST & DELETE.

The Access-Control-Allow-Origin is correctly assigned in the GET request as follows:

access-control-allow-credentials: true 
access-control-allow-headers: Origin,X-Requested-With,Content-Type,Accept,x-access-token 
access-control-allow-origin: * 
content-length: 150 
content-type: application/json 
date: Mon,09 Aug 2021 07:06:45 GMT 
x-amz-apigw-id: DyYQPFBHFiAFrQA= 
x-amzn-remapped-content-length: 150 
x-amzn-requestid: 24fac4dc-189c-468e-9ca7-1bfd6ccfbabe 
x-amzn-trace-id: Root=1-6110d401-2816fc3630142ecd24604935;Sampled=0 

it is not correctly being assigned in the POST & DELETE methods. When I host it on API Gateway, the above-mentioned API is being automatically added to the Access-Control-Allow-Origin in place of "*", which I am specifically mentioning when I declare the response headers as shown above.

The response headers for the POST & DELETE methods:

access-control-allow-credentials: true 
access-control-allow-headers: Origin,X-Requested-With,Content-Type,Accept,x-access-token  access-control-allow-methods: GET,POST,DELETE 
access-control-allow-origin: https://xxxxx.execute-api.xx-xxxxxx-x.amazonaws.com/dev/{proxy+} 
content-length: 392 
content-type: application/json 
date: Mon,09 Aug 2021 07:01:37 GMT 
x-amz-apigw-id: DyXgoHozliAFnJA= 
x-amzn-remapped-content-length: 392 
x-amzn-requestid: a03fad7e-1caf-4a8c-b188-932923085755 
x-amzn-trace-id: Root=1-6110d2d0-39fe47e07531d93a585117d7;Sampled=0 

Because of this, the following error is shown in the frontend:

Failed to load resource: Origin http://localhost:3000 is not allowed
by Access-Control-Allow-Origin.

I’m setting the response headers to all the endpoints as follows:

from fastapi import APIRouter

router = APIRouter(
    prefix="/dimensioning",
    tags=["dimensioning"],
)

@router.post('/')
def post_body(response: Response):
    response.headers['Access-Control-Allow-Origin'] = "*"
    response.headers['Access-Control-Allow-Credentials'] = "true"
    response.headers['Access-Control-Allow-Headers'] = "Origin, X-Requested-With, Content-Type, Accept, x-access-token"
    response.headers['Content-Type'] = "application/json"
    # do the other stuff

@router.get('/')
def get_body(response: Response):
    response.headers['Access-Control-Allow-Origin'] = "*"
    response.headers['Access-Control-Allow-Credentials'] = "true"
    response.headers['Access-Control-Allow-Headers'] = "Origin, X-Requested-With, Content-Type, Accept, x-access-token"
    response.headers['Content-Type'] = "application/json"
    # do the other stuff

@router.delete('/')
def delete_body(response: Response):
    response.headers['Access-Control-Allow-Origin'] = "*"
    response.headers['Access-Control-Allow-Credentials'] = "true"
    response.headers['Access-Control-Allow-Headers'] = "Origin, X-Requested-With, Content-Type, Accept, x-access-token"
    response.headers['Content-Type'] = "application/json"
    # do the other stuff

I am also following the structure here. So in my main.py, as done here, I have

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

My serverless.yml file:

events:
      - http:
          path: /
          method: ANY
          cors: true
      - http:
          path: /{proxy+}
          method: ANY
          cors: true

I also checked out How do I enable CORS? and modified my serverless.yml file as below and still I get the same error:

functions:
  dimensionierungstool:
    handler: app.main.handler
    timeout: 15
    memorySize: 512
    events:
      - http:
          path: /
          method: ANY
          cors:
            origin: '*'
            headers:
              - Content-Type
              - X-Amz-Date
              - Origin
              - Accept
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
              - X-Requested-With
              - x-access-token
            allowCredentials: true
            maxAge: 86400
      - http:
          path: /api/v1/dimensioning/
          method: post
          cors:
            origin: '*'
            headers:
              - Content-Type
              - X-Amz-Date
              - Origin
              - Accept
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
              - X-Requested-With
              - x-access-token
            allowCredentials: true
            maxAge: 86400

Is there something that I am missing that should be added for these methods?

Thanks

What is likely happening (and has happened to me in the past) is that the two faulty methods are actually returning with error responses of some kind; 4XX or 5XX errors. By default, those error types do not have CORS correctoly set but there is a way to fix that using the resources section of the serverless.yml. You can see more info on setting those default return values in this blog post, by going halfway down to the section with the heading " CORS with custom authorizers": https://www.serverless.com/blog/cors-api-gateway-survival-guide/

I updated my serverless.yml file to:

functions:
  dimensionierungstool:
    handler: app.main.handler
    timeout: 15
    memorySize: 512
    events:
      - http:
          path: /
          method: ANY
          cors:
            origin: '*'
            allowCredentials: true
            headers:
              - x-access-token
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
      - http:
          path: /{proxy+}
          method: ANY
          cors:
            origin: '*'
            allowCredentials: true
            headers:
              - x-access-token
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
resources:
  Resources:
    GatewayResponseDefault4XX:
      Type: 'AWS::ApiGateway::GatewayResponse'
      Properties:
        ResponseParameters:
          gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
          gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
        ResponseType: DEFAULT_4XX
        RestApiId:
          Ref: 'ApiGatewayRestApi'

but this too doesn’t work. When I inspect the response, I see that there are 2 APIs:


This is for the POST
2
This is for the OPTIONS method

Can you add for 5XX too. Also, take a look at your CloudWatch logs for that function to see if there are any errors being logged that end up returning the 500 error via API Gateway

After spends hours on this issue and coordinating with the frontend developer, we realized that was a missing parameter in the frontend that was required for the backend. It was causing an internal server error, that’s why the headers were not available.

After adding the parameter, the requests were successful