Skip to content
LangStop

Tutorials

Keyboard Shortcuts

ActionShortcut
Toggle SidebarCtrl+B
Save TabCtrl+S
Close TabAlt+W
Switch to Tab 1Alt+Shift+1
Switch to Tab 2Alt+Shift+2
Switch to Tab 3Alt+Shift+3
Switch to Tab 4Alt+Shift+4
Switch to Tab 5Alt+Shift+5
Switch to Tab 6Alt+Shift+6
Switch to Tab 7Alt+Shift+7
Switch to Tab 8Alt+Shift+8
Switch to Tab 9Alt+Shift+9

How to Fix CORS Errors — Complete Guide with Examples

What Is CORS and Why Does the Browser Enforce It?

CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls how web pages from one origin can request resources from a different origin. An origin is defined by the protocol (https), domain (api.example.com), and port (:443) — a change in any of these makes it a different origin.

Browsers enforce the Same-Origin Policy by default, which blocks JavaScript from making requests to a different origin than the page served from. CORS provides a way for servers to opt in and explicitly allow cross-origin requests via HTTP headers.

Why CORS Exists

Without CORS, any website could make authenticated requests to your bank's API, read your email, or access private data — all from within your browser session. CORS is the browser's way of asking the server: "Is this foreign website allowed to read your response?"

The critical distinction: CORS is enforced by the browser, not by the server. A server will still process a cross-origin request and return data — the browser just hides the response from the client-side JavaScript if the server did not include the appropriate CORS headers.


Common CORS Error Messages

"No 'Access-Control-Allow-Origin' header is present"

Access to fetch at 'https://api.example.com/data' from origin 'https://myapp.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.

Meaning: The server did not include the Access-Control-Allow-Origin header in its response. The browser received a response but cannot share it with your JavaScript.

"Response to preflight request doesn't pass access control check"

Access to fetch at 'https://api.example.com/submit' from origin 'https://myapp.com'
has been blocked by CORS policy: Response to preflight request doesn't pass
access control check: It does not have HTTP ok status.

Meaning: The browser sent an OPTIONS preflight request (described below) and the server returned an error status (like 404 or 500) instead of the expected 200 with CORS headers.

"Cannot set credentials with 'Access-Control-Allow-Origin' set to '*'"

Access to fetch at 'https://api.example.com/user' from origin 'https://myapp.com'
has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin'
header in the response must not be the wildcard '*' when the request's credentials
mode is 'include'.

Meaning: When sending cookies or authorization headers, the server must specify an explicit origin, not *. Wildcards are not allowed with credentials.

"Method PUT is not allowed by Access-Control-Allow-Methods"

Access to fetch at 'https://api.example.com/resource' from origin 'https://myapp.com'
has been blocked by CORS policy: Method PUT is not allowed by
Access-Control-Allow-Methods in preflight response.

Meaning: The preflight response listed allowed HTTP methods, but PUT (or DELETE, PATCH, etc.) was not included. The server must list all methods your frontend uses.


Server-Side Fix: Setting Access-Control-Allow-Origin

The core CORS fix is adding HTTP response headers on the server. Here is every header you need to understand:

Essential CORS Response Headers

Header Purpose Example
Access-Control-Allow-Origin Specifies which origins can read the response https://myapp.com or *
Access-Control-Allow-Methods Lists permitted HTTP methods GET, POST, PUT, DELETE
Access-Control-Allow-Headers Lists permitted request headers Content-Type, Authorization
Access-Control-Allow-Credentials Whether to expose cookies/auth headers true
Access-Control-Max-Age How long to cache the preflight result (seconds) 86400
Access-Control-Expose-Headers Non-standard headers accessible to JS X-Total-Count, X-Rate-Limit

Basic Configuration

Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

Important: Use a specific origin in production, not *. The wildcard is convenient for development but prevents credentials and is a security risk.


Framework-Specific Solutions

Express.js (Node.js)

The simplest approach uses the cors npm package:

const express = require('express');
const cors = require('cors');
const app = express();
 
// Allow all origins (development only)
app.use(cors());
 
// Restrict to specific origin (production)
app.use(cors({
  origin: 'https://myapp.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400,
}));
 
// Dynamic origin based on request
const allowedOrigins = ['https://myapp.com', 'https://staging.myapp.com'];
app.use(cors({
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,
}));

To handle OPTIONS preflight requests explicitly:

app.options('*', cors()); // Enable preflight for all routes
 
app.post('/api/data', cors(), (req, res) => {
  res.json({ message: 'CORS-enabled POST request' });
});

Nginx

Add CORS headers to your server block or location context:

server {
    listen 443 ssl;
    server_name api.example.com;
 
    location / {
        # Set CORS headers
        add_header 'Access-Control-Allow-Origin' 'https://myapp.com' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Max-Age' '86400' always;
 
        # Handle preflight requests
        if ($request_method = 'OPTIONS') {
            add_header 'Content-Length' '0';
            add_header 'Content-Type' 'text/plain';
            return 204;
        }
 
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

The always flag ensures the header is sent even on error responses (4xx, 5xx).

Apache

Using .htaccess or the virtual host configuration:

<IfModule mod_headers.c>
    Header always set Access-Control-Allow-Origin "https://myapp.com"
    Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
    Header always set Access-Control-Allow-Headers "Content-Type, Authorization"
    Header always set Access-Control-Allow-Credentials "true"
    Header always set Access-Control-Max-Age "86400"
</IfModule>
 
# Handle preflight OPTIONS requests
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]

Make sure mod_headers is enabled: sudo a2enmod headers.

Flask (Python)

Using the flask-cors extension:

from flask import Flask, jsonify
from flask_cors import CORS
 
app = Flask(__name__)
 
# Allow all origins (development)
CORS(app)
 
# Restrict to specific origin
CORS(app, resources={
    r"/api/*": {
        "origins": "https://myapp.com",
        "methods": ["GET", "POST", "PUT", "DELETE"],
        "allow_headers": ["Content-Type", "Authorization"],
        "supports_credentials": True,
        "max_age": 86400
    }
})
 
@app.route('/api/data')
def get_data():
    return jsonify({"message": "CORS-enabled response"})

Django (Python)

Using django-cors-headers:

# settings.py
 
INSTALLED_APPS = [
    ...
    'corsheaders',
    ...
]
 
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',  # As early as possible
    'django.middleware.common.CommonMiddleware',
    ...
]
 
# Allow specific origin (production)
CORS_ALLOWED_ORIGINS = [
    "https://myapp.com",
    "https://staging.myapp.com",
]
 
# Or allow all (development only)
CORS_ALLOW_ALL_ORIGINS = True  # Not for production!
 
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_METHODS = [
    "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS",
]
CORS_ALLOW_HEADERS = [
    "content-type", "authorization",
]
CORS_PREFLIGHT_MAX_AGE = 86400

Handling Preflight OPTIONS Requests

When a browser sends a cross-origin request that is not a simple request, it first sends an OPTIONS preflight to check if the server allows the actual request.

What Triggers a Preflight?

A request is considered "simple" (no preflight needed) only when:

  • Method is GET, HEAD, or POST
  • Content-Type is application/x-www-form-urlencoded, multipart/form-data, or text/plain
  • No custom headers like Authorization or X-Custom-Header

Everything else triggers a preflight: PUT, DELETE, PATCH, application/json content type, custom headers, or credentials.

Preflight Flow

Browser                          Server
  │                                │
  │  OPTIONS /api/data             │
  │  Origin: https://myapp.com     │
  │  Access-Control-Request-Method:│
  │    POST                        │
  │  Access-Control-Request-Headers:│
  │    Content-Type, Authorization │
  │───────────────────────────────>│
  │                                │
  │  200 OK                        │
  │  Access-Control-Allow-Origin:  │
  │    https://myapp.com           │
  │  Access-Control-Allow-Methods: │
  │    GET, POST, PUT              │
  │  Access-Control-Allow-Headers: │
  │    Content-Type, Authorization │
  │<───────────────────────────────│
  │                                │
  │  POST /api/data                │
  │  Content-Type: application/json│
  │  Authorization: Bearer ...     │
  │───────────────────────────────>│
  │                                │
  │  200 OK (with CORS headers)    │
  │<───────────────────────────────│

Your server must respond to OPTIONS with 200 (not 404, 405, or 500) and include the CORS headers. Many frameworks require explicit OPTIONS route handling.


Credentials and Cookies with CORS

When your frontend needs to send cookies, HTTP authentication, or client TLS certificates cross-origin:

Frontend (Fetch)

fetch('https://api.example.com/user', {
  method: 'GET',
  credentials: 'include',  // Send cookies with the request
  headers: {
    'Content-Type': 'application/json',
  },
});

Frontend (Axios)

axios.get('https://api.example.com/user', {
  withCredentials: true,  // Send cookies
});

Server Requirements

When credentials: 'include' is used:

  1. Access-Control-Allow-Origin must be an explicit origin — The wildcard * is not allowed.
  2. Access-Control-Allow-Credentials: true must be set — This header tells the browser to expose the response to the frontend.
  3. Access-Control-Allow-Origin and Access-Control-Allow-Credentials must be consistent — The origin must match exactly; no pattern matching.

Important Security Note

With credentialed requests, the server should validate the Origin header against a whitelist in production. Never echo back the Origin header blindly:

// ❌ Dangerous — echoes any origin
app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
  next();
});
 
// ✅ Secure — validates against whitelist
const WHITELIST = ['https://myapp.com', 'https://admin.myapp.com'];
app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (WHITELIST.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Access-Control-Allow-Credentials', 'true');
  }
  next();
});

Development Workarounds

Proxy Middleware (Webpack / Vite)

During local development, proxy API requests through your dev server to avoid CORS entirely:

Vite (vite.config.js):

export default {
  server: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        secure: true,
      },
    },
  },
};

Webpack Dev Server (webpack.config.js):

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        secure: true,
      },
    },
  },
};

With a proxy, your frontend makes requests to /api/... on the same origin, so no CORS headers are needed.

CORS Browser Extensions

Extensions like "CORS Unblock" or "Allow CORS" disable CORS enforcement in the browser by intercepting response headers. These are useful for rapid prototyping, but never rely on them in production — they only affect your local browser, not your users'.

Disable CORS in Chrome (Development Only)

Launch Chrome with web security disabled:

# macOS
open -a Google Chrome --args --disable-web-security --user-data-dir=/tmp/chrome_dev
 
# Linux
google-chrome --disable-web-security --user-data-dir=/tmp/chrome_dev
 
# Windows
start chrome --disable-web-security --user-data-dir=C:/Temp/chrome_dev

Warning: This disables all same-origin policy checks. Never browse the web in this session — use it only for testing your own application.


Testing CORS with curl and REST Clients

Using curl

# Check if CORS headers are present
curl -H "Origin: https://myapp.com" \
     -H "Access-Control-Request-Method: POST" \
     -X OPTIONS \
     -I https://api.example.com/data
 
# Expected response headers
# access-control-allow-origin: https://myapp.com
# access-control-allow-methods: GET, POST, PUT, DELETE
# access-control-allow-headers: Content-Type, Authorization
# access-control-max-age: 86400

Simulating a Cross-Origin POST

curl -X POST https://api.example.com/data \
  -H "Origin: https://myapp.com" \
  -H "Content-Type: application/json" \
  -d '{"key": "value"}' \
  -I

Using LangStop REST API Client

The LangStop REST API Client lets you inspect response headers including CORS headers. Make a request and check the response headers tab for:

  • access-control-allow-origin
  • access-control-allow-methods
  • access-control-allow-headers
  • access-control-allow-credentials

This is the fastest way to verify your CORS configuration is correct without writing any code.


Summary: CORS Fix Checklist

  1. Identify the origin — Determine which frontend origin is making the request.
  2. Add CORS headers — Set Access-Control-Allow-Origin to your frontend origin.
  3. Handle preflight — Respond to OPTIONS requests with 200 and the appropriate CORS headers.
  4. Allow methods and headers — List all HTTP methods and custom headers your frontend uses.
  5. Handle credentials — If sending cookies or auth, use Access-Control-Allow-Credentials: true and a specific origin.
  6. Test with curl — Verify headers appear in the OPTIONS response.
  7. Use a proxy in development — Avoid CORS issues entirely by proxying API requests through your dev server.

Related LangStop Tools

Related Tools

Try these complementary developer tools: