My life has been blocked by CORS policy: Response to preflight request doesn't pass access

Who doesn't like a bit of monday morning cors? 😆 Was hoping that someone could help me narrow down my issue if they've got a sec. I've a FE and BE server, FE using axios, BE express. Locally, the FE is on port 4600, backend 3550, and there doesn't seem to be an issue. I'd thought that was because I'd set the correct headers on the server:
module.exports = (app) => {
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4600');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Authorization, Accept');
res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
next();
})
};
module.exports = (app) => {
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4600');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Authorization, Accept');
res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
next();
})
};
Changing the allowed origin to something incorrect, say 4700, results in the error I'd expect: The 'Access-Control-Allow-Origin' header has a value 'http://localhost:4700' that is not equal to the supplied origin. So all of the above is working as far as I can tell. The problem comes when I upload the sites: FE: example.com, BE: api.example.com. Both are accessible, so for example, navigating to api.example.com/email will return json from a test route. However, the request that had previously worked locally now returns
Access to XMLHttpRequest at 'https://api.example.com/email/' from origin 'https://example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Access to XMLHttpRequest at 'https://api.example.com/email/' from origin 'https://example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
What I guess I don't understand is why locally the header is obviously present and being used (which is why it denied access when the port was changed to 4700), but when live it complains it isn't. I was hoping someone had some debugging pointers please! 🙂
17 Replies
flovous
flovous2y ago
I used to have the same issue in Django, but I would recommend you try finding a solution with ChatGPT you can use a prompt like this one: "How to fix blocked by CORS policy in Express" Or "how to allow a certain origin to make a request to your API without getting blocked by CORS policy in Express.js"
Jochem
Jochem2y ago
Please don't just tell people to check with ChatGPT, it's about as helpful as telling someone to just google something, and on top of that ChatGPT isn't the tool for that job anyway. It can give you incorrect or incomplete information, and won't necessarily tell you about the caveats of any proposed solution
flovous
flovous2y ago
Sorry then.
JWode
JWodeOP2y ago
Yeah, I appreciate the suggestion, but I'm wary of throwing everything at the problem and seeing what sticks - which I feel is what chatgpt often leads to. (ngl though, i do often use it :)) For example, I know I should probably use the cors middleware, but I'm still interested to know why the header is being returned locally, but not on the live site. And why I can access the get routes through the browser, but not through postman
Jochem
Jochem2y ago
As for an actual solution, I can't dive into it myself right now, but I know that any time spent learning about CORS isn't wasted. There's a good article on MDN that might be worth reading: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS Looking at the response, it's an issue with the preflight, which is an OPTION call to the backend so that the CORS headers can be checked before the client sends any potentially sensitive information to the server in the actual request why it would work for a local request and not a remote one is a bit of a mystery though... Different ports are different origins as far as CORS is concerned
JWode
JWodeOP2y ago
Yeah, I'd got that far RE the preflight, but it's always great to get confirmation that I'm focusing on the right bit, so thanks. Understand that you're probably busy, monday morning and all, so the extra reading material is perfect 👍 I'll be no doubt banging my head against this brick wall all day though, so will update the thread in a kind of rubber duck debugging way. If you get a chance to look at it later, or think of anything, it would be much appreciated, but no worries if you don't!
Jochem
Jochem2y ago
will do, thanks for understanding 🙂
JWode
JWodeOP2y ago
--------------------------------
JWode
JWodeOP2y ago
Anyway, strangely glad to hear that I'm not the only one that finds this distinction between local and live odd. Locally the server responds to the OPTION call with the correct headers:
Request URL: http://localhost:3550/email/
Request Method: OPTIONS
Status Code: 200 OK
Remote Address: [::1]:3550
Referrer Policy: strict-origin-when-cross-origin
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Authorization, Accept
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
Access-Control-Allow-Origin: http://localhost:4600
Access-Control-Expose-Headers: Content-Disposition
Allow: GET,HEAD,POST
Connection: keep-alive
Content-Length: 13
Content-Type: text/html; charset=utf-8
Date: Mon, 27 Feb 2023 10:18:43 GMT
ETag: W/"d-bMedpZYGrVt1nR4x+qdNZ2GqyRo"
Keep-Alive: timeout=5
X-Powered-By: Express
Request URL: http://localhost:3550/email/
Request Method: OPTIONS
Status Code: 200 OK
Remote Address: [::1]:3550
Referrer Policy: strict-origin-when-cross-origin
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Authorization, Accept
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
Access-Control-Allow-Origin: http://localhost:4600
Access-Control-Expose-Headers: Content-Disposition
Allow: GET,HEAD,POST
Connection: keep-alive
Content-Length: 13
Content-Type: text/html; charset=utf-8
Date: Mon, 27 Feb 2023 10:18:43 GMT
ETag: W/"d-bMedpZYGrVt1nR4x+qdNZ2GqyRo"
Keep-Alive: timeout=5
X-Powered-By: Express
vs Live site:
Request URL: https://api.example.com/email/
Request Method: OPTIONS
Status Code: 502
Remote Address: 144.126.196.138:443
Referrer Policy: strict-origin-when-cross-origin
content-length: 568
content-type: text/html
date: Mon, 27 Feb 2023 09:56:53 GMT
server: nginx/1.18.0 (Ubuntu)
Request URL: https://api.example.com/email/
Request Method: OPTIONS
Status Code: 502
Remote Address: 144.126.196.138:443
Referrer Policy: strict-origin-when-cross-origin
content-length: 568
content-type: text/html
date: Mon, 27 Feb 2023 09:56:53 GMT
server: nginx/1.18.0 (Ubuntu)
That kind of looks like Express is dealing with the request headers locally, but nginx on the live site? Could that also perhaps explain why get requests from the browser (api.example.com/email) work, but postman requests to the same address don't? Perhaps my nginx config is routing traffic weirdly 🤔
Jochem
Jochem2y ago
This is about PHP, and by now almost 4 years old, but I think it's describing your problem: https://blog.alwoke.com/2019/06/how-to-handle-cors-options-request-with.html Looks like nginx doesn't fully support passing on OPTIONS requests:
That Error is triggered directly in NGINX, because NGINX does not know what to do with the OPTIONS Method!
You'll have to check if that might've changed, but the solution they propose is to hardcode your CORS in your nginx config
JWode
JWodeOP2y ago
yeah, i've been reading a lot of answers that set the headers in the server block ok cool, at least I now know what to focus on. I didn't want to start duplicating headers in the config file without at least knowing it was the problem ----------------------------- Yeah, so mirroring the headers in my nginx config worked - but I then noticed that my express headers weren't specifying OPTIONS as a method (although I was doubtful this mattered as the local setup was working) - so I thought I'd just check that out quickly, removed the headers from the nginx config and... it continued to work... before I'd even made the OPTIONS changes? 😆
Jochem
Jochem2y ago
did you restart nginx after the config change?
JWode
JWodeOP2y ago
yeah, twice, just to make sure 😄 at least i know the fix if it goes back to being a pain 🤷‍♂️
Jochem
Jochem2y ago
I gave up needing to understand how every IT issue worked 20 years ago when I had to keep an MP3 paused in WinAmp, or my microphone wouldn't work properly. Had to be WinAmp, and just having it open wasn't enough, it had to be playing or paused on an MP3. Good to know you've got a solution though!
JWode
JWodeOP2y ago
amazing. genuinely made me laugh out loud. needing to know why is one of my biggest flaws that i'm slowly starting to shed - "i'll understand it later" is massively underrated
Jochem
Jochem2y ago
definitely, especially for stuff that's adjacent to what your primary focus is. If the issue was actually CORS, and not nginx being weird, I'd want to know what it was too, but as long as you're not a sysadmin that has to manage dozens of nginx instances, "eh, it works now" sounds like a perfectly good conclusion
JWode
JWodeOP2y ago
no wonder imposter syndrome is such a big thing in this industry am looking forward to my first dev interviews: biggest strength? my ability to settle for less 😁 thanks for taking a look though, appreciate it
Want results from more Discord servers?
Add your server