Fix CORS Error: Access-Control-Allow-Origin header missing in Express
This error occurs when a browser blocks a cross-origin request because the server response lacks the Access-Control-Allow-Origin header. Fix it by installing and configuring the cors middleware in your Express app, specifying allowed origins, methods, and headers for your frontend domain.
Reading the Stack Trace
Here's what each line means:
- No 'Access-Control-Allow-Origin' header is present on the requested resource.: The browser enforced the same-origin policy because the server did not include the required CORS header.
- from origin 'http://localhost:3000': The frontend runs on port 3000, but the API is on port 4000, making this a cross-origin request.
- at XMLHttpRequest.handleError (node_modules/axios/lib/adapters/xhr.js:162:14): Axios caught the blocked request and threw a Network Error since the browser prevented the response from being read.
Common Causes
1. No CORS middleware configured
The Express server does not include any CORS middleware, so cross-origin requests from the frontend are blocked by the browser.
const express = require('express');
const app = express();
app.get('/api/users', (req, res) => {
res.json([{ id: 1, name: 'Alice' }]);
});
app.listen(4000);
2. CORS middleware placed after routes
The cors middleware is added after the routes, so it never runs for those routes.
const express = require('express');
const cors = require('cors');
const app = express();
app.get('/api/users', (req, res) => {
res.json([{ id: 1, name: 'Alice' }]);
});
app.use(cors()); // Too late - routes already defined above
The Fix
Install the cors package and add it as middleware before your routes. Configure it with the specific origin of your frontend to allow cross-origin requests while keeping the API secure.
const express = require('express');
const app = express();
app.get('/api/users', (req, res) => {
res.json([{ id: 1, name: 'Alice' }]);
});
app.listen(4000);
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'http://localhost:3000',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
app.get('/api/users', (req, res) => {
res.json([{ id: 1, name: 'Alice' }]);
});
app.listen(4000);
Testing the Fix
const request = require('supertest');
const express = require('express');
const cors = require('cors');
describe('CORS configuration', () => {
let app;
beforeEach(() => {
app = express();
app.use(cors({
origin: 'http://localhost:3000',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
app.get('/api/users', (req, res) => {
res.json([{ id: 1, name: 'Alice' }]);
});
});
it('includes Access-Control-Allow-Origin header', async () => {
const response = await request(app)
.get('/api/users')
.set('Origin', 'http://localhost:3000');
expect(response.headers['access-control-allow-origin']).toBe('http://localhost:3000');
});
it('responds to preflight OPTIONS request', async () => {
const response = await request(app)
.options('/api/users')
.set('Origin', 'http://localhost:3000')
.set('Access-Control-Request-Method', 'POST');
expect(response.status).toBe(204);
});
});
Run your tests:
npm test
Pushing Through CI/CD
git checkout -b fix/cors-configuration,git add src/app.js src/__tests__/cors.test.js package.json,git commit -m "fix: add CORS middleware for cross-origin API access",git push origin fix/cors-configuration
Your CI config should look something like this:
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test
- run: npm run lint
The Full Manual Process: 18 Steps
Here's every step you just went through to fix this one bug:
- Notice the error alert or see it in your monitoring tool
- Open the error dashboard and read the stack trace
- Identify the file and line number from the stack trace
- Open your IDE and navigate to the file
- Read the surrounding code to understand context
- Reproduce the error locally
- Identify the root cause
- Write the fix
- Run the test suite locally
- Fix any failing tests
- Write new tests covering the edge case
- Run the full test suite again
- Create a new git branch
- Commit and push your changes
- Open a pull request
- Wait for code review
- Merge and deploy to production
- Monitor production to confirm the error is resolved
Total time: 30-60 minutes. For one bug.
Or Let bugstack Fix It in Under 2 minutes
Every step above? bugstack does it automatically.
Step 1: Install the SDK
npm install bugstack-sdk
Step 2: Initialize
const { initBugStack } = require('bugstack-sdk')
initBugStack({ apiKey: process.env.BUGSTACK_API_KEY })
Step 3: There is no step 3.
bugstack handles everything from here:
- Captures the stack trace and request context
- Pulls the relevant source files from your GitHub repo
- Analyzes the error and understands the code context
- Generates a minimal, verified fix
- Runs your existing test suite
- Pushes through your CI/CD pipeline
- Deploys to production (or opens a PR for review)
Time from error to fix deployed: Under 2 minutes.
Human involvement: zero.
Try bugstack Free →No credit card. 5-minute setup. Cancel anytime.
Deploying the Fix (Manual Path)
- Install the cors package with npm install cors.
- Open a pull request with the CORS middleware configuration.
- Wait for CI checks to pass on the PR.
- Have a teammate review the allowed origins to ensure they match your deployment domains.
- Merge to main and verify cross-origin requests work in staging.
Frequently Asked Questions
BugStack tests CORS headers for both simple and preflight requests, confirms the allowed origin matches your frontend, and verifies no security policies are weakened.
BugStack delivers fixes as pull requests. Your team reviews the CORS configuration to ensure only intended origins are allowed before merging.
No. Using a wildcard allows any website to call your API. Always specify exact origins in production to prevent unauthorized cross-origin access.
CORS is enforced by browsers only. Server-to-server requests (like from Postman or curl) skip CORS entirely, which is why the API works from those tools.