A CORS preflight request failed because the server didn't respond with the required Access-Control-Allow-Origin header. This happens when your Express server doesn't properly configure CORS headers for complex requests (PUT, DELETE, POST with custom headers).
When browsers make 'complex' HTTP requests (anything other than simple GET/HEAD/POST, or requests with custom headers), they automatically send a preflight OPTIONS request first to check if the server allows the cross-origin request. If your server doesn't respond to this OPTIONS request with the required CORS headers—specifically Access-Control-Allow-Origin—the browser blocks the actual request. This is a browser security feature to prevent unauthorized cross-origin access to your API.
First, ensure you have the cors middleware installed in your Node.js project:
npm install corsThis is the standard package for handling CORS in Express.
Add the CORS middleware to your Express app at the top level, before defining routes:
const express = require('express');
const cors = require('cors');
const app = express();
// Apply CORS to all routes
app.use(cors());
// Your routes go here
app.get('/api/data', (req, res) => {
res.json({ message: 'Hello' });
});
app.listen(3000);This allows requests from any origin. If you only tested with GET requests and they worked, the issue is likely that you're missing CORS headers for complex requests.
For production, allow only trusted domains:
const corsOptions = {
origin: ['https://yourdomain.com', 'https://app.yourdomain.com'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization'],
optionsSuccessStatus: 200
};
app.use(cors(corsOptions));The optionsSuccessStatus: 200 is important for legacy browsers that fail on 204 responses.
If you only want CORS on specific routes, you must explicitly handle OPTIONS:
app.options('/api/data', cors(corsOptions)); // For specific route
app.post('/api/data', (req, res) => {
res.json({ message: 'Success' });
});
// Or for all routes
app.options('*', cors(corsOptions));Without this, preflight OPTIONS requests will return 404 and fail.
Check the Network tab in your browser's DevTools:
1. Look for the OPTIONS request that appears before your actual request
2. Click on it and check the Response headers
3. Verify that Access-Control-Allow-Origin is present
4. Verify Access-Control-Allow-Methods includes your HTTP method (POST, DELETE, etc.)
5. If the OPTIONS request returns 404 or 500, your middleware isn't catching it properly
The OPTIONS response should include:
- Access-Control-Allow-Origin: https://yourdomain.com (or *)
- Access-Control-Allow-Methods: GET, POST, PUT, DELETE
- Access-Control-Allow-Headers: Content-Type, Authorization
Ensure CORS middleware is applied BEFORE route handlers:
// WRONG - routes defined before cors
app.get('/api/data', handler);
app.use(cors()); // Too late!
// CORRECT - cors applied first
app.use(cors());
app.get('/api/data', handler);Middleware in Express is executed in order. CORS must be applied before your routes are defined.
Preflight request behavior: Browsers automatically send OPTIONS preflight requests for 'complex' requests—anything with HTTP methods other than GET/HEAD/POST, or requests with custom headers like Authorization. You cannot disable this from client-side code; it's a browser security feature.
Credentials and wildcard origins: If you use Access-Control-Allow-Credentials: true, you cannot use Access-Control-Allow-Origin: *. You must specify exact origins.
Development vs Production: During development, you might use app.use(cors()) to allow all origins. Always restrict to specific origins in production.
Reverse proxies: If your Express app is behind Nginx or another reverse proxy, the proxy might be stripping CORS headers. Configure the proxy to pass them through.
Custom middleware conflicts: If you have other middleware that sends responses early (like authentication checks), they might bypass CORS headers. Ensure CORS is applied globally before any route-specific middleware.
Error: EMFILE: too many open files, watch
EMFILE: fs.watch() limit exceeded
Error: Middleware next() called multiple times (next() invoked twice)
Express middleware next() called multiple times
Error: Worker failed to initialize (worker startup error)
Worker failed to initialize in Node.js
Error: EMFILE: too many open files, open 'file.txt'
EMFILE: too many open files
Error: cluster.fork() failed (cannot create child process)
cluster.fork() failed - Cannot create child process