← CORS misconfiguration checker

This checker vs. checking CORS by hand with curl

Every developer testing CORS already has this move: send one request with an attacker-shaped Origin header and read what comes back.

curl -sI -H "Origin: https://evil.example" https://api.example.com/v1/account

If Access-Control-Allow-Origin comes back as https://evil.example, that one request already proved the server reflects an arbitrary origin. For that specific, most-obvious bug, a single manual request is enough. The problem is the other three patterns this checker looks for are not visible in any single response -- they only show up when you compare how the server answers several differently-crafted requests against each other, which is where a one-off curl call stops being enough evidence.

Where one request stops being enough

What you're trying to catchOne curl request by handThis checker's CLI
A server that reflects any origin at all, with no allow-list logic. Works. This is the one pattern a single crafted request reliably shows. Also finds it, in the same run as the three below.
An allow-list built on unanchored startsWith/endsWith string matching. Invisible to the obvious test -- an origin like https://evil.example is correctly rejected by this exact bug. Confirming it needs two specifically-crafted origins (https://api.example.com.attacker-controlled.invalid and https://evil-api.example.com) plus a same-origin control, compared side by side. Sends both bypass shapes and the control automatically, and diffs the three responses for you.
The literal origin Origin: null being accepted, exploitable from a sandboxed iframe. Easy to forget -- it is not a URL, so it does not occur to most people testing the reflected-origin case to also send it. Sent on every run, whether or not you thought to ask for it.
A dynamic Access-Control-Allow-Origin missing Vary: Origin, a shared-cache cross-visitor leak risk. Requires sending two requests with different origins and diffing both full header sets -- not something a single curl -I shows. Compares the baseline and same-origin probes automatically and flags the gap as a notice.
Re-running the same six-request comparison after every deploy. Six separate curl calls, six read-backs, every time -- not something most people repeat unprompted. One command, exit-code gateable in CI.

A single well-aimed curl request is still the fastest way to confirm the obvious reflected-origin case, and this checker's own CLI is not doing anything a request tool couldn't -- it is sending the same kind of requests curl would, just six of them in the specific shapes that expose the other three patterns, and comparing the results for you instead of leaving the comparison to be done, and remembered, by hand.

Common questions

Can't I just run curl with a fake Origin header myself?

Yes, for the most obvious case: curl -H "Origin: https://evil.example" -I https://api.example.com/endpoint and reading Access-Control-Allow-Origin back will catch a server that reflects any origin at all. What one request cannot show you is a comparison -- an unanchored allow-list bug only reveals itself when you compare how the server responds to several differently-crafted origins side by side, which is six requests and six diffs done by hand, every time you want to re-check.

What does comparing several requests catch that one curl call does not?

An allow-list built on startsWith or endsWith string matching instead of an exact host comparison. A single request with an obviously fake origin like https://evil.example is correctly rejected by this exact bug, so it looks safe. The bug only surfaces against a crafted origin such as https://api.example.com.attacker-controlled.invalid (passes a naive startsWith check) or https://evil-api.example.com (passes a naive endsWith check) -- and confirming it is a real bug rather than a coincidence means sending both shapes plus a same-origin control and comparing all three responses, not reading any one of them alone.

Is Origin: null something I would remember to test by hand?

It is easy to skip, because it is not a real website's origin -- it is the literal string "null", which browsers send from a sandboxed iframe, a redirected request, or a file:// page, and which a security-minded allow-list might accept by mistake precisely because it looks like the absence of an origin rather than an attacker-controlled one. Nothing about testing the obvious reflected-origin case prompts you to also try this one; it has to be a request you remember to craft separately.

How would I notice a missing Vary: Origin header just by eyeballing responses?

You would not, from a single request -- a missing Vary: Origin looks identical to a present one until you send two requests with different Origin headers and diff the two response header sets for whether Access-Control-Allow-Origin actually changed. Noticing that a shared cache in front of the API could now serve one visitor's allowed origin back to a different visitor requires holding both responses in view at once, which is exactly the kind of side-by-side comparison a single manual curl call does not produce.

Is this page making a live comparison against a tool or company?

No. This page describes a manual method every developer already has -- sending a request with curl (or Postman, or a browser devtools replay) and reading the response headers by eye. It names no competing product or company. The checker's own CLI requests go straight from your machine to the URL you name, never through this site.

Already testing with a full web security platform instead of raw curl? See how this checker compares to Burp Suite. More background: what a reflected-origin CORS misconfiguration actually lets an attacker do.

Only point either method at a system you own or are authorised to test.

← Back to the CORS misconfiguration checker