🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

How to Fix Access-Control-Allow-Origin Missing Header CORS Error

⏱️4 min read  ·  837 words

The error No ‘Access-Control-Allow-Origin’ header is present on the requested resource is the most common CORS error. It means the browser blocked your cross-origin request because the server didn’t say it’s allowed. Here’s how to fix it properly.

Why This Happens

Browsers enforce the Same-Origin Policy for security — a page at app.example.com can’t freely make requests to api.other.com. For cross-origin requests, the server must include an Access-Control-Allow-Origin header saying which origins are permitted. If it’s missing, the browser blocks the response and shows this error.

Key point: This is enforced by the browser and fixed on the SERVER. The client can’t add this header — the server must send it.

Fix 1: Express with the cors Package

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

// Allow all origins (development only)
app.use(cors());

// ✅ Production: allow specific origin
app.use(cors({
  origin: 'https://app.example.com',
}));

// ✅ Multiple allowed origins
const allowed = ['https://app.example.com', 'https://admin.example.com'];
app.use(cors({
  origin: (origin, callback) => {
    if (!origin || allowed.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
}));

Fix 2: Express Manual Headers

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

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

Fix 3: Nginx

location /api/ {
    add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;

    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://app.example.com';
        add_header 'Access-Control-Max-Age' 86400;
        return 204;
    }

    proxy_pass http://localhost:3000;
}

Fix 4: With Credentials (Cookies/Auth)

// When sending cookies or auth headers cross-origin:

// Server: exact origin (NOT wildcard) + allow credentials
app.use(cors({
  origin: 'https://app.example.com',   // must be exact, not '*'
  credentials: true,
}));

// Client: include credentials
fetch('https://api.example.com/data', {
  credentials: 'include',
});

// ❌ This combination FAILS:
// origin: '*' with credentials: true
// The browser rejects wildcard origin when credentials are included

The Development Proxy Alternative

// Avoid CORS during development entirely with a dev proxy

// vite.config.ts
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
      }
    }
  }
};
// Frontend calls /api/... which the dev server proxies to your backend -
// same origin, no CORS. (Production still needs proper CORS headers.)

Common Mistakes

// 🐛 Setting CORS headers in BOTH nginx and Express
// Results in duplicate Access-Control-Allow-Origin headers,
// which the browser rejects. Set them in ONE place.

// 🐛 Wildcard with credentials
res.header('Access-Control-Allow-Origin', '*');   // fails with credentials

// 🐛 Forgetting to handle OPTIONS preflight
// PUT/DELETE/JSON requests send a preflight OPTIONS first -
// it must get a valid CORS response too

// 🐛 Trying to fix it in the frontend
// You CANNOT add Access-Control-Allow-Origin from client code -
// it must come from the server

Debugging

# Check what headers the server actually returns
curl -I -H "Origin: https://app.example.com" https://api.example.com/data

# Look for:
# Access-Control-Allow-Origin: https://app.example.com  ← should be present

# If it's missing, the server isn't configured correctly.
# If it's the wrong origin, fix the server's allowed origins.

Frequently Asked Questions

Q: Why can’t I fix this in my frontend code?
A: CORS is enforced by the browser and controlled by the SERVER. The Access-Control-Allow-Origin header must come from the server responding to the request. No amount of frontend code can add it — you must configure the server (or use a proxy).

Q: Why does it work in Postman but not the browser?
A: CORS is only enforced by browsers. Postman and curl make requests directly without the browser’s CORS check. This confirms your server works — you just need to add the CORS headers for browser clients.

Q: Should I just use Access-Control-Allow-Origin: *?
A: Only for truly public APIs with no credentials. For anything with authentication or cookies, you must specify exact origins (wildcard fails with credentials). Specifying exact origins is also more secure — don’t use * in production for authenticated APIs.

Q: Where should I set CORS headers — nginx or the app?
A: One place only. If you set them in both nginx and your app, you get duplicate headers, which browsers reject. Pick one layer (usually the app for flexibility, or nginx if you have a reverse proxy) and configure CORS there.

Q: Why does my POST work but PUT fails?
A: PUT/DELETE and requests with JSON bodies or custom headers trigger a preflight OPTIONS request. Your server must respond to OPTIONS with valid CORS headers too. Ensure your CORS config handles the OPTIONS method, not just GET/POST.

Conclusion

“No ‘Access-Control-Allow-Origin’ header is present” means the server didn’t tell the browser your cross-origin request is allowed. The fix is always on the SERVER: add the Access-Control-Allow-Origin header via the cors package (Express), manual headers, or nginx config, specifying exact origins (required when using credentials). Handle OPTIONS preflight requests, set CORS in only one layer to avoid duplicates, and remember you can’t fix it from frontend code. For development, a dev-server proxy avoids CORS entirely — but production always needs proper server-side CORS headers.

✍️ Leave a Comment

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

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা