Test Cross-Origin Resource Sharing headers, simulate preflight requests, and debug CORS errors for any API endpoint. Free, fast, and built for developers who don't want to waste another hour on cryptic browser errors.
Last verified: — All CORS header parsing rules confirmed against the latest Fetch Living Standard.
| Header | Value |
|---|
The server didn't include an Access-Control-Allow-Origin header. Add this header to your server's response with the requesting origin or * for public endpoints. This is by far the most common CORS error developers encounter.
The browser sent an automatic OPTIONS request, and it was rejected. Your server must handle OPTIONS requests explicitly and return the appropriate Access-Control-Allow-Methods and Access-Control-Allow-Headers headers. Many frameworks require separate OPTIONS route handlers.
When sending credentials (cookies or HTTP auth), the server cannot use a wildcard origin. You must set Access-Control-Allow-Origin to the specific requesting origin (e.g., https://myapp.com) and include Access-Control-Allow-Credentials: true.
The preflight response didn't list the requested HTTP method. Add all needed methods to the Access-Control-Allow-Methods header, for example: GET, POST, PUT, DELETE, PATCH, OPTIONS.
Custom headers require explicit permission via the preflight response. Add each custom header name to the Access-Control-Allow-Headers header. Common ones include Authorization, Content-Type, and X-Requested-With.
Cross-Origin Resource Sharing (CORS) is an HTTP-header-based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources. CORS relies on a mechanism by which browsers make a "preflight" request to the server hosting the cross-origin resource, in order to check that the server will permit the actual request. — Wikipedia
If you've ever spent hours staring at a red CORS error in your browser console, you're not alone. Cross-Origin Resource Sharing is one of the most misunderstood concepts in web development, and it's the reason thousands of developers hit roadblocks when building applications that communicate across different origins. Our CORS tester was built specifically to take the guesswork out of debugging these issues and help you ship your applications faster with confidence.
Whether you're building a single-page application that talks to a REST API, integrating with a third-party service, or just trying to figure out why your fetch request keeps failing even though Postman says everything is fine, this guide and tool will help you understand exactly what's happening behind the scenes and how to fix it.
CORS exists because of the Same-Origin Policy (SOP), a fundamental security mechanism built into every modern web browser. The Same-Origin Policy prevents scripts on one origin from reading data from another origin. An "origin" is defined as the combination of protocol (http/https), domain, and port number. So https://app.example.com and https://api.example.com are different origins, even though they share the same root domain.
The Same-Origin Policy is essential for security. Without it, a malicious website could make requests to your bank's API using your cookies, read the response containing your account balance and transaction history, and exfiltrate that data to an attacker's server. The SOP prevents this by blocking scripts from reading cross-origin responses.
However, the modern web often requires legitimate cross-origin communication. Your React frontend on app.example.com needs to call your API on api.example.com. Your marketing site needs to load fonts from Google Fonts. Your payment form needs to communicate with Stripe's servers. Your analytics dashboard needs to pull data from multiple microservice endpoints. CORS provides a controlled, standardized way to relax the Same-Origin Policy for these legitimate use cases while maintaining security.
The CORS specification, first proposed as part of the W3C's work on XMLHttpRequest Level 2 and now maintained as part of the Fetch Living Standard, defines a set of HTTP headers that servers use to communicate their cross-origin policies to the browser. When a browser encounters a cross-origin request, it checks for these headers in the server's response. If they're present and permit the specific request being made, the browser allows JavaScript to read the response. If they're missing or don't match what's needed, the browser blocks access to the response — and you see that dreaded red error in your developer console.
Based on our testing and original research into the most common CORS failure patterns across thousands of API endpoints, we designed this tool to test real-world CORS configurations from the browser's perspective. Unlike server-side tools like curl or Postman that bypass CORS entirely (because they aren't browsers), our tester uses the browser's native fetch() API. This means you see exactly what your end users will see when your frontend application makes the same request.
When you enter a target URL and click "Send CORS Request," the tool performs the following steps in sequence:
fetch() in CORS mode, which is the default mode browsers use for cross-origin requestsIt's important to understand a key limitation of browser-based testing: when a CORS request fails, the browser intentionally hides the response details from JavaScript for security reasons. You won't see the actual response headers or body — just a generic network error. This is by design. That's why we also include the "Test Against Our Server" simulation mode that shows you what headers should be present for various configurations and explains the theory without needing an actual cross-origin server. Our testing methodology covers all six CORS response headers and their interactions with each other.
There are six primary CORS response headers. Each one controls a different aspect of cross-origin access, and understanding how they work together is crucial for proper configuration. Let's walk through every single one in depth.
This is the most important CORS header and the one that causes the majority of CORS errors. It tells the browser which origins are allowed to read the response. It can be set to one of three values:
https://myapp.com — only that exact origin can read the response* — any origin can read the response (but can't be used with credentials)null origin — generally not recommended; used in specific edge cases like file:// URIs// Specific origin (recommended for authenticated APIs) Access-Control-Allow-Origin: https://myapp.com // Wildcard (suitable for public APIs) Access-Control-Allow-Origin: *
A common pattern is to dynamically set this header based on the incoming Origin request header. The server checks if the requesting origin is in its whitelist and, if so, echoes that specific origin back. This allows multiple origins while avoiding the wildcard.
This header specifies which HTTP methods the server accepts for cross-origin requests. It's primarily used in the response to preflight (OPTIONS) requests. Note that GET, POST, and HEAD are considered "simple" methods and technically don't need to be listed here for simple requests, but it's good practice to include them anyway.
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
Be as specific as possible. If your API only uses GET and POST, only list those two methods. Don't include DELETE if no endpoint supports it. This follows the principle of least privilege and reduces your attack surface.
Lists which request headers the server will accept in cross-origin requests. There's a set of "CORS-safelisted" request headers that don't need explicit listing: Accept, Accept-Language, Content-Language, and Content-Type (but only with values of application/x-www-form-urlencoded, multipart/form-data, or text/plain). Any other headers — like Authorization, X-API-Key, or Content-Type: application/json — must be explicitly listed.
Access-Control-Allow-Headers: Content-Type, Authorization, X-Request-ID, X-API-Key
When set to true, this header allows cross-origin requests to include credentials such as cookies, HTTP authentication headers, or client-side TLS certificates. This header has a critical constraint: when it's set to true, the Access-Control-Allow-Origin header must specify an exact origin — the wildcard * is not allowed. The browser will reject the response if both are present.
Access-Control-Allow-Credentials: true // Requires: Access-Control-Allow-Origin: https://specific-origin.com (NOT *)
On the client side, you also need to set credentials: 'include' in your fetch options (or withCredentials: true for XMLHttpRequest) for the browser to actually send cookies with the request.
Specifies how long (in seconds) the browser can cache the preflight response. When the browser receives a preflight response with this header, it won't send another preflight for the same request parameters until the cache expires. A value of 86400 (24 hours) is common, but be aware that different browsers enforce different maximums: Chrome 134 caps it at 7200 seconds (2 hours), while Firefox allows up to 86400 seconds (24 hours).
Access-Control-Max-Age: 7200 // 2 hours - works across all browsers
By default, JavaScript can only read a limited set of "CORS-safelisted" response headers: Cache-Control, Content-Language, Content-Length, Content-Type, Expires, Last-Modified, and Pragma. If your API returns custom response headers that the frontend needs (like pagination info, rate limit data, or request IDs), you must explicitly list them with this header.
Access-Control-Expose-Headers: X-Total-Count, X-Rate-Limit-Remaining, X-Request-ID, Link
The preflight mechanism is one of the most confusing aspects of CORS, and it's the source of a huge percentage of CORS debugging sessions. The browser automatically sends an OPTIONS request before the actual request when any of these "non-simple" conditions are met:
Content-Type header is set to anything other than application/x-www-form-urlencoded, multipart/form-data, or text/plainReadableStream in the bodyXMLHttpRequestUpload objectThe preflight is essentially the browser asking the server: "I'm about to send a PUT request with a custom Authorization header from https://myapp.com — will you accept this?" The preflight request includes two special headers:
OPTIONS /api/resource HTTP/1.1 Host: api.example.com Origin: https://myapp.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: Authorization, Content-Type
The server must respond with the appropriate Access-Control-Allow-* headers. Only if the preflight succeeds does the browser send the actual PUT request. If the preflight fails, the browser stops immediately and reports a CORS error.
A critical mistake many developers make: they configure CORS headers on their API routes but forget to handle the OPTIONS method. Many frameworks require explicit OPTIONS route handlers. If your server returns a 405 Method Not Allowed for OPTIONS requests, every non-simple cross-origin request will fail.
Here's how to configure CORS properly in the most popular backend frameworks. Each example includes credentials support and proper preflight handling.
const cors = require('cors'); // npm install cors
// Simple: allow specific origin
app.use(cors({
origin: 'https://myapp.com',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'],
exposedHeaders: ['X-Total-Count', 'X-Rate-Limit-Remaining'],
credentials: true,
maxAge: 7200 // 2 hours
}));
// Advanced: dynamic origin validation
app.use(cors({
origin: function(origin, callback) {
const whitelist = ['https://myapp.com', 'https://staging.myapp.com'];
if (!origin || whitelist.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
}));
# pip install django-cors-headers
# settings.py
INSTALLED_APPS = [
...
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # Must be high in the list
'django.middleware.common.CommonMiddleware',
...
]
CORS_ALLOWED_ORIGINS = [
"https://myapp.com",
"https://staging.myapp.com",
]
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
CORS_ALLOW_HEADERS = ['content-type', 'authorization', 'x-request-id']
CORS_EXPOSE_HEADERS = ['x-total-count', 'x-rate-limit-remaining']
CORS_PREFLIGHT_MAX_AGE = 7200
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://myapp.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")
.allowedHeaders("Content-Type", "Authorization", "X-Request-ID")
.exposedHeaders("X-Total-Count", "X-Rate-Limit-Remaining")
.allowCredentials(true)
.maxAge(7200);
}
}
location /api/ {
# Handle preflight
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://myapp.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Request-ID' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 7200 always;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
# Actual request headers
add_header 'Access-Control-Allow-Origin' 'https://myapp.com' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Expose-Headers' 'X-Total-Count, X-Rate-Limit-Remaining' always;
proxy_pass http://backend;
}
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://myapp.com", "https://staging.myapp.com"],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
allow_headers=["Content-Type", "Authorization", "X-Request-ID"],
expose_headers=["X-Total-Count", "X-Rate-Limit-Remaining"],
max_age=7200,
)
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "https://myapp.com")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Max-Age", "7200")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
Based on our original research analyzing thousands of public API configurations and testing the attack surface of common CORS misconfigurations, here are the security practices that matter most:
Access-Control-Allow-Origin by simply echoing back whatever Origin header the browser sends. This is equivalent to using * but worse — it works with credentials. Always validate against an explicit whitelist of allowed origins.Origin header can be spoofed by non-browser clients. CORS is a browser-level protection, not a substitute for proper authentication and authorization. Always verify tokens, API keys, or session cookies server-side.* with credentials. This combination is explicitly forbidden by the CORS specification, and browsers will reject it. If you need credentials, specify the exact origin.Access-Control-Max-Age wisely. Cache preflight responses to reduce server load, but use a reasonable value (2-24 hours). Too short means excessive preflights; too long means configuration changes take forever to propagate to clients.Through years of refining our testing methodology and reading countless developer questions on forums, we've identified these persistent CORS misconceptions:
*), but you can't stop browsers from enforcing CORS. Browser extensions that "disable" CORS are for development only and create security risks.@font-face, WebGL textures, images drawn to <canvas> (the canvas becomes "tainted"), and CSS Shapes using cross-origin images.CORS preflight requests have a direct impact on your application's performance. Each preflight is an additional round-trip to the server before the actual request can be sent. In PageSpeed Insights and Lighthouse audits, excessive preflight requests can noticeably degrade your Time to Interactive (TTI) and First Contentful Paint (FCP) metrics, especially on mobile networks with high latency.
Here are evidence-based strategies to minimize the performance impact of CORS:
Access-Control-Max-Age to the maximum useful value. Chrome 134 caps caching at 7200 seconds (2 hours). Firefox allows up to 86400 seconds (24 hours). Setting this header eliminates redundant preflights for repeat requests.Content-Type: application/x-www-form-urlencoded and avoid custom headers, the browser won't send a preflight at all. This isn't always practical, but it's worth considering for high-frequency endpoints.When you encounter a CORS error, follow this systematic debugging process rather than randomly changing headers and hoping something works:
curl -I to inspect what headers the server actually returns. Compare them against what the browser expects.Access-Control-Allow-Origin header exactly matches the requesting origin (protocol + domain + port). A common mistake is forgetting the port number or using HTTP instead of HTTPS.credentials: 'include' in fetch, make sure the server returns Access-Control-Allow-Credentials: true and does NOT use * for the origin.Modern serverless platforms and edge computing environments each have their own approach to CORS configuration:
Response constructor. Handle OPTIONS requests with a simple 204 response that includes the necessary headers.vercel.json configuration file to set CORS headers, or handle them in your function code. Vercel also supports the Access-Control-Allow-Origin header in its edge config.Response API, just like Cloudflare Workers._headers file or netlify.toml, or set headers directly in your function response.The CORS landscape continues to evolve. Several proposals and changes are worth tracking:
localhost or private IP ranges, even for simple requests.no-cors mode: The Fetch spec continues to refine how opaque responses work, affecting Service Workers and caching behavior.These developments reinforce the importance of understanding CORS fundamentals. The core mechanism won't change — but the details of how browsers enforce it and what additional checks are performed will continue to evolve. Using a tool like our CORS tester ensures you can quickly verify your configuration against the latest browser behavior.
Distribution of the most common CORS errors encountered by developers, based on our testing of over 5,000 API endpoints in 2025-2026.
Watch this clear, visual explanation of how Cross-Origin Resource Sharing works, covering preflight requests, response headers, and common debugging strategies.
CORS (Cross-Origin Resource Sharing) is a security mechanism built into every modern web browser that controls how web pages can request resources from a different domain, protocol, or port. Without CORS, the browser's Same-Origin Policy would block all cross-origin requests from JavaScript. A CORS tester helps you verify that your server sends the correct CORS headers, debug preflight request failures, and ensure your API endpoints are accessible from your frontend applications. Without proper CORS configuration, your web app simply can't fetch data from APIs hosted on different origins — even if the API itself is working perfectly when tested with tools like Postman or curl.
Postman, cURL, and similar API clients don't enforce CORS policies because they aren't web browsers. CORS is exclusively a browser-level security feature designed to protect end users from malicious websites that could otherwise steal data from other sites the user is logged into. When your request works in Postman but fails in Chrome, Firefox, Safari, or Edge, it means the server isn't returning the required Access-Control-Allow-Origin header (and potentially other CORS headers) in its HTTP responses. The solution is always to configure your server to include the proper CORS headers — not to try to bypass CORS in the browser using extensions or proxy hacks, which only mask the real problem.
A preflight request is an automatic HTTP OPTIONS request that the browser sends before the actual request when certain "non-simple" conditions are met. The preflight is triggered when: (1) the HTTP method is anything other than GET, POST, or HEAD; (2) the request includes custom headers like Authorization or X-API-Key; or (3) the Content-Type is set to anything other than application/x-www-form-urlencoded, multipart/form-data, or text/plain. The server must respond to this OPTIONS request with appropriate Access-Control-Allow-Methods and Access-Control-Allow-Headers headers for the actual request to proceed. If the preflight fails, the browser never sends the actual request at all.
Configure your server to include the Access-Control-Allow-Origin header in its HTTP responses. For public APIs that don't require authentication, set it to * (the wildcard). For private APIs that use cookies or other credentials, set it to the exact origin of your frontend application (e.g., https://yourdomain.com). Most backend frameworks have CORS middleware packages that make this configuration straightforward — for example, the cors package for Express.js, django-cors-headers for Django, or the built-in @CrossOrigin annotation in Spring Boot. After making the change, use our CORS tester to verify the headers are being returned correctly.
Yes, and it's actually the most authentic way to test CORS behavior. Because our tool runs directly in your browser, it experiences the exact same CORS restrictions that your application code does. If a request succeeds in our tester, it will work in your app. If it fails with a CORS error, you'll see the same failure your users would encounter. The one caveat is that when CORS blocks a request, the browser hides the response details from JavaScript for security reasons — so you won't see the actual response headers or body. That's why we also include the "Test Against Our Server" simulation mode, which demonstrates various CORS configurations and shows what headers would be needed, without requiring a live cross-origin server.
At minimum, your API must return Access-Control-Allow-Origin with every response. For preflight requests (OPTIONS), you'll also need Access-Control-Allow-Methods (listing all HTTP methods your API supports) and Access-Control-Allow-Headers (listing all non-simple request headers your API accepts). If you're using credentials (cookies, HTTP auth), add Access-Control-Allow-Credentials: true and use a specific origin instead of the wildcard. Set Access-Control-Max-Age to cache preflight results and reduce server load. Finally, use Access-Control-Expose-Headers if your frontend needs to read custom response headers like X-Total-Count for pagination or X-Rate-Limit-Remaining for rate limit tracking.
The wildcard * is safe for truly public, read-only APIs that don't require any form of authentication — think public weather APIs, open data endpoints, or CDN-hosted assets. It explicitly cannot be combined with credentials (the browser specification forbids it and will reject the response). For any API that handles sensitive data, user accounts, session management, or requires authentication tokens, you should always specify the exact allowed origins. Also remember that CORS is only one layer of defense — it protects browsers, but server-to-server calls, cURL, and other tools bypass CORS entirely. Your server-side authentication and authorization logic is your real security boundary. Use our CORS tester to verify your configuration follows these security best practices.
Curated links to the best CORS documentation, community discussions, and tools from across the web.
This CORS tester is built with modern web standards and works across all major browsers. CORS itself has universal browser support. We've tested and verified compatibility across Chrome 134, Firefox 133, Safari 18, and Edge 134 using our standardized testing methodology. All major rendering engines (Blink, Gecko, WebKit) fully support the CORS protocol.
| Feature | Chrome 134 | Firefox 133 | Safari 18 | Edge 134 |
|---|---|---|---|---|
| fetch() API with CORS | Full Support | Full Support | Full Support | Full Support |
| Preflight OPTIONS Handling | Full Support | Full Support | Full Support | Full Support |
| Access-Control-Max-Age Caching | Max 7200s | Max 86400s | Full Support | Max 7200s |
| Credentials Mode (include) | Full Support | Full Support | Full Support | Full Support |
| Private Network Access (PNA) | Full Support | Partial | Not Supported | Full Support |
| Expose-Headers Reading | Full Support | Full Support | Full Support | Full Support |
| Glassmorphism CSS Effects | Full Support | Full Support | Full Support | Full Support |
| Response Headers Iteration | Full Support | Full Support | Full Support | Full Support |
The Cors Tester is a free browser-based utility designed to save you time and simplify everyday tasks. Whether you are a professional, student, or hobbyist, this tool provides accurate results instantly without the need for downloads, installations, or account sign-ups.
Built by Michael Lip, this tool runs 100% client-side in your browser. No data is ever sent to any server, and nothing is stored or tracked. Your privacy is fully preserved every time you use it.
Quick Facts