[Django]-CORS error while consuming calling REST API with React

31👍

Please Note: This solution is not for production configuration. This is merely a workaround for easier setup while development. Please refrain from using this in production configuration.

Install django-cors-headers through pip install django-cors-headers

Then, add in installed apps ‘corsheaders’.

Add the setting,

CORS_ORIGIN_ALLOW_ALL = True

and,

ALLOWED_HOSTS = ['*']

This should do the trick.

UPDATE

You’ll also need to add it to the middlewares,

MIDDLEWARE = [  # Or MIDDLEWARE_CLASSES on Django < 1.10
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

25👍

The currently accepted answer potentially opens sites up to security risks:

Why does the error happen:

In order for your AJAX request to work well, there are two things that need to happen:

  1. The request has to be accepted by the server.
  2. The returned request must be accepted by the browser, so that the client-side can do something with it.

The error that the OP reports, indicates that the second part of this process is failing. This is because if the request is sent from domain that is different to the server returning the request, the browser won’t accept it unless the appropriate headers are set (that is, the server has given permission for the browser to read it).

How to fix it:

Now to fix this, we can use django-cors-headers. This will add the apropriate headers, so that the browser accepts the returned response. To install run:

pip install django-cors-headers

and add it to your middleware:

MIDDLEWARE = [
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

Now, you need to add the domain you are sending your AJAX request from, to the list of allowed domains:

CORS_ALLOWED_ORIGINS = [
    "www.example.com",
    "http://127.0.0.1:8000",
    ...
]

What about CORS_ORIGIN_ALLOW_ALL?

Do not use this unless you have a specific need to. Setting this to true, will mean that, any origin will be able to make a request to your API, and get a response. Unless you are making a public API, you probably won’t need to do this. More likely you will only need to serve a single domain or a few domains (maybe you have a front-end, served from a different place to your API etc.)

If you are happy for any domain to access your API then you can set the following:

CORS_ORIGIN_ALLOW_ALL = True

If you do this, you will also need to set the following:

ALLOWED_HOSTS = ['*']

The reason for this, is Django will only accept certain hosts by default, so there’s no point setting CORS_ORIGIN_ALLOW_ALL = True unless you’re actually going to accept requests from anyone (that is the part 1 in the explanation above).

Note that by setting allowed hosts to a wildcard, you open yourself up to HTTP host header attacks. Make sure you understand these, and have made sure you are not affected. You can read more about them in the django docs.

Also note: if you have not set your ALLOWED_HOSTS and you are wondering why your requests are working, it is because when DEBUG=True certain hosts are allowed automatically, http://127.0.0.1:8000 etc.

4👍

Using django-cors-headers

Start by installing django-cors-headers using pip

pip install django-cors-headers

You need to add it to your project settings.py file:

INSTALLED_APPS = (
    ##...
    'corsheaders'
)

Next you need to add corsheaders.middleware.CorsMiddleware middleware to the middleware classes in settings.py

MIDDLEWARE_CLASSES = (
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.BrokenLinkEmailsMiddleware',
    'django.middleware.common.CommonMiddleware',
    #...
)

You can then, either enable CORS for all domains by adding the following setting

CORS_ORIGIN_ALLOW_ALL = True

Or Only enable CORS for specified domains:

CORS_ORIGIN_ALLOW_ALL = False

CORS_ORIGIN_WHITELIST = (
    'http//:localhost:8000',
)

2👍

UPDATE (12.12.22)

Just add to the package.json file the following line:

"proxy": "http://localhost:8000"

OLD ANSWER

Based on Joe Sadoski’s great idea,

I suggest an improvement to his solution that also supports react hot-reload-on-changes:

(All other proposed solutions here do not work in chrome since the last upgrades in 2022)

version: '3.1'

services:
  web:
    image: nginx:latest
    ports: 
      - 8080:80
    volumes: 
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    command: [nginx-debug, '-g', 'daemon off;']
    environment:
    - NGINX_HOST=localhost
    - NGINX_PORT=80
    network_mode: host
    restart: unless-stopped

My change here is: network_mode: host

Now the nginx.conf looks like this:

events {}
http{
    server {
        listen      80;
        server_name localhost;
        
        // this block handle react hot-reload
        location /sockjs-node {
            proxy_pass http://localhost:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }

        // this block handle API calls
        location ~* /(accounts|api|admin)  {
            proxy_pass   http://localhost:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }


        // this block handle all the rest (react/statics...)
        location / {
            proxy_pass   http://localhost:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

Here I change the upstreams to point on localhost so both react server and django should run on the host computer in ports 3000 and 8000.

So, after docker-compose up, manage.py runserver, and npm start, both apps are available at http://localhost!

1👍

See Yinon_90’s answer for a better working version of this concept.


I wanted to propose a solution that does not require altering the behavior of the Django or React apps.

In production, you might want to serve both apps on the same domain/from the same server, under different paths. This wouldn’t cause any CORS conflict in production.

Of course, we want to debug Django and utilize React HMR & Dev tools while debugging. For this, I’ve spun up an nginx docker container:

docker-compose.yml:

version: '3.8'

services:
  web:
    image: nginx:latest
    ports: 
      - 8080:80
    volumes: 
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    command: [nginx-debug, '-g', 'daemon off;']
    environment:
    - NGINX_HOST=localhost
    - NGINX_PORT=80

nginx.conf:

Django debug is on port 8000, React HMR is on port 3000. I’ve allowed three paths to go to the Django app, /accounts, /api, and /admin. The rest goes to the React app (and into the React Router system)

events {}
http{
    server {
        listen      80;
        server_name localhost;
        location ~* /(accounts|api|admin) {
            proxy_pass      http://host.docker.internal:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
        location / {
            proxy_pass      http://host.docker.internal:3000;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

After docker-compose up, manage.py runserver, and npm start, both apps are available at localhost:8000!

0👍

I had the same problem, It was quite funny I was getting 200 code and swagger was working correctly, but react didn’t work properly.
simply these require in the setting.py:

CORS_ORIGIN_ALLOW_ALL = True

CORS_ALLOW_CREDENTIALS = True

and for front:

const setCookie = async () => {
        axios.get(`${process.env.REACT_APP_API_URL}/core/csrf-cookie/`, {
                headers: { 'Authorization': null },
                withCredentials: true,
            }
        ).catch( () => {
            console.log('Something went wrong!')
        });
        setCsrfToken(getCookie('csrftoken'))
        console.log(csrfToken)
    }

call setCookie in the useEffect and it should work.

and Don’t forget to build react and set STATICFILES_DIRS

0👍

This is a solution for those who have set everything as required by django-cors-headers but are still getting an error such as:

Access to XMLHttpRequest at 'http://127.0.0.1:8000/' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Well, the problem, at least in my case, was that django-cors-headers did not recognize http://localhost:5173 for some reason. As we know localhost has the IP address 127.0.0.1. So I changed the host address of my react server to explicitly use the IP address 127.0.0.1. I was using react with vite so I did the following changes in my vite.confing.ts to set a new domain:

export default defineConfig({
  // ...
  server: {
    host: '127.0.0.1',  // Set the host to 127.0.0.1
    port: 5173,         // Set the port to 5173
  },
})

So the domain of my frontend server after that will be http://127.0.0.1:5173.

Now you only have to add this new domain to CORS_ALLOWED_ORIGINS in settings.py and the error should be resolved.

CORS_ALLOWED_ORIGINS = [
     'http://127.0.0.1:5173',
]

— End of solution —

Further information for context:

  • OS: Windows 11
  • API calls made using: axios.
  • If you want to see the difference of http://localhost:5173 and http://127.0.0.1:5173 try starting your frontend server using each of them and each time check how the system represents these domains behind the scene using the cmd command netstat -ano.

Leave a comment