CORS header configuration checker
This CORS header configuration checker reviews the response headers that control browser access across origins.
Run — free
Paste a header block and it confirms that Access-Control-Allow-Origin is present, detects the unsafe combination of a wildcard origin with credentials, and identifies a missing Vary: Origin signal for a specific allowed origin. The result is deterministic and explains every finding, making it useful during deployment reviews, incident investigation, automated configuration checks, and routine API hardening without sending a request to the target server.
Read the result as a focused configuration review
Start by copying the response headers exactly as a browser or command-line client received them, with one Name: value pair on each line. Header names are matched without regard to capitalization, and comma-separated Vary values are handled as individual tokens. The checker requires Access-Control-Allow-Origin because there is no CORS policy to assess without that response header; its absence therefore produces an input error instead of an inconclusive report. A successful report returns the normalized allowed origin, whether credentials are enabled, whether the response varies by Origin, and an ordered list of findings. The valid field means that no error-severity issue was detected. A warning can still be present, so consumers should also inspect issue_count and issues rather than treating valid as the only signal. This narrow output is intentional: it gives reviewers stable facts about two common mistakes without pretending to prove that an application’s entire authorization model is secure. Preserve the original response and request origin alongside the result when the check forms part of an audit record.
Understand wildcard origins and credentialed requests
Access-Control-Allow-Origin: * is convenient for genuinely public, non-credentialed resources because it permits any requesting origin to read the response. It must not be combined with Access-Control-Allow-Credentials: true. Browsers reject that pairing for credentialed CORS access, and the configuration often reveals that the server’s intended trust boundary was not expressed correctly. This checker records that combination as wildcard_origin_with_credentials with error severity. The remedy is not automatically to echo every incoming Origin. Instead, decide which origins are trusted, compare the request Origin against an explicit allowlist, and emit one approved origin only after a match. If cookies or other ambient credentials are unnecessary, disable credential support and keep the public wildcard policy. Also remember that CORS controls whether browser scripts can read a response; it is not authentication, authorization, request forgery protection, or a firewall. Review cookie attributes, authorization checks, methods, exposed headers, and preflight behavior separately before declaring the endpoint secure.
Use Vary: Origin to keep shared caches honest
When a server chooses a specific Access-Control-Allow-Origin value according to the request Origin, the response representation effectively depends on that request header. Vary: Origin tells browsers, reverse proxies, and content delivery caches that responses produced for different origins must not be treated as interchangeable. Without it, a cache can reuse a response carrying one tenant’s allowed origin for another request, causing intermittent failures and potentially undermining the policy assumptions around cached content. The checker therefore emits missing_vary_origin when the allowed origin is specific and neither Origin nor the wildcard token appears in Vary. It accepts Origin in a comma-separated Vary line and across repeated Vary headers. The finding is a warning because a pasted response cannot reveal whether the origin value is constant for every request or whether caching is disabled elsewhere. Confirm the server’s actual selection logic and cache directives before changing production behavior. If the origin is dynamically reflected after allowlist validation, adding Origin to Vary is generally the clearest and safest cache signal.
What you can do with it
Review an API deployment
Paste headers from a staging response and catch unsafe credential or cache variation settings before release.
Investigate browser CORS failures
Turn captured response headers into explicit findings when a frontend behaves differently across origins or cache paths.
Automate configuration checks
Run deterministic header blocks through the API in CI and fail policy checks on error-severity findings.
FAQ
What does the check cost?
The browser version runs locally, and each API request costs $0.002.
Why is a missing Access-Control-Allow-Origin header an error?
That header is the basis of a CORS response policy. Without it, there is no allowed-origin value for this focused checker to evaluate.
Is Access-Control-Allow-Origin: * always unsafe?
No. It is appropriate for some public resources that do not allow credentials. The reported error requires both the wildcard origin and credentials set to true.
Why is missing Vary: Origin a warning rather than an error?
The response alone cannot show whether the allowed origin changes between requests or whether an intermediary can cache it. The warning prompts verification of that surrounding behavior.
Does this prove that an endpoint is secure?
No. It checks two common CORS response-header mistakes. Authentication, authorization, cookie policy, preflight handling, allowed methods, and request forgery defenses require separate review.
Are header names case-sensitive?
No. HTTP header names and the relevant directive tokens are compared case-insensitively.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/security/cors-header-check \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"headers":"Access-Control-Allow-Origin: https://app.example.com\nAccess-Control-Allow-Credentials: true\nVary: Accept-Encoding, Origin"}'const res = await fetch("https://api.kit.forhosting.com/security/cors-header-check", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"headers": "Access-Control-Allow-Origin: https://app.example.com\nAccess-Control-Allow-Credentials: true\nVary: Accept-Encoding, Origin"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/security/cors-header-check",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"headers": "Access-Control-Allow-Origin: https://app.example.com\nAccess-Control-Allow-Credentials: true\nVary: Accept-Encoding, Origin"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/security/cors-header-check", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"headers":"Access-Control-Allow-Origin: https://app.example.com\\nAccess-Control-Allow-Credentials: true\\nVary: Accept-Encoding, Origin"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"headers":"Access-Control-Allow-Origin: https://app.example.com\nAccess-Control-Allow-Credentials: true\nVary: Accept-Encoding, Origin"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/security/cors-header-check", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"headers": "Access-Control-Allow-Origin: https://app.example.com\nAccess-Control-Allow-Credentials: true\nVary: Accept-Encoding, Origin"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "security.cors_header_check",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |