Handling CORS preflight requests

Hi, I’m having issue trying to get Netlify Lambda respond properly to CORS preflight requests.
Go to any site and do a fetch request like this and you’ll see the error

fetch('https://ravely-widget.netlify.app/api/actions/tester', { method: 'POST', body: JSON.stringify({a:'a'}), headers: { 'Content-Type': 'application/json' } }).then(res => res.text()).then(console.log).catch(console.log)

Within my lambda function I detect if the event.httpMethod is OPTIONS, if it is I return a response with CORS headers:

const CORS_HEADERS = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'Content-Type',
  'Access-Control-Allow-Methods': '*',
};

if (event.httpMethod === 'OPTIONS') {
  console.log('OPTIONS ', { CORS_HEADERS });
  return {
    statusCode: 200,
    headers: CORS_HEADERS,
    body: JSON.stringify({ message: 'Successful preflight call.' }),
  };
}

However when I do an OPTIONS request using Postman/Insomnia it does not return those headers, e.g.
making a POST request to http://ravely-widget.netlify.app/api/actions/tester
which causes the preflight requests done by browsers to fail (when it called from outside my domain).
This is all I get (notice the missing access-control-allow headers)

< HTTP/2 200 
< cache-control: no-cache
< server: Netlify
< x-nf-request-id: 01FDD1DKW929G2BXB2XQBM7BB0
< age: 1
< date: Wed, 18 Aug 2021 16:27:30 GMT
< content-length: 0

It works properly on my local dev server — if I were to try the same on my local dev server I’d get this (access-control-allow- headers are present)

< HTTP/1.1 200 OK
< x-powered-by: Express
< access-control-allow-origin: *
< access-control-allow-headers: Content-Type
< access-control-allow-methods: *
< date: Wed, 18 Aug 2021 16:24:17 GMT
< connection: close
< transfer-encoding: chunked

Does anyone know how to solve this? Thanks!

Netlify site name: ravely-widget.netlify.app

Hello, try adding a rule in netlify.toml file like this:

Hi @edgar thanks for the link!
Unfortunately adding the headers didn’t seem to help

I’ve added the header in my toml file

[[headers]]
  for = "/api/actions/*"
  [headers.values]
    Access-Control-Allow-Origin = "*"

and yet the same fetch call produces the same CORS errors

fetch('https://ravely-widget.netlify.app/api/actions/tester', { method: 'POST', body: JSON.stringify({a:'a'}), headers: { 'Content-Type': 'application/json' } })
  .then(res => res.text()).then(console.log)
  .catch(console.log)

with the following error still showing

No 'Access-Control-Allow-Origin' header is present on the requested resource.

Doing an OPTIONS request also doesn’t return any CORS headers in the response

* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Connection state changed (MAX_CONCURRENT_STREAMS == 4294967295)!

< HTTP/2 200 
< cache-control: no-cache
< server: Netlify
< x-nf-request-id: 01FDV7BA6DA7PKBK3QS9CA563C
< age: 0
< date: Tue, 24 Aug 2021 04:40:27 GMT
< content-length: 0

Hey there!

First up, can you ensure that you are specifying a POST (with capitals) request? Or, change the condition to match any case.

A * should be fine but, just to rule it out, would you mind specifying the methods explitly?

Hello @ravelysid, check out this example I made for you:

I tested the deployed URL from my example with the fetch request you posted above and it worked fine.

Hi @edgar many thanks for the example!

I’ve actually just realized my mistake — I returned the response object as is without encasing it in a Promise -_-"

Nevertheless I appreciate your taking the time to help out and even make a working example, I’m sure it’ll help others as well should they face a similar issue; I’ll be marking your reply as the answer. Thanks again!

2 Likes

So glad you found a solution, @ravelysid! @edgar, thanks for chiming in here and providing that example!

1 Like