DEV Community

Dev Nestio
Dev Nestio

Posted on • Originally published at devnestio.pages.dev

CORS Headers Generator: Stop Guessing, Start Copying

CORS errors are some of the most frustrating in web development. The configuration has many moving parts, and a single mistake — like using * with credentials — silently breaks your app.

I built a free tool that lets you configure CORS visually and get copy-ready code for Express.js, nginx, Apache, and Cloudflare Workers.

Tool

CORS Headers Generator
https://devnestio.pages.dev/cors-generator/

Configure origins, methods, allowed headers, exposed headers, credentials, and preflight max-age — then pick your framework.

How CORS Works

Browsers block cross-origin requests (different domain, port, or protocol) unless the server explicitly allows them via CORS headers.

# The browser sends a preflight OPTIONS request:
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

# The server responds with:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Enter fullscreen mode Exit fullscreen mode

The Most Common Mistakes

Wildcard + Credentials = broken

# This does NOT work:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true   <- ignored by browsers!
Enter fullscreen mode Exit fullscreen mode

When using credentials (cookies, Authorization headers), you must specify the exact origin:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Vary: Origin
Enter fullscreen mode Exit fullscreen mode

Multiple origins need dynamic responses

The Access-Control-Allow-Origin header can only contain one origin. For multiple allowed origins, check the request Origin header on the server and reflect it back:

const ALLOWED_ORIGINS = ['https://app.example.com', 'https://admin.example.com'];

const corsOptions = {
  origin: (origin, callback) => {
    if (!origin || ALLOWED_ORIGINS.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('CORS not allowed'));
    }
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  maxAge: 86400,
};

app.use(cors(corsOptions));
app.options('*', cors(corsOptions)); // Handle preflight
Enter fullscreen mode Exit fullscreen mode

Always add Vary: Origin when the response differs by origin — this prevents CDNs from caching the wrong origin's response.

nginx Configuration

# Single origin
add_header Access-Control-Allow-Origin "https://app.example.com" always;
add_header Access-Control-Allow-Credentials "true" 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-Max-Age "86400" always;

# Handle OPTIONS preflight
if ($request_method = OPTIONS) {
  add_header Content-Length 0;
  add_header Content-Type text/plain;
  return 204;
}
Enter fullscreen mode Exit fullscreen mode

Cloudflare Workers

export default {
  async fetch(request, env) {
    const origin = request.headers.get('Origin');
    const ALLOWED = ['https://app.example.com', 'https://admin.example.com'];
    const allow = ALLOWED.includes(origin) ? origin : null;

    const corsHeaders = {
      'Access-Control-Allow-Origin': allow || '',
      'Vary': 'Origin',
      'Access-Control-Allow-Credentials': 'true',
      'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
      'Access-Control-Max-Age': '86400',
    };

    if (request.method === 'OPTIONS') {
      return new Response(null, { status: 204, headers: corsHeaders });
    }

    const response = await fetch(request);
    const newHeaders = new Headers(response.headers);
    Object.entries(corsHeaders).forEach(([k, v]) => newHeaders.set(k, v));
    return new Response(response.body, { ...response, headers: newHeaders });
  }
};
Enter fullscreen mode Exit fullscreen mode

Generate your CORS config in seconds — no more copy-pasting from StackOverflow.

https://devnestio.pages.dev/cors-generator/

Top comments (0)