Last updated: Mar 7, 2024
Reading timeยท6 min
The error "Response to preflight request doesn't pass access control check" occurs for multiple reasons:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. Error 405
CORS is a mechanism that allows a server to use a combination of HTTP headers to indicate from which domains, other than its own, it receives requests.
If your server is hosted on http://example.com
, for you to be able to make an
HTTP request from http://localhost:3000
, your server has to send the necessary
CORS headers in its responses.
To allow other origins to make requests to your server, you have to set the
Access-Control-*
headers in your server's responses.
The server should be setting the following CORS headers along with the response.
# ๐๏ธ your domain below, e.g. http://localhost:3000 Access-Control-Allow-Origin: http://example.com Access-Control-Allow-Methods: POST, PUT, PATCH, GET, DELETE, OPTIONS Access-Control-Allow-Headers: Origin, X-Api-Key, X-Requested-With, Content-Type, Accept, Authorization
You might have to tweak the values depending on your use case but open the
Network
tab in your browser, click on the request and check if your server is
setting these CORS-related headers.
The headers are:
Access-Control-Allow-Origin
- which origins are allowed to make requests to
the server.Access-Control-Allow-Methods
- which HTTP methods the origins are allowed to
use when making requests to the server.Access-Control-Allow-Headers
- which HTTP headers the origins are allowed to
use when making requests to the server.Access-Control-Allow-Credentials
- whether to expose the server response to
the frontend when the request's credentials mode is set to include
. When
credentials mode is set to include
, our frontend will always send user
credentials (i.e. cookies, auth headers) even for CORS calls.There is also an Access-Control-Allow-Credentials
header. Setting it to true
is only necessary if your browser sends user credentials with requests (e.g.
cookies or the Authorization header).
# ๐๏ธ only if your browser sends user credentials (cookies or Auth headers) Access-Control-Allow-Credentials: true
*
(asterisk) symbol as the origin and see if that works.When an asterisk *
is set for the Access-Control-Allow-Origin
header, any
origin on the internet can access the server.
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: POST, PUT, PATCH, GET, DELETE, OPTIONS Access-Control-Allow-Headers: *
You would want to narrow this down in production, but it's a useful tool when debugging.
Note that the Access-Control-Allow-Credentials
header cannot be set to true
if Access-Control-Allow-Origin
is set to an asterisk *
.
When the Access-Control-Allow-Headers
is set to an asterisk, all headers are
allowed in a preflight request.
Access-Control-Allow-*
headers to allow other origins to make HTTP requests to it.There are usually packages that take care of CORS for you if you use mainstream backend frameworks, such as Express.js.
You can google for "enable CORS for my_framework", e.g. "enable CORS for express.js".
In the case of Express.js, this is handled by the
CORS module which
automatically sends the specified Access-Control-Allow-*
headers on server
responses.
Make sure that the URL you've specified when making the HTTP request is correct and complete.
http://
or https://
if you're
testing on localhost without an SSL certificate./posts
.GET
or POST
) has to be correct for the specific path
(e.g. /posts
).For example, if you use the native fetch
API to make an HTTP request, your
request looks something like the following.
async function getUser() { try { const response = await fetch('https://randomuser.me/api/', { method: 'GET', headers: { accept: 'application/json', }, }); if (!response.ok) { throw new Error(`Error! status: ${response.status}`); } const result = await response.json(); return result; } catch (err) { console.log(err); } } getUser();
If you're making a POST
, PUT
or PATCH
request, make sure the body
is
passed to the JSON.stringify()
method unless the library that you use does
that automatically for you.
For example, when using fetch
, you have to manually convert the body
you're
sending over the network to JSON.
Try setting the following HTTP response headers on your server.
# ๐๏ธ your domain below, e.g. http://localhost:3000 Access-Control-Allow-Origin: http://example.com Access-Control-Allow-Methods: POST, PUT, PATCH, GET, DELETE, OPTIONS Access-Control-Allow-Headers: Origin, X-Api-Key, X-Requested-With, Content-Type, Accept, Authorization
If that doesn't work, set the Access-Control-Allow-Origin
to an asterisk to
allow HTTP requests from all origins and make an HTTP request to the server
again.
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: POST, PUT, PATCH, GET, DELETE, OPTIONS Access-Control-Allow-Headers: *
Once you get CORS working, you can narrow down the values of the
Access-Control-Allow-*
headers.
A proxy is a server that sits between the client (browser) and the server you need to make an HTTP request to.
If you aren't able to set the Access-Control-Allow-*
response headers on the
server, you can make an HTTP request to a proxy, which makes an HTTP request to
the other server.
This is possible because the same origin policy isn't enforced when making requests from one server to another.
Access-Control-Allow-*
headers to your client (browser), then it can just fetch the information from the other server and respond with it.Here is an example of a simple proxy using Express.js.
In order to use it, you first have to install the dependencies.
npm install express
The proxy consists of the following code stored in an index.js
file.
const express = require('express'); const app = express(); app.use((_req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', '*'); next(); }); app.get('/users', async (_req, res) => { const url = 'https://randomuser.me/api/'; try { const response = await fetch(url); if (!response.ok) { throw new Error(`Error! status: ${response.status}`); } const result = await response.json(); return res.json(result); } catch (error) { console.log(error); return res.status(500).json({error: 'An error occurred'}); } }); const port = 3456; app.listen(port, () => console.log(`Server running on http://localhost:${port}`), );
You can start the proxy server with the node index.js
command.
node index.js
You can make an HTTP request to http://localhost:3456/users to get a response with the data.
We set the Access-Control-Allow-Origin
and Access-Control-Allow-Headers
headers to an asterisk to allow all origins to make requests to the proxy
server.
The next step is to make an HTTP request to the other server and send the response to the client.
The proxy server simply sits between the browser and the other server and takes advantage of the fact that one server can make an HTTP request to another without having to pass CORS checks.
Alternatively, you can use a proxy server that makes a request to the specified URL and responds with the result.
The cors-anywhere package is a NodeJS proxy which adds CORS headers to the proxied request.
You can use the following command to install the cors-anywhere
package.
npm install cors-anywhere
Here are the contents of a simple implementation of the proxy server stored in
an index.js
file.
const cors_proxy = require('cors-anywhere'); const host = process.env.HOST || '0.0.0.0'; // Listen on a specific port via the PORT environment variable const port = process.env.PORT || 8080; cors_proxy .createServer({ originWhitelist: [], // Allow all origins }) .listen(port, host, function () { console.log('Running CORS Anywhere on ' + host + ':' + port); });
You can start the proxy server with the node index.js
command.
node index.js
The server is available at http://localhost:8080
.
As shown in the docs, the URL to proxy is taken from the path.
Here are some examples.
# ๐๏ธ makes a request to randomuser.me/api http://localhost:8080/randomuser.me/api # ๐๏ธ makes a request to google.com http://localhost:8080/google.com
The concept is the same - the proxy server makes an HTTP request to the specified URL and sends the response data back to the client.
To solve the error "Response to preflight request doesn't pass access control check", make sure: