Posting this here because it might save someone hours of pain.
Long story short, as many users have pointed out, n8n doesn’t handle CORS headers on preflights, which can make it hard to make requests from the frontend (Add ability to set CORS allow-list in n8n webhooks - #3 by Domenico_Rutilgliano)
Since I was already using nginx as a reverse proxy to run n8n on a subdomain, I opted to try the “CORS wide open” policy that the user [roemhildtg] kindly posted in the thread.
However when I implemented this, I ran into a weird issue in my frontend console:
Access to fetch at 'https://example.com/webhook/examplewebhook' from origin 'https://example.com' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'https://example.com, *', but only one is allowed. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
So for some reason I was getting zero access-control headers without the config but getting a duplicate allow-origin header when setting one with nginx.
After hours troubleshoot I decide to check n8n’s codebase and found that in this line: https://github.com/n8n-io/n8n/blob/b11c4de0392289ff1ac4c4293ff92ba3bdeecbc0/packages/cli/src/middlewares/cors.ts#L3
You’re adding CORS headers if the string ‘origin’ exists in the request headers, resulting in sending:
Access-Control-Allow-Origin: $request_origin_url
when
Access-Control-Allow-Origin: *
already exists. It’s not possible to have multiple Access-Control-Allow-Origin
headers, and so I had to overwrite this behavior manually in nginx with:
# Hide CORS headers from the backend response
proxy_hide_header 'Access-Control-Allow-Origin';
proxy_hide_header 'Access-Control-Allow-Methods';
proxy_hide_header 'Access-Control-Allow-Credentials';
proxy_hide_header 'Access-Control-Allow-Headers';
It’s super confusing when your team consistently states there’s no config done for CORS when you have an express middleware function that can conflict the referenced answer in the main feature request thread.
I’m not sure what the purpose of the corsMiddleware function is however, so I’m not sure on if this is a fixable issue on your end.