Skip to content

Express CORS per Route — Fine-Grained Cross-Origin Control for Specific Endpoints

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Express CORS per Route. We cover key concepts, practical examples, and best practices to help you master this topic.

Express allows configuring CORS at the route level using the cors package applied individually, enabling different origins, methods, and headers for different API endpoints within the same application.

What You'll Learn

  • Applying CORS middleware to specific routes
  • Using different CORS configs per endpoint
  • Combining global and per-route CORS settings

Why It Matters

Different endpoints have different security requirements. A public API endpoint might allow any origin, while an admin endpoint needs strict origin control. DodaTech's Express API uses per-route CORS to allow public read endpoints wide access while restricting write operations.

flowchart TD
    A["Request to /api/public"] --> B["CORS: Allow any origin"]
    A2["Request to /api/admin"] --> C["CORS: Strict origin check"]
    A3["Request to /api/webhook"] --> D["CORS: Specific partner origin"]
    B --> E["Handle request"]
    C --> F["Handle request"]
    D --> G["Handle request"]
    style B fill:#86efac,stroke:#16a34a
    style C fill:#fef08a,stroke:#ca8a04
    style D fill:#dbeafe,stroke:#2563eb

Code Examples

// Express per-route CORS configuration
const express = require('express');
const cors = require('cors');

const app = express();

// Public endpoint: any origin
app.get('/api/public/data',
  cors({ origin: '*' }),
  (req, res) => {
    res.json({ message: 'Public data' });
  }
);

// Authenticated endpoint: specific origin with credentials
const authCors = cors({
  origin: 'https://app.example.com',
  credentials: true,
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type', 'Authorization']
});

app.get('/api/user/profile', authCors, (req, res) => {
  res.json({ name: 'Alice', email: 'alice@example.com' });
});

// Admin endpoint: restricted to admin domain
const adminCors = cors({
  origin: ['https://admin.example.com', 'https://dashboard.internal'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Admin-Key']
});

app.delete('/api/admin/users/:id', adminCors, (req, res) => {
  res.json({ deleted: req.params.id });
});
// Using a CORS config map for cleaner code
const corsConfigs = {
  public: { origin: '*' },
  auth: { origin: 'https://app.example.com', credentials: true },
  admin: {
    origin: ['https://admin.example.com'],
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    credentials: true
  },
  webhook: {
    origin: ['https://stripe.com', 'https://api.github.com'],
    methods: ['POST']
  }
};

function routeCors(configName) {
  return cors(corsConfigs[configName]);
}

app.get('/api/public', routeCors('public'), handler);
app.post('/api/auth/login', routeCors('auth'), handler);
app.post('/api/webhooks/stripe', routeCors('webhook'), handler);
# Test per-route CORS
curl -I -H "Origin: https://unknown.com" \
  https://api.example.com/api/public/data

curl -I -H "Origin: https://unknown.com" \
  https://api.example.com/api/user/profile
# Second request should be blocked

Common Mistakes

1. Applying Global CORS with Wrong Defaults

A global app.use(cors()) with permissive defaults may override stricter per-route configs.

2. Not Ordering Route-Specific CORS Before Global CORS

Place specific middleware before the global config to avoid accidental overrides.

3. Forgetting OPTIONS Handling for Non-Standard Methods

Each route must handle OPTIONS. The cors package handles this automatically.

4. Overlapping Route Patterns

A broader route pattern with permissive CORS may match before a specific route with strict CORS.

5. Not Testing Each Route Individually

Test CORS behavior for every route, not just the first one that works.

Practice Questions

  1. How do you apply CORS to a single Express route?
  2. Can different routes have different allowed origins?
  3. What happens if a route has no CORS middleware?
  4. How do you handle OPTIONS for different CORS configs?
  5. Should you use global or per-route CORS?

Answers:

  1. Pass cors(config) as middleware before the route handler.
  2. Yes. Each route can have its own cors() configuration.
  3. The browser blocks the response for cross-origin requests on that route.
  4. The cors package handles OPTIONS automatically when applied to a route.
  5. Use global for common defaults, per-route for endpoints with specific requirements.

Challenge: Refactor an Express API with a mix of public, authenticated, admin, and Webhook endpoints. Implement per-route CORS configuration, add a test suite that verifies each endpoint's CORS behavior, and generate a CORS policy documentation page.

FAQ

Does per-route CORS affect performance?

Negligibly. Each request evaluates only the CORS middleware for its specific route. The overhead is a few microseconds per header check.

Can I use async functions to determine CORS settings per route?

Yes. The cors package supports a function for the origin option, allowing async database lookups or dynamic configuration per route.

How does per-route CORS work with Express error handling middleware?

CORS errors are handled before the route completes. The cors middleware sends the appropriate CORS headers or blocks the request before your error handler runs.

Should I use per-route CORS or a single global config?

Use a single global config for APIs with uniform CORS requirements. Use per-route for APIs with mixed public, authenticated, and admin endpoints.

Can I combine per-route CORS with Express routers?

Yes. Apply CORS middleware at the router level or individual route level within each router. This keeps CORS configuration close to the route definitions.

Mini Project

Build an Express API with three endpoint categories: public (open CORS), authenticated (specific origin with credentials), and admin (multiple specific origins). Implement per-route CORS, add automated tests with a headless browser that verifies each endpoint's CORS behavior, and generate a CORS documentation page.

What's Next

Learn FastAPI CORS middleware configuration for Python async APIs, then explore Django CORS with django-cors-headers.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro