SelestL Solutions
Technology

How to Fix CORS No Access-Control-Allow-Origin

By SelestL • Published on
How to Fix CORS No Access-Control-Allow-Origin

You write an asynchronous fetch() or Axios request in your React, Vue, or vanilla JavaScript frontend to retrieve data from your backend API. Everything looks correct, but your browser console lights up in red: Access to fetch at 'https://api.example.com/data' from origin 'https://app.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. The network request fails, returning a status of (failed) net::ERR_FAILED.

The CORS Access-Control-Allow-Origin error is one of the most frequent hurdles encountered when decoupling frontend single-page applications from backend REST or GraphQL microservices. Under the browser Same-Origin Policy (SOP), client scripts are strictly prohibited from reading responses from a differing domain, subdomain, port, or protocol unless the destination server explicitly returns permission headers.

In this technical walkthrough, we break down preflight OPTIONS requests, explain why CORS is strictly a server-side configuration, and provide production-ready middleware snippets for Express (Node.js), Laravel (PHP), FastAPI (Python), and Nginx. If your business needs secure, scalable API architectures, check out our end-to-end API development and backend integration services.

1. How Cross-Origin Resource Sharing Actually Operates

1. How Cross-Origin Resource Sharing Actually Operates

According to the official MDN Web Docs, two URLs have the "Same Origin" only if their protocol (HTTP vs HTTPS), domain (example.com vs api.example.com), and port (3000 vs 8000) are identical. If any element differs, the browser initiates a cross-origin transaction.

  • Simple Requests: Basic GET and POST requests without custom authorization headers send an Origin header. The server must reply with Access-Control-Allow-Origin matching the requesting domain (or wildcard *).
  • Preflight OPTIONS Requests: If your request includes JSON payloads (application/json), Bearer tokens, or custom HTTP headers, the browser automatically sends an HTTP OPTIONS handshake first. If the server does not handle OPTIONS, the main request is never sent.
  • Credentials and Cookies: If your frontend passes credentials: 'include', the wildcard * is rejected by browsers; you must return the explicit origin string and set Access-Control-Allow-Credentials: true.

Common Misconception: Frontend Headers Cannot Fix CORS

Setting 'Access-Control-Allow-Origin': '*' in your client-side fetch or Axios configuration does nothing. CORS headers are response headers that must be emitted by the backend server answering the request.

2. Backend Configurations for Modern Frameworks

Add these verified middleware configurations to your backend service:

Fix A: Express.js (Node.js)

Install the official cors package and configure allowed origins and preflight handling:

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

const corsOptions = {
  origin: ['https://app.example.com', 'http://localhost:3000'],
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true
};

app.use(cors(corsOptions));

Fix B: Laravel 10 and 11 (PHP)

In Laravel, CORS is handled natively via config/cors.php. Update your allowed paths and origins:

return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['https://app.example.com', 'http://localhost:3000'],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 86400,
    'supports_credentials' => true,
];

Fix C: Nginx Reverse Proxy Header Injection

If you prefer terminating CORS at your edge web server before traffic hits your backend application, add these directives to your Nginx location block:

location /api/ {
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '$http_origin' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
        add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Content-Length' 0;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        return 204;
    }

    add_header 'Access-Control-Allow-Origin' '$http_origin' always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    proxy_pass http://127.0.0.1:8000;
}

3. Local Development Workaround: Frontend Proxying

During local development, you can completely bypass browser CORS checks by routing requests through your development server (like Vite or Next.js) which proxies traffic server-to-server:

// vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true,
        secure: false,
      }
    }
  }
};

4. CORS Response Header Reference

HTTP Header Purpose Sample Value
Access-Control-Allow-Origin Specifies permitted requesting domain https://app.example.com
Access-Control-Allow-Methods Allowed HTTP request verbs GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers Permitted custom request headers Authorization, Content-Type
Access-Control-Allow-Credentials Allows transmitting cookies and tokens true

To learn more about modern web full-stack architectures, read our technical guide on deploying modern web applications. For custom enterprise security and API design consulting, reach out through our contact page.

5. Where to Go From Here

CORS is a browser security protocol designed to protect your users, not an obstacle designed to impede developers. Follow these architectural best practices when designing web services:

  • Never use Access-Control-Allow-Origin: * on authenticated endpoints that handle user credentials or private data.
  • Ensure your backend framework answers preflight HTTP OPTIONS requests with a 204 No Content or 200 OK status.
  • Keep staging and production CORS whitelist domains clearly separated across your environment variable files.

Frequently Asked Questions

Why does Postman work but my browser throws a CORS error?
CORS is strictly enforced by web browsers to protect end-users under the Same-Origin Policy. Standalone tools like Postman, curl, and backend servers do not enforce browser origin checks, so their requests succeed.
Can I fix CORS by changing my frontend JavaScript code?
No. CORS headers must be sent by the server responding to the HTTP request. Client-side fetch headers cannot bypass browser security policies unless you use a development proxy.
Why does Access-Control-Allow-Origin: * fail when sending cookies?
Browsers explicitly block credentialed requests (cookies or Authorization headers) when the server responds with a wildcard origin (*). You must specify the exact client domain and include Access-Control-Allow-Credentials: true.
What is an HTTP OPTIONS preflight request?
A preflight request is an automated HTTP OPTIONS request sent by the browser before the actual request to verify that the target server understands the origin, HTTP method, and custom headers being sent.
Tags: #CORS #JavaScript #Web Development #APIs #Node.js #PHP

Related Articles