Skip to main content

Express API CORS

Introduction

When developing modern web applications, you'll often have a frontend application on one domain making requests to your Express REST API on another domain. For example, your React application might be running on localhost:3000, while your Express API runs on localhost:5000. In production, your frontend might be hosted at example.com while your API is at api.example.com.

By default, browsers block these cross-origin HTTP requests due to the Same-Origin Policy, a critical security mechanism built into web browsers. This is where CORS (Cross-Origin Resource Sharing) comes in.

CORS is a mechanism that uses HTTP headers to tell browsers whether a specific web application can share resources with another web application from a different origin (domain, protocol, or port).

Why is CORS Necessary?

Without CORS, if your frontend JavaScript code tries to make a fetch request to an API on a different origin, the browser will block the request for security reasons. This security feature protects users from malicious scripts that might try to access sensitive data across domains.

Let's look at a common scenario:

// Frontend running on http://localhost:3000
fetch('http://localhost:5000/api/users')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

Without proper CORS configuration, this request would fail with an error like:

Access to fetch at 'http://localhost:5000/api/users' from origin 'http://localhost:3000' 
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is
present on the requested resource.

Implementing CORS in Express

Express makes it easy to implement CORS in your API using the cors middleware package.

Step 1: Install the CORS package

bash
npm install cors

Step 2: Basic Implementation

Here's how to enable CORS for all requests:

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

const app = express();

// Enable CORS for all routes
app.use(cors());

app.get('/api/users', (req, res) => {
res.json([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]);
});

app.listen(5000, () => {
console.log('Server running on port 5000');
});

With this basic setup, your API now includes the necessary CORS headers in its responses, allowing requests from any origin.

CORS Configuration Options

The simple implementation above is fine for development, but for production applications, you'll typically want more control over which domains can access your API.

Configuring Specific Origins

javascript
const corsOptions = {
origin: 'https://example.com',
optionsSuccessStatus: 200 // some legacy browsers (IE11) choke on 204
};

app.use(cors(corsOptions));

Multiple Origins

javascript
const corsOptions = {
origin: ['https://example.com', 'https://www.example.com', 'https://app.example.com'],
optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

Dynamic Origin Validation

You can also use a function to dynamically determine whether to allow a request based on the origin:

javascript
const corsOptions = {
origin: function (origin, callback) {
const allowedOrigins = ['https://example.com', 'https://www.example.com'];
if (allowedOrigins.includes(origin) || !origin) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

CORS for Specific Routes

You can also apply CORS to specific routes instead of all routes:

javascript
// Enable CORS for all routes
app.use(cors());

// Different CORS settings for a specific route
const specificCorsOptions = {
origin: 'https://special-client.com',
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
};

app.get('/api/special-data', cors(specificCorsOptions), (req, res) => {
res.json({ message: 'This is special data!' });
});

Advanced CORS Options

The CORS middleware accepts various options for finer control:

Handling Credentials

If your API requests include credentials like cookies, you need to set the credentials option:

javascript
const corsOptions = {
origin: 'https://example.com',
credentials: true,
optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

Configuring Allowed Methods

Specify which HTTP methods are allowed:

javascript
const corsOptions = {
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

Setting Allowed Headers

Control which headers can be used in requests:

javascript
const corsOptions = {
origin: 'https://example.com',
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

Exposing Headers

Specify which headers from the response should be accessible to the client:

javascript
const corsOptions = {
origin: 'https://example.com',
exposedHeaders: ['Content-Length', 'X-Custom-Header'],
optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

Real-World Example: Building a Public API with Selective CORS

Let's create an Express API that serves different content with different CORS policies:

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

// Public API - accessible from anywhere
const publicApiCors = {
origin: '*',
methods: ['GET'],
};

// Partner API - only accessible from trusted domains
const partnerApiCors = {
origin: ['https://trusted-partner1.com', 'https://trusted-partner2.com'],
methods: ['GET', 'POST', 'PUT'],
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization']
};

// Internal API - only accessible from our own domains
const internalApiCors = {
origin: ['https://admin.ourcompany.com', 'https://dashboard.ourcompany.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
};

// Public data endpoint - anyone can access
app.get('/api/public/products', cors(publicApiCors), (req, res) => {
res.json([
{ id: 1, name: 'Basic Widget', price: 9.99 },
{ id: 2, name: 'Advanced Widget', price: 19.99 }
]);
});

// Partner-only endpoint - only trusted partners can access
app.get('/api/partners/discounts', cors(partnerApiCors), (req, res) => {
res.json([
{ id: 1, product_id: 1, partner_discount: 2.00 },
{ id: 2, product_id: 2, partner_discount: 5.00 }
]);
});

// Internal-only endpoint - only our company domains can access
app.get('/api/internal/sales', cors(internalApiCors), (req, res) => {
res.json({
total_sales: 52643.21,
monthly_growth: 12.7,
top_product_id: 2
});
});

app.listen(5000, () => {
console.log('Server running on port 5000');
});

Debugging CORS Issues

If you're facing CORS issues, here are some common problems and solutions:

  1. Check the browser console: It often provides specific error messages about the CORS problem.

  2. Verify origin configuration: Ensure that the origin in your CORS configuration matches exactly with the requesting site's origin. For example, http://localhost:3000 is different from http://localhost:3000/ (trailing slash).

  3. Check for missing headers: Some requests, particularly those with credentials, require specific headers.

  4. Preflight requests: For complex requests, browsers send a preflight OPTIONS request first. Ensure your server handles OPTIONS requests correctly.

  5. Using browser extensions: For development, you can use browser extensions to disable CORS restrictions temporarily, but never rely on this for production.

Common CORS Errors and Fixes

Error: "No 'Access-Control-Allow-Origin' header"

Fix: Ensure you're using the CORS middleware and have correctly configured the allowed origins.

javascript
app.use(cors({
origin: 'http://your-allowed-origin.com'
}));

Error: "Method not allowed by CORS"

Fix: Specify the allowed methods in your CORS configuration:

javascript
app.use(cors({
origin: 'http://your-allowed-origin.com',
methods: ['GET', 'POST', 'PUT', 'DELETE']
}));

Fix: Enable credentials in your CORS configuration and ensure your frontend request includes credentials:

javascript
// Backend
app.use(cors({
origin: 'http://your-allowed-origin.com',
credentials: true
}));

// Frontend
fetch('http://api.example.com/data', {
credentials: 'include'
})

Summary

CORS is a crucial security feature for web applications that controls how web pages in one domain can request resources from another domain. In Express.js:

  1. Use the cors middleware package to easily implement CORS in your API
  2. Configure allowed origins, methods, and headers based on your security requirements
  3. Apply different CORS settings to different routes when needed
  4. Remember that proper CORS configuration is essential for both development and production environments

Understanding and correctly implementing CORS is essential for building secure, modern web applications where frontend and backend often exist on different domains.

Additional Resources

Exercises

  1. Create an Express API with two endpoints: one accessible from any origin and another only accessible from a specific domain.

  2. Modify an Express API to allow requests only from a list of trusted domains that you specify.

  3. Create an API endpoint that allows different HTTP methods (GET, POST, PUT, DELETE) for different origins.

  4. Implement a dynamic CORS validator that checks against a database of allowed domains instead of a hardcoded list.

  5. Build a simple frontend and backend on different ports and implement CORS to allow them to communicate properly.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)