๐ŸŒ Detecting your locationโ€ฆ
๐Ÿ“ข Advertisement โ€” Configure AdSense in Appearance โ†’ Customize โ†’ AdSense Settings

How to Fix ‘CORS Policy: No Access-Control-Allow-Origin Header’ Error

โฑ๏ธ5 min read  ยท  960 words

The error “Access to fetch at ‘…’ from origin ‘…’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present” is one of the most common web development errors. It’s enforced by the browser as a security mechanism. Here’s every cause and fix.

๐Ÿ”‘ Key Takeaway

The error “Access to fetch at ‘…’ from origin ‘…’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present” is one of the most common web development errors. It’s enforced by the browser as a security mechanism.

Why CORS Exists (Required Understanding)

CORS (Cross-Origin Resource Sharing) prevents malicious websites from making requests to your bank’s API using your logged-in cookies. Without CORS, visiting evil.com could silently make API calls to yourbank.com with your session cookies. The browser enforces CORS โ€” servers declare which origins are allowed.

CORS is a browser security feature โ€” it only applies to browser-initiated requests. curl, Postman, and server-to-server requests are not affected by CORS.

Fix 1: Express.js โ€” Using cors Package

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

// โœ… Allow specific origin (recommended for production)
app.use(cors({
  origin:      'https://yourfrontend.com',
  methods:     ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,   // required if frontend sends cookies/auth headers
}));

// โœ… Allow multiple origins
const allowedOrigins = ['https://app.example.com', 'https://admin.example.com'];
app.use(cors({
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error(`Origin ${origin} not allowed by CORS`));
    }
  },
  credentials: true,
}));

// โŒ Allow all origins (never in production with credentials)
app.use(cors({ origin: '*' }));
// Note: wildcard '*' cannot be used with credentials: true

Fix 2: Manual CORS Headers in Express

// Without the cors package
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'https://yourfrontend.com');
  res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
  res.header('Access-Control-Allow-Credentials', 'true');

  // Handle preflight requests
  if (req.method === 'OPTIONS') {
    return res.status(204).end();
  }
  next();
});

Fix 3: Nginx Reverse Proxy

location /api/ {
    proxy_pass http://localhost:3000/;

    # CORS headers
    add_header 'Access-Control-Allow-Origin' 'https://yourfrontend.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;

    # Handle preflight
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://yourfrontend.com';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;
        return 204;
    }
}

Fix 4: Frontend โ€” Credentials Mode

// โŒ Fetch without credentials โ€” cookies not sent
const res = await fetch('https://api.example.com/user');

// โœ… Include credentials (cookies, authorization headers)
const res = await fetch('https://api.example.com/user', {
  credentials: 'include',   // send cookies cross-origin
});

// For axios:
axios.defaults.withCredentials = true;
// Or per-request:
axios.get('/api/user', { withCredentials: true });

Important: When credentials: 'include', the server must set Access-Control-Allow-Origin to the specific origin (not *) AND Access-Control-Allow-Credentials: true.

Understanding Preflight Requests

For “non-simple” requests (PUT, DELETE, custom headers, application/json body), the browser sends an OPTIONS preflight request first to check if the actual request is allowed. If the preflight fails, the actual request never executes.

// Browser sends OPTIONS before POST with JSON body:
OPTIONS /api/users HTTP/1.1
Origin: https://frontend.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type

// Server must respond:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400  // cache preflight for 24 hours

Development Proxy (Avoid CORS During Development)

// vite.config.ts โ€” proxy /api to backend during dev
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^/api/, ''),
      }
    }
  }
};

// package.json (Create React App) โ€” proxy all /api requests
{
  "proxy": "http://localhost:3000"
}

The proxy makes requests from the same origin as the frontend (from the dev server), bypassing CORS entirely during development โ€” without compromising production security.

Common Mistakes

// โŒ Mistake 1: CORS headers on wrong route
app.get('/api/data', (req, res) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.json(data);
});
// OPTIONS preflight hits a different handler that doesn't have CORS headers

// โŒ Mistake 2: Setting headers after response sent
app.use(cors());
app.get('/data', authenticate, (req, res) => {
  res.header('Access-Control-Allow-Origin', '*');  // Too late if cors() runs first
});

// โŒ Mistake 3: Multiple conflicting CORS headers
// Browser rejects when two Access-Control-Allow-Origin headers present
// Don't set CORS in both nginx AND Express โ€” pick one place

Frequently Asked Questions

Q: Can I disable CORS in the browser for development?
A: Yes: open -a "Google Chrome" --args --disable-web-security --user-data-dir="/tmp/chrome-dev". ONLY for local dev โ€” never browse real sites with this flag. Using a proxy is safer and doesn’t require disabling security.

Q: Why does it work in Postman but not the browser?
A: CORS is only enforced by browsers. Postman sends the request directly without a browser CORS check. This confirms your server is working โ€” you just need to add CORS headers.

Q: Should I add CORS headers in the application or the reverse proxy?
A: One place only. If using nginx, add headers in nginx and don’t add them in Express (prevents duplicate headers). If deploying to a service without nginx control, add in Express.

Q: Why does my API work from the same origin but not cross-origin?
A: That’s exactly what CORS prevents โ€” cross-origin requests require explicit server permission. Same-origin requests (frontend and API on same domain/port) don’t need CORS headers.

Q: Can I use a CORS proxy service?
A: For development prototyping, services like cors-anywhere work. Never for production โ€” you’re sending all your API requests through a third-party service. Fix CORS properly on your server.

Conclusion

Fix CORS by adding proper Access-Control-Allow-Origin headers on your server โ€” not by disabling CORS or using browser plugins. For Express.js, the cors npm package handles all cases cleanly. For Nginx, add headers at the proxy level. For development, use Vite’s proxy config to avoid CORS entirely. Always specify exact origins rather than wildcard * in production, especially when using credentials.

โœ๏ธ Leave a Comment

Your email address will not be published. Required fields are marked *

๐ŸŒ Read in:๐Ÿ‡ฌ๐Ÿ‡ง English๐Ÿ‡ฉ๐Ÿ‡ช Deutsch๐Ÿ‡ง๐Ÿ‡ท Portuguรชs๐Ÿ‡ธ๐Ÿ‡ฆ ุงู„ุนุฑุจูŠุฉ๐Ÿ‡ฎ๐Ÿ‡ณ เคนเคฟเคจเฅเคฆเฅ€๐Ÿ‡ง๐Ÿ‡ฉ เฆฌเฆพเฆ‚เฆฒเฆพ