PureDevTools

CORS Header Generator

Configure Cross-Origin Resource Sharing headers and export to HTTP, nginx, Apache, or Express.js

All processing happens in your browser. No data is sent to any server.

Allowed Origins

https://example.com

Allowed Methods

Content-TypeAuthorization

Headers in this list are readable by browser JavaScript (beyond the default safe headers).

How long browsers cache preflight responses. 0 = omit header. Max recommended: 86400 (24 h).

Allow Credentials

Allows cookies, HTTP authentication, and TLS client certificates. Cannot be used with wildcard origins.

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Access-Control-Allow-Originhttps://example.com
Access-Control-Allow-MethodsGET, POST, OPTIONS
Access-Control-Allow-HeadersContent-Type, Authorization
Access-Control-Max-Age86400

It’s 11 PM and your frontend is silently failing. The console says “blocked by CORS policy” but the API works fine in Postman. You know you need Access-Control-Allow-Origin somewhere, but the header has six variants, the nginx syntax is different from Express, and you just spent 20 minutes Googling whether * works with credentials (it doesn’t).

Why This Generator (Not Stack Overflow Snippets)

The most-copied CORS snippet on Stack Overflow is Access-Control-Allow-Origin: * — which works for public APIs but breaks the moment you need cookies or auth tokens. The second most-copied answer is a nginx block that’s missing always, so it only sets headers on 200 responses and silently drops them on 4xx/5xx errors. Both have been copied millions of times.

This tool generates correct, complete CORS configurations for four server environments — raw HTTP headers, nginx, Apache .htaccess, and Express.js — with the correct OPTIONS preflight handling included. It also warns you about invalid combinations (like * with credentials) before you deploy them. Everything is generated in your browser; your origin URLs and API configuration stay on your device.

What Is CORS?

Cross-Origin Resource Sharing (CORS) is a browser security mechanism that controls which web pages can request resources from a different origin (domain, protocol, or port). Without CORS headers, the browser’s Same-Origin Policy blocks cross-origin responses — your JavaScript fetch or XMLHttpRequest silently fails even though the server received and processed the request.

When your frontend at https://app.example.com calls https://api.example.com, the browser sends an Origin header with every request. The API server must respond with matching CORS headers; otherwise the browser discards the response before your code sees it.

Request:  GET /data HTTP/1.1
          Origin: https://app.example.com

Response: HTTP/1.1 200 OK
          Access-Control-Allow-Origin: https://app.example.com

The Six CORS Response Headers

Access-Control-Allow-Origin

The most important CORS header — it tells the browser which origin is allowed to read the response.

Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: https://app.example.com
ValueMeaning
*Any origin allowed (no credentials)
https://app.example.comOnly this exact origin
(dynamic echo)Server echoes the request Origin when it matches an allow-list

Only one origin value is permitted per response. To allow multiple specific origins, the server must dynamically check the Origin request header and echo back the matching value.

Access-Control-Allow-Methods

Lists the HTTP methods permitted for cross-origin requests.

Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS

Used primarily in preflight responses. Simple requests (GET, HEAD, POST with certain content types) skip the preflight; all other methods trigger one.

Access-Control-Allow-Headers

Specifies which non-standard request headers the server accepts.

Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With

Headers like Content-Type and Authorization are common. The browser checks this header during preflight before sending the real request.

Access-Control-Expose-Headers

By default, browser JavaScript can only access a small set of safe response headers (Cache-Control, Content-Language, Content-Length, Content-Type, Expires, Last-Modified, Pragma). Custom headers require explicit exposure:

Access-Control-Expose-Headers: X-Total-Count, X-RateLimit-Remaining

This is essential for APIs that communicate pagination or rate-limit information in headers.

Access-Control-Max-Age

Sets how long (in seconds) browsers can cache the preflight result, avoiding repeated OPTIONS requests.

Access-Control-Max-Age: 86400
BrowserCap
Chrome7200 s (2 h)
Firefox86400 s (24 h)
Safari600 s (10 min)

Omitting this header means the browser re-issues a preflight with every request.

Access-Control-Allow-Credentials

Allows cookies, HTTP authentication, and TLS client certificates to be sent with cross-origin requests.

Access-Control-Allow-Credentials: true

Critical constraint: This header cannot be used with Access-Control-Allow-Origin: *. You must specify a concrete origin. The browser also requires the fetch call to include credentials: 'include' (or withCredentials: true for XHR).

How Preflight Requests Work

For non-simple requests, the browser sends an automatic preflight (OPTIONS) request before the actual request:

OPTIONS /api/users HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization

The server must respond with matching CORS headers and a 2xx status:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

If the preflight fails (missing headers, wrong values, or non-2xx status), the browser blocks the actual request.

Simple requests (no preflight needed) must satisfy all of:

Server-Side Implementation Examples

nginx

# In your server{} or location{} block
add_header 'Access-Control-Allow-Origin' 'https://app.example.com' 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;

if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' 'https://app.example.com';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
    add_header 'Access-Control-Max-Age' 86400;
    add_header 'Content-Type' 'text/plain; charset=utf-8';
    add_header 'Content-Length' 0;
    return 204;
}

Apache (.htaccess)

# Requires: a2enmod headers
<IfModule mod_headers.c>
    Header always set Access-Control-Allow-Origin "https://app.example.com"
    Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
    Header always set Access-Control-Allow-Headers "Content-Type, Authorization"
    Header always set Access-Control-Max-Age "86400"
</IfModule>

Express.js

// npm install cors
const cors = require('cors');

app.use(cors({
  origin: 'https://app.example.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  maxAge: 86400,
}));

Cloudflare Workers / Edge Functions

const corsHeaders = {
  'Access-Control-Allow-Origin': 'https://app.example.com',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
  'Access-Control-Max-Age': '86400',
};

addEventListener('fetch', event => {
  const request = event.request;
  if (request.method === 'OPTIONS') {
    return event.respondWith(
      new Response(null, { status: 204, headers: corsHeaders })
    );
  }
  // ... handle normal request
});

Common CORS Mistakes

1. Setting the header but forgetting preflight

Adding Access-Control-Allow-Origin to GET responses but not handling OPTIONS preflight:

# Wrong — only handles GET, not OPTIONS preflight
location /api/ {
    add_header 'Access-Control-Allow-Origin' '*';
    proxy_pass http://backend;
}

# Right — handle OPTIONS separately
location /api/ {
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
        return 204;
    }
    add_header 'Access-Control-Allow-Origin' '*' always;
    proxy_pass http://backend;
}

2. Wildcard + credentials

# This combination is rejected by all browsers
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

Solution: specify the exact origin and use dynamic origin matching if multiple origins are needed.

3. Missing always in nginx

Without always, nginx only adds headers to 2xx responses, not 4xx or 5xx:

# Wrong — headers missing on errors
add_header 'Access-Control-Allow-Origin' '*';

# Right — always present
add_header 'Access-Control-Allow-Origin' '*' always;

4. Not including Vary: Origin

When echoing the request origin dynamically, cache layers (CDN, reverse proxy) may serve a cached response with one origin’s CORS header to a request from a different origin. Add Vary: Origin to prevent this:

Vary: Origin
Access-Control-Allow-Origin: https://app.example.com

5. Multiple Access-Control-Allow-Origin headers

Sending two Access-Control-Allow-Origin headers in the same response causes browsers to reject the response. This commonly happens when both the reverse proxy and the application framework set the header.

Security Considerations

Frequently Asked Questions

Is CORS enforced by the browser or the server? CORS is a browser-side policy. The server sends CORS headers, and the browser decides whether to expose the response to JavaScript. Non-browser clients (curl, Postman, server-to-server) ignore CORS headers entirely.

Why can’t I see the error details when CORS fails? For security reasons, browsers report CORS failures with a generic error message (“has been blocked by CORS policy”) and hide the actual response body and headers. Open DevTools → Network → the failed request → Headers tab to see what the server actually sent.

Does setting Access-Control-Allow-Origin: * open a security hole? It depends. A wildcard origin means any website can read your API responses. For truly public data (weather APIs, open datasets) this is fine. For APIs that return user-specific data, require authentication — a wildcard origin combined with non-credentialed requests is still safe because the attacker’s page cannot send the user’s session cookies without credentials: 'include' and a matching specific origin.

What is the Vary: Origin header and when do I need it? When your server dynamically selects the CORS origin based on the request, add Vary: Origin to tell caches (CDN, browser) to cache the response separately per origin. Without it, a CDN may serve a cached response with origin A’s CORS header to origin B.

Does this tool send my configuration to a server? No. All processing happens entirely in your browser. Your origin URLs and configuration never leave your device.

Related Tools

More DevOps & Networking Tools