Table of contents
TL;DR: you need a proxy
I was browsing Reddit yesterday and saw someone asking about an opaque error message while trying to make an API request with fetch()
:
Here’s the pertinent error message:
Access to fetch at ${SOME URL} from origin ${ANOTHER URL} has been blocked by CORS policy: Response to preflight request doesn’t pass access control check
As is typical for things around this error, the responses on Reddit had one or two people who know their stuff amongst 100 people who think commenting “idk but good luck” is helpful.
This is one of those errors where the wording is accurate but it is very far from helpful. And even if you’ve run into it and gotten past it, a lot of folks don’t really understand what’s happening.
What isn’t crystal clear is this: by default, JavaScript code running in the browser on one origin can’t make a request to a different origin.
There are many good reasons for this! For more details, I’d start with the MDN documentation on the Same-origin policy and CORS, the method by which a server can allow some requests from different origins and Jake Archibald’s comprehensive post about CORS.
Cross-Origin errors are so common during web development that it was part of our interview screening process at the last place I worked. Our take-home coding test had you build a little web app that made a request to a public API like, I dunno, one that spits out Chuck Norris jokes. Then you’d display the jokes or whatever. It didn’t matter.
What did matter was this: because you’d be developing locally at localhost
, you would very likely hit a CORS error. And if you couldn’t figure it out you have probably been lying about your “20 years” of experience as a JavaScript developer. (Your “10 years” of React experience when React has not existed for that long maybe should have been a clue.)
Anyway, I don’t work there anymore and it seems only fair to rid the world of one more “gotcha” interview thing. So here’s…
How to fix it
Two broad paths here.
If you control the server that you are making requests to
When you’re dinking around with some external API, this is probably not your situation. But if it is: add CORS headers and support for OPTIONS
requests to your server responses. Do this carefully! This article from Auth0 seems like an OK starting point, and Jake Archibald’s article is also great.
If you do not control the server you are making requests to or cannot configure it to support CORS
What you’re probably going to end up doing is proxy your request to the different origin. The “proxy” part means you make requests to your server on the same origin, and then your server makes a request to the different origin.
(Sometimes this particular setup is called a reverse proxy and I’ve also seen it called an “API Gateway” pattern.)
This eliminates your CORS problem on two fronts:
- The browser is making a request for a URL on the same origin and therefore does not have to do any special CORS checks.
- A server is making the actual request to the site on a different origin. Servers don’t do CORS checks; only browsers do CORS checks.
Ta-da!
A common setup for local development looks like:
- Your development server is running at, say,
http://localhost:3000
. This is what you open in your browser. - Your client-side code makes requests to a particular path, for example
/api
. In the browser, this resolves tohttp://localhost:3000/api
- When something makes a request to
/api
, your dev server makes its own request to the external API server, tacking on anything after/api
. So if you were proxying tohttps://api.chucknorris.io
, a request to/api
would go to the root ofhttps://api.chucknorris.io
, whereas a request to/api/WHATEVER/ELSE
would go tohttps://api.chucknorris.io/WHATEVER/ELSE
or a request to/api/whatever?query=something
would go tohttps://api.chucknorris.io/whatever?query=something
. - Your dev server passes along the response from the external service
Here’s some of the ways I’ve tackled this over the years:
- Using Apache? Set up a reverse proxy.
- Using Netlify? Proxy a path to another service.
- Using a Node HTTP server like Express? There’s middleware for that.
- Using Vite for development? Configure the dev server to proxy requests.
- Using React and
create-react-app
in development? Set up a simple proxy server in yourpackage.json
. - Using a swanky all-in-one framework like SvelteKit or Next.js? Set up some server-side routes that
fetch
external data for you.
The above is obviously not a complete list of solutions; Google “<insert language or hosting provider or app framework here> proxy” and you’ll probably find something.
But now you know what you’re looking for. Good luck!