How to Handle CORS Errors in Web Development: Causes and Fixes Explained
What Is CORS and Why Does It Exist? If you have ever built a web application where the frontend talks to a separate backend API, you have almost certainly seen a bright red error in your browser console that reads something like “Access to fetch at … has been blocked by CORS policy.” CORS stands for Cross-Origin Resource Sharing. It is a security mechanism enforced by web browsers that restricts web pages from making HTTP requests to a domain (or port, or protocol) different from the one that served the page. The browser does this to protect users from malicious sites that could silently call APIs on their behalf. In practical terms, if your frontend is running on http://localhost:3000 and your API lives at http://localhost:5000, the browser considers these two different origins. Unless the server at port 5000 explicitly tells the browser “I allow requests from port 3000,” the browser will block the response. Understanding the mechanism behind CORS is the first step to fixing it correctly and securely. Let’s break it all down. How CORS Works Under the Hood When your JavaScript code in the browser makes a cross-origin request, the browser adds an Origin header to the outgoing request. The server is expected to respond with specific headers that tell the browser whether the request is allowed. The most important response header is: Access-Control-Allow-Origin – Specifies which origins are permitted to access the resource. There are additional headers involved depending on the complexity of the request: Response Header Purpose Access-Control-Allow-Origin Declares allowed origin(s). Can be a specific URL or * (wildcard). Access-Control-Allow-Methods Lists HTTP methods the server permits (GET, POST, PUT, DELETE, etc.). Access-Control-Allow-Headers Specifies which custom headers the client is allowed to send. Access-Control-Allow-Credentials Indicates whether cookies or auth headers can be included. Access-Control-Max-Age How long (in seconds) the preflight response can be cached. What Is a Preflight Request? Not all cross-origin requests are treated equally. The browser categorizes them into simple requests and preflighted requests. Simple Requests A request is considered “simple” if it meets all of these conditions: The HTTP method is GET, HEAD, or POST. The only headers set manually are from the safe list: Accept, Accept-Language, Content-Language, or Content-Type (with values limited to application/x-www-form-urlencoded, multipart/form-data, or text/plain). Simple requests go directly to the server. The browser checks the response headers and either exposes or blocks the response. Preflighted Requests If a request does not qualify as simple (for example, it uses PUT or DELETE, or sends a Content-Type: application/json header), the browser first sends an automatic OPTIONS request called a preflight. This preflight asks the server: “Are you okay with this type of request from this origin?” Only if the server responds to the OPTIONS request with the correct CORS headers will the browser proceed to send the actual request. This is where many developers get tripped up. Their server handles the main request fine but does not respond to the preflight OPTIONS request correctly, resulting in a CORS error. Most Common CORS Error Scenarios Here are the situations developers run into most frequently: No Access-Control-Allow-Origin header in the response. The server simply does not include the header at all. Wildcard origin with credentials. Using Access-Control-Allow-Origin: * while also setting Access-Control-Allow-Credentials: true is not allowed by browsers. Missing preflight handling. The server does not respond to OPTIONS requests, or returns a non-2xx status code for them. Mismatched allowed methods or headers. The server allows GET and POST but the client sends a PUT request. Protocol or port mismatch. The frontend is on https but calling an http API, or the ports differ. Redirect on a preflight request. If the OPTIONS request gets redirected (e.g., HTTP to HTTPS), the browser will reject it. How to Fix CORS Errors: Concrete Configurations Let’s walk through the most popular server environments and show exactly how to configure CORS headers correctly. Fix CORS in Node.js with Express The easiest approach is to use the cors npm package. Step 1: Install the package: npm install cors Step 2: Use it in your Express app: const express = require(‘express’); const cors = require(‘cors’); const app = express(); // Allow a specific origin app.use(cors({ origin: ‘https://yourfrontend.com’, methods: [‘GET’, ‘POST’, ‘PUT’, ‘DELETE’], allowedHeaders: [‘Content-Type’, ‘Authorization’], credentials: true })); app.listen(5000, () => { console.log(‘Server running on port 5000’); }); If you need to allow multiple origins dynamically: const allowedOrigins = [‘https://yourfrontend.com’, ‘https://staging.yourfrontend.com’]; app.use(cors({ origin: function (origin, callback) { if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error(‘Not allowed by CORS’)); } }, credentials: true })); Manual Approach (Without the cors Package) If you prefer not to use a third-party package, you can set headers manually with middleware: 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 if (req.method === ‘OPTIONS’) { return res.sendStatus(204); } next(); }); Key point: Always handle the OPTIONS method explicitly and return a 204 No Content status for preflight requests. Fix CORS in Apache For Apache servers, you add CORS headers in your .htaccess file or in the virtual host configuration. <IfModule mod_headers.c> Header set Access-Control-Allow-Origin “https://yourfrontend.com” Header set Access-Control-Allow-Methods “GET, POST, PUT, DELETE, OPTIONS” Header set Access-Control-Allow-Headers “Content-Type, Authorization” Header set Access-Control-Allow-Credentials “true” # Handle preflight requests RewriteEngine On RewriteCond %{REQUEST_METHOD} OPTIONS RewriteRule ^(.*)$ $1 [R=204,L] </IfModule> Make sure mod_headers and mod_rewrite are enabled on your Apache server: sudo a2enmod headers sudo a2enmod rewrite sudo systemctl restart apache2 Fix CORS in Nginx In your Nginx server block or location block, add the following: location /api/ { # Preflight request handling if ($request_method = ‘OPTIONS’) { add_header ‘Access-Control-Allow-Origin’ ‘https://yourfrontend.com’; add_header ‘Access-Control-Allow-Methods’ ‘GET, POST, PUT, DELETE, OPTIONS’; add_header ‘Access-Control-Allow-Headers’ ‘Content-Type, Authorization’; add_header ‘Access-Control-Allow-Credentials’ ‘true’; add_header ‘Access-Control-Max-Age’ 86400; add_header ‘Content-Length’ 0; add_header ‘Content-Type’ ‘text/plain’; return 204; } 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; proxy_pass http://127.0.0.1:5000; } Important: The always keyword ensures headers are sent even on error responses (4xx, 5xx).
How to Handle CORS Errors in Web Development: Causes and Fixes Explained Read More »