Skip to site navigation
Go home

Lee Reamsnyder

How to work around CORS errors in JavaScript

2022 Nov 29 5 min read Published by Lee Reamsnyder Permalink

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():

A screen shot of the browser console with the error message “Access to fetch at https://superheroapi.com/ from origin http://localhost:3000 has been blocked by CORS policy: Response to preflight request doesn’t pass access control check”

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.

By “origin”, I mean the combination of protocol (https://), subdomains if any (www.), top-level domain (example.com), and any port numbers in a URL. If any of those are different, it’s a different origin. So http://localhost:3000 is not the same origin as http://localhost:3001; https://www.example.com is a different origin than https://test.example.com.

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:

  1. The browser is making a request for a URL on the same origin and therefore does not have to do any special CORS checks.
  2. 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:

  1. Your development server is running at, say, http://localhost:3000. This is what you open in your browser.
  2. Your client-side code makes requests to a particular path, for example /api. In the browser, this resolves to http://localhost:3000/api
  3. 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 to https://api.chucknorris.io, a request to /api would go to the root of https://api.chucknorris.io, whereas a request to /api/WHATEVER/ELSE would go to https://api.chucknorris.io/WHATEVER/ELSE or a request to /api/whatever?query=something would go to https://api.chucknorris.io/whatever?query=something.
  4. 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 your package.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!

← Older post Setting up a new M1 Mac for web development → Newer post Row, Stack, and Space components save your sanity

Menu

  • Home
  • Blog
  • Work
  • Contact
  • Archives
  • Feeds: RSS | JSON

Search

Elsewhere

  • GitHub
  • Instagram
  • Mastodon
  • Twitter
© Copyright 2006–2023, Lee James Reamsnyder
Back to top