How to Fix a React JS Getting Errors in XMLHttpRequest with Axios

When I first started working with React JS for my frontend, I quickly ran into one of the most frustrating issues: CORS errors when making API requests with Axios. If you’ve ever seen red errors in your browser console screaming about “No ‘Access-Control-Allow-Origin’ header”, you know exactly what I’m talking about.

I’ll walk you through the exact problem I faced, the mistake in my code, and how I finally fixed it by configuring the server correctly.

The Problem

In one of my Redux actions, I was trying to make a simple login call to my backend running on localhost:8000 while my frontend was on localhost:3000.

Here’s the code I started with:

export function UserLogin(values) {
  var headers = {
    'Access-Control-Allow-Origin': '*',
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  };

  const request = axios.post('http://localhost:8000/login', headers, values)
    .then(response => {
      console.log(response);
    })
    .catch(error => {
      if (error.response) {
        console.log(error.response.data);
      } else if (error.request) {
        console.log(error.request);
      } else {
        console.log('Error', error.message);
      }
    });

  return {
    type: LOGIN_REQUEST_SUCCESS,
    payload: request
  };
}

But instead of logging me in, the browser threw this at me:

Failed to load http://localhost:8000/login:
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:3000' is therefore not allowed access.

At first glance, I thought I could “fix” it by adding the Access-Control-Allow-Origin header in my Axios request. Spoiler: that doesn’t work.

Why This Happen

After digging deeper, I realized two major issues:

  1. CORS headers belong to the server, not the client.
    Browsers will ignore any Access-Control-Allow-Origin header I try to sneak into my request. That header has to come from the server response.
  2. My Axios arguments were out of order.
    The correct method signature is: axios.post(url, data, config) But I mistakenly passed my headers as the request body and my values as the config. No wonder the server wasn’t happy!

Fix the Axios Call

Here’s the corrected version of my Redux action:

export function UserLogin(values) {
  const request = axios
    .post(
      'http://localhost:8000/login',
      values, // request body
      {
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json'
        },
        // withCredentials: true // uncomment if you need cookies/sessions
      }
    )
    .then(response => {
      console.log(response);
    })
    .catch(error => {
      if (error.response) {
        console.log('Server responded with non-2xx:', error.response.data);
      } else if (error.request) {
        console.log('No response received:', error.request);
      } else {
        console.log('Error setting up request:', error.message);
      }
    });

  return {
    type: LOGIN_REQUEST_SUCCESS,
    payload: request
  };
}

With this change, my request was properly formatted, but I still had to solve the real issue: the backend server’s CORS configuration.

The Real Fix Configuring the Server

Because I was making requests from http://localhost:3000 to http://localhost:8000, the browser sent a preflight OPTIONS request. Unless my server responded with the right CORS headers, the request was going to fail.

Node/Express Example

Here’s what I added to my Express backend:

const express = require('express');
const cors = require('cors');
const app = express();

app.use(express.json());

app.use(
  cors({
    origin: 'http://localhost:3000',
    methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
    allowedHeaders: ['Content-Type', 'Accept', 'Authorization'],
    credentials: true
  })
);

app.options('*', cors()); // handle preflight requests

app.post('/login', (req, res) => {
  res.json({ ok: true });
});

app.listen(8000, () => console.log('API running on port 8000'));

That was it! The moment I restarted my server, my React app could finally talk to my backend without complaints.

Other Backend

If you’re not on Node/Express, the solution is similar:

  • Django: Install django-cors-headers and add CORS_ALLOWED_ORIGINS = ['http://localhost:3000']
  • Laravel: Use the built-in fruitcake/laravel-cors package and configure config/cors.php.
  • Spring Boot: Add @CrossOrigin(origins = "http://localhost:3000") to your controller or configure a global CorsConfiguration.

Common Gotchas I Learn the Hard Way

  • If you’re sending cookies or Authorization headers:
    • Set withCredentials: true in Axios.
    • Add Access-Control-Allow-Credentials: true on the server.
    • You cannot use * as the origin you must set a specific one like http://localhost:3000.
  • Using Content-Type: application/json automatically triggers a preflight OPTIONS request. That’s not a bug it’s how the browser works. Just make sure your server responds witth:
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
Access-Control-Allow-Headers: Content-Type, Accept, Authorization

Final Thought

Running into CORS issues with React and Axios was frustrating until I realized the fix wasn’t on the frontend but the backend. I learned to structure Axios requests correctly (url, data, config) and stopped trying to send CORS headers from the client. Once I configured my server to handle preflight requests and return the right headers, the errors vanished, and now I know to always check the server first when I see a No ‘Access-Control-Allow-Origin’ error.

Related blog posts