CORS Tester
See exactly why a browser blocks a cross-origin request, and the headers that fix it.
Request
Sending the requests from MySmartCoPilot’s server…
What the browser sends first
Result
Enter the URL your code calls and the origin of the page that calls it. The test then shows:
- whether the browser sends a preflight, and the exact request;
- each check the browser runs on the answers, and the first one that fails;
- the error message Chrome prints in the console;
- which response headers your code can read, and caching problems;
- the headers your server should send.
What Chrome’s console says
Headers that make this request work
Raw requests and responses
Status lines and response headers as MySmartCoPilot’s server received them, with cookie values hidden. The Server header and a few headers added on the way (a request ID, a cache status, connection details) are left out: they would not be your server’s.
About the CORS Tester
A browser lets a page read another site’s response only when the server says so with CORS headers. When it does not, your code gets a bare TypeError (“Failed to fetch” in Chrome) and the console a long red message. This tester runs the same checks the browser runs, in the same order, and tells you which one fails and what to change on the server.
Enter the URL your code calls, the origin of the calling page, the method and the headers your code sets. MySmartCoPilot’s server then sends the preflight (OPTIONS with Access-Control-Request-Method and Access-Control-Request-Headers) when a browser would send one, and the request itself with your Origin, follows redirects the way browsers do, and explains every Access-Control-* header in the answers: allowed origin, credentials, methods, headers, Max-Age, Expose-Headers and Vary: Origin. For APIs on localhost or behind a login, paste the response headers from your developer tools instead; they are checked in your browser.
How to use it
- Choose Live URL for a public API, or Paste headers for an API on localhost, a private network or behind a login.
- Enter the URL your code calls and the origin of the calling page (for example
https://app.example.comorhttp://localhost:3000), and pick the method. Or paste the CORS error from the browser’s console into the URL field: the URL, the origin and, when the message names them, the method and header are filled in for you. - List the headers your code sets, one per line (
Content-Type: application/json,Authorization: Bearer <token>), and tick credentials if the call sends cookies (credentials: 'include'). Before you send anything, the panel shows whether a preflight comes first and its exact request. - Press Test CORS (or Check headers): read the verdict, the check that fails, and the message Chrome prints in the console.
- Copy the headers that make this request work into your server or framework configuration, and test again. Copy report or Report (.txt) keeps the whole analysis.
Examples
GET https://api.example.com/me from https://app.example.com, credentials: include Response: Access-Control-Allow-Origin: *
Blocked: the wildcard * is not allowed with credentials. Fix: Access-Control-Allow-Origin: https://app.example.com, Access-Control-Allow-Credentials: true, Vary: Origin
PUT with Content-Type: application/json Preflight answer: Access-Control-Allow-Methods: GET, POST and Access-Control-Allow-Headers: Content-Type
Blocked at the preflight: "Method PUT is not allowed by Access-Control-Allow-Methods in preflight response."
GET, HEAD and POST always pass; every other method must be listed, in the same letter case your code sends.
DELETE https://api.example.com/v1/items/7 — the server answers OPTIONS with 301 to …/7/
Blocked at the preflight: "Redirect is not allowed for a preflight request." Call the final URL directly.
Origin: https://app.example.com Access-Control-Allow-Origin: https://app.example.com/
Blocked: the header names another origin — an origin never ends with a slash, and the comparison is exact.
Common uses
- Finding out why fetch() from a single-page app fails with "has been blocked by CORS policy" before touching any code.
- Checking a new API gateway, CDN or reverse-proxy rule before releasing it, including redirects and error responses.
- Making sure a credentialed API (cookies or HTTP authentication) does not reflect every origin or trust "null".
- Debugging an API on localhost by pasting the preflight and response headers from the browser’s network panel.
- Getting the exact header lines to paste into server, framework or CDN configuration.
How a browser decides
The rules come from the Fetch Standard, which every current browser implements:
- Simple or preflighted? GET, HEAD and POST with only CORS-safelisted headers (
Accept,Accept-Language,Content-Language,Content-Typewithapplication/x-www-form-urlencoded,multipart/form-dataortext/plain, and a simpleRange) are sent straight away. Anything else — another method,Content-Type: application/json,Authorization, any custom header — first gets a preflight:OPTIONSlisting the method and the header names, sorted and in lower case. - The preflight passes only with a 2xx status, no redirect, an
Access-Control-Allow-Originthat allows the origin, the method inAccess-Control-Allow-Methods(GET, HEAD and POST always pass), and every header inAccess-Control-Allow-Headers. Methods are compared case-sensitively, and*is a wildcard only without credentials. The standard says*never coversAuthorization; current browsers still let it (Chrome with a warning), so listAuthorizationby name. - Every response, redirects included, must pass the CORS check:
Access-Control-Allow-Originis*(without credentials) or exactly the request’s origin, and with credentialsAccess-Control-Allow-Credentialsis exactlytrue. After a redirect to another origin the browser sendsOrigin: null. - What your code can read: the status, the body and seven safelisted headers (
Cache-Control,Content-Language,Content-Length,Content-Type,Expires,Last-Modified,Pragma), plus whatAccess-Control-Expose-Headerslists.Set-Cookienever.
Credentials, caching and Vary: Origin
With credentials: 'include' (or withCredentials in XMLHttpRequest) the server must name the exact origin and send Access-Control-Allow-Credentials: true; * fails in every header (Fetch Standard: CORS protocol and credentials). The preflight itself never carries cookies, so authentication must let OPTIONS through.
Browsers remember a successful preflight for Access-Control-Max-Age seconds, 5 when it is missing, per origin, URL and credentials mode, and cap it: Chrome at 7,200 seconds (Chromium source), Firefox at 86,400 (MDN) and Safari at 600 (WebKit source). Send digits only: Chrome and Safari fall back to 5 seconds for a value such as 10m, while Firefox then caches nothing at all (Firefox source), and a negative value turns the cache off everywhere.
When Access-Control-Allow-Origin depends on the request, send Vary: Origin, or a browser or CDN cache may serve one site’s answer to another (Fetch Standard: CORS protocol and HTTP caches). A fixed * needs no Vary.
What the live test sends
- The preflight, only when a browser would send one:
OPTIONSwith yourOrigin,Access-Control-Request-MethodandAccess-Control-Request-Headers, without following redirects. - The request itself as GET (HEAD when you chose HEAD), with your
Origin, following up to 4 redirects and switching toOrigin: nullafter a redirect to another origin, as browsers do. PUT, PATCH, DELETE and POST are never sent, and no request body is sent: they could change data on your server. - Unless you untick it, the same GET once with
Origin: https://unrelated-origin.example(a reserved domain nobody owns) and once withOrigin: null, to see whether the server trusts any site.
Requests come from MySmartCoPilot’s server (a cloud data centre, usually near you) with the user agent MySmartCoPilotBot/1.0 and no cookies. Only the status lines and response headers come back; cookie values in Set-Cookie are hidden.
The console messages, decoded
- No 'Access-Control-Allow-Origin' header is present — the response (or the preflight, when the message says "Response to preflight request…") has no such header. Often an error page from authentication, a proxy or a crashed handler.
- …contains multiple values — the header arrives twice (application and proxy both add it) or holds a list. Send exactly one origin.
- …is not equal to the supplied origin — a different origin, a trailing slash, another scheme, port or letter case.
- **…must not be the wildcard '' when the request's credentials mode is 'include'* — use the exact origin with
Access-Control-Allow-Credentials: true. - It does not have HTTP ok status — the preflight got 401, 404, 405 or 500: let
OPTIONSthrough before authentication and routing. - Method … / Request header field … is not allowed — add it to
Access-Control-Allow-Methods/Access-Control-Allow-Headers.
These are Chrome’s wordings (Chromium source); Firefox and Safari describe the same failures in their own words (MDN: CORS errors).
Limitations
- The live test checks a GET (or HEAD) in place of PUT, PATCH, DELETE or POST. Servers that send different CORS headers per method can answer your real request differently; check it in the network panel.
- Requests carry no cookies and come from a data centre with MySmartCoPilotBot’s user agent, so a server that changes its answer for signed-in users, browsers or regions (or blocks bots) may answer you differently.
- Only public http:// and https:// URLs on the standard ports can be tested live; for localhost, private networks and other ports, paste the headers.
- Browser features around CORS are not simulated: the HTTP cache, service workers, extensions, Chrome’s prompt for requests to local networks and mixed-content blocking.
- When a preflighted request is redirected, the browser sends a second preflight to the new URL; the live test does not send that one, so it reports the result as open and suggests testing the final URL directly.
- The raw view leaves out the Server header and a few headers added on the way to MySmartCoPilot’s server (a request ID, a cache status, connection details), because they would not show your server’s values. None of them affects CORS.
- Console wording follows current Chrome and may change in later versions.
Privacy
In “Live URL” mode the URL, the Origin, the method and the names of the headers that need permission go to MySmartCoPilot’s server, which sends the requests and returns only status lines and response headers; nothing is stored, and the log keeps only the host name, status and time. Header values, cookies and everything pasted in “Paste headers” mode stay in your browser.
Frequently asked questions
Why does my request work in curl or Postman but not in the browser?
CORS is enforced by browsers only. curl and Postman ignore it; the browser sends the request too, but hides the answer from your page unless the CORS headers allow it — and for preflighted requests it does not send the request at all when the preflight fails.
Do I need a preflight for JSON?
Yes, if you set Content-Type: application/json: only application/x-www-form-urlencoded, multipart/form-data and text/plain are safelisted. The server must then answer OPTIONS and list Content-Type in Access-Control-Allow-Headers.
Is Access-Control-Allow-Origin: * safe?
For public data that any site may read, yes: * never works with cookies or HTTP authentication. Reflecting every request’s Origin together with Access-Control-Allow-Credentials: true is the dangerous setup, because then any website can read your signed-in visitors’ data — the trust check flags it.
How do I allow several origins?
The header takes one origin (or *), not a list. Check the request’s Origin against your allowed list on the server, send back the one that matched, and add Vary: Origin.
Why is my Origin "null"?
Browsers send Origin: null from sandboxed iframes, file:// pages and data: URLs, and after a redirect to another origin during a CORS request. Avoid allowing null: any site can create a document with that origin.
Can I test an API on localhost?
Not live — MySmartCoPilot’s server can reach only public websites. Choose Paste headers, then copy the response headers of the OPTIONS request and of the request itself from your browser’s network panel; they are analysed in your browser.
My server sends the right headers but the browser still blocks the call. Why?
Common causes: the headers are missing on the redirect or error response (the check runs on every response), a proxy or CDN adds a second Access-Control-Allow-Origin, an old cached response without Vary: Origin, or authentication rejecting the OPTIONS request, which never carries credentials.