Security

CORS Policy Generator

CORS configurations that aren't too permissive or too restrictive. Security without breaking legitimate requests. Because '*' in production is asking for trouble.

Instant🔒In your browserNo signup
Live
    View as text

    What CORS is and why it exists

    CORS (Cross-Origin Resource Sharing) is the browser security policy that blocks JavaScript requests between different domains. It exists to prevent evil.com from making requests to yourbank.com using your active cookies. Without CORS, any site could read your Gmail session or make bank transfers in your name.

    The browser sends a preflight request (OPTIONS) before the real request if you use non-simple methods (PUT, DELETE) or custom headers. The server responds with Access-Control-* headers indicating what's allowed. If the server doesn't respond with correct headers, the browser blocks the response even though the server processed it. It's client-side policy, not server-side.

    CORS doesn't protect your API from direct requests (curl, Postman, backend-to-backend). It only applies in browser contexts. If your API is public, CORS doesn't add real security—you need authentication, rate limiting, validation. CORS is to prevent compromised browser attacks, not to protect sensitive endpoints. That's the responsibility of tokens, OAuth, API keys.

    Common mistakes that break CORS

    Access-Control-Allow-Origin: * with credentials: true doesn't work. The browser rejects the combination. If you need credentials (cookies, Authorization header), you must specify exact origin: https://app.example.com. You can't use wildcard. This forces you to implement dynamic logic: check the Origin header from the request against a whitelist and respond with that specific origin.

    Forgetting OPTIONS: the preflight OPTIONS must return 200 with correct CORS headers, without authentication. Many backends require auth on all routes, including OPTIONS. This breaks preflight: the browser never sends credentials in OPTIONS, so preflight fails with 401 before attempting the real request. Exclude OPTIONS from auth middleware.

    Incomplete exposed headers: if your API returns custom headers (X-Total-Count, X-RateLimit-Remaining), the frontend can't read them without Access-Control-Expose-Headers. By default it only exposes simple headers (Content-Type, Content-Length). If your frontend does response.headers.get('X-Total-Count') and it's null, you forgot to expose it. List all custom headers your frontend needs to read.

    Configurations by environment

    Development: Access-Control-Allow-Origin: * is fine locally. Facilitates testing with different ports (frontend on 3000, backend on 8000). But never commit this config to production. Use environment variables: ALLOWED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000 in dev, real origins in prod.

    Staging: whitelist of staging and preview domains. If you use Vercel/Netlify with preview URLs, you need to allow https://*.vercel.app or similar. This is more permissive than production but controlled. Consider regex for subdomain matching: /^https:\/\/.*\.example\.com$/. Test that it works before merging to main.

    Production: exact list of allowed origins. https://app.example.com, https://www.example.com. No wildcards if you can avoid it. If you have dynamic subdomains (tenant-based SaaS), validate against strict pattern: /^https:\/\/[a-z0-9-]+\.example\.com$/. Log rejected origins to detect DNS typos or attack attempts. Long MaxAge (7200+) reduces preflight overhead in production.

    Implementation in different frameworks

    Express.js: use cors package. app.use(cors({ origin: process.env.ALLOWED_ORIGINS.split(','), credentials: true })). Configure before routes. For fine control, pass function: origin: (origin, callback) => { if (whitelist.includes(origin)) callback(null, true); else callback(new Error('Not allowed')); }. Handle OPTIONS explicitly if you have custom middleware.

    Next.js: in API routes, set headers manually: res.setHeader('Access-Control-Allow-Origin', allowedOrigin); res.setHeader('Access-Control-Allow-Methods', 'GET,POST'); if (req.method === 'OPTIONS') { res.status(200).end(); return; }. Or use middleware in next.config.js with global headers. Edge runtime has limitations; test thoroughly.

    Django: django-cors-headers package. In settings: CORS_ALLOWED_ORIGINS = ['https://app.example.com'], CORS_ALLOW_CREDENTIALS = True. Add corsheaders.middleware.CorsMiddleware at the beginning of MIDDLEWARE. For regex: CORS_ALLOWED_ORIGIN_REGEXES = [r'^https://\w+\.example\.com$']. Careful with middleware order: CORS must process before auth.

    FAQ

    Why do I see the request in Network but CORS blocks it?

    The server processed the request and responded, but the browser blocked the response because correct CORS headers were missing. It's client-side protection.

    Can I use Access-Control-Allow-Origin: * in production?

    Only if your API is completely public, doesn't use credentials, and doesn't return sensitive data. Otherwise, specify exact origins.

    What is a 'simple request' that doesn't require preflight?

    GET/POST/HEAD with basic headers (Content-Type: text/plain, application/x-www-form-urlencoded, multipart/form-data). Anything else triggers preflight.

    Does CORS protect my API from bots and scrapers?

    No. CORS only applies in browsers. Bots, curl, Postman completely ignore CORS. You need authentication and rate limiting.

    Was this generator useful?