How to Diagnose CORS Preflight Failures
Since preflight failures block requests before execution, prioritize OPTIONS response validity first.
Typical symptoms
- Console shows “preflight did not succeed”
- Actual request never reaches the target endpoint
- curl works but browser fails
Diagnostic steps
- 1) Use CORS Error Troubleshooting to classify failure by browser error text
- 2) Use CORS Diagnostic to validate request/response consistency
- 3) Verify ACAO/ACAM/ACAH/ACAC with CORS Response Inspect
- 4) Recheck allow rules with Origin Allowlist Check
- 5) Confirm header-origin mismatches via Host/Authority/Origin Inspect
Minimum OPTIONS requirements
- Return 2xx for OPTIONS
- Match Access-Control-Allow-Origin to request origin
- Include requested method in Access-Control-Allow-Methods
- Include requested headers in Access-Control-Allow-Headers
- Add Vary: Origin when origin reflection is dynamic
Common causes
- OPTIONS is blocked by API gateway authorization
- Allow-Headers does not match real request headers
- Using ACAO=* together with credentials=true
- Environment origin missing from production allowlist
Post-fix verification
- Confirm preflight returns 2xx for same scenario
- Confirm actual request reaches backend and returns expected response
- Verify issue no longer reproduces on major browsers
Tools to use
- CORS Error Troubleshooting
- CORS Diagnostic
- CORS Response Inspect
- Origin Allowlist Check
- Host/Authority/Origin Inspect
- CORS Checklist
FAQ
- Why does curl pass while browser requests fail?
- Browsers enforce preflight and CORS constraints. Validate OPTIONS behavior and Allow-* consistency.
- Can I fix it by setting Access-Control-Allow-Origin to *?
- Not with credentials=true. For credentialed requests, return explicit origins.
Referenced specs
Next to view (diagnostic order)
These links are generated from site_map rules in recommended diagnostic order.
- CORS Error Troubleshooting — Troubleshoot CORS failures by correlating browser errors with request/response headers
- CORS Diagnostic — Diagnose CORS decisions by comparing Origin and Allow-*
- CORS Response Inspect — Parse Access-Control-Allow-* headers to audit CORS responses
- Origin Allowlist Check — Match Origin values against an allowlist
- CORS Checklist — Provide a step-by-step CORS verification checklist
- Host/Authority/Origin Inspect — Cross-check Host/:authority/Origin/Referer for mismatches
- Symptom-Based Diagnostic Guide (Start Here) — A central hub that routes cache/CORS/JWT/MIME incidents into shortest symptom-first diagnostic paths
- How to Diagnose Missing 304 Responses — Trace ETag/Last-Modified and If-* round trips to isolate missing 304 behavior
Same-theme links
Scenario Clusters
Operational incident scenarios that route you into the shortest diagnostic path
- Symptom-Based Diagnostic Guide (Start Here) — A central hub that routes cache/CORS/JWT/MIME incidents into shortest symptom-first diagnostic paths
- How to Diagnose Missing 304 Responses — Trace ETag/Last-Modified and If-* round trips to isolate missing 304 behavior
- How to Diagnose Stale Content After Deployment — Check cache policy by HTML/API/static assets to isolate stale deployment issues quickly
- JWT 401/403 Diagnostic Playbook — Separate 401 and 403 using Authorization, WWW-Authenticate, claims, and signature checks
- How to Diagnose Retry Storms on 429/503 — Isolate Retry-After parsing and client implementation gaps to stop excessive retries
- How to Diagnose JS/CSS Blocks from nosniff Mismatch — Trace Content-Type vs nosniff mismatches, fallback responses, and delivery-layer rewrites
- How to Diagnose Set-Cookie Not Persisting — Isolate cookie persistence failures by checking Domain/Path/Secure/SameSite in order
- How to Diagnose Lost Login After OAuth Return — Isolate cookie-delivery failures after IdP return across SameSite, Secure, Path/Domain, and collisions
- How to Diagnose Same-Name Cookie Collisions — Resolve unstable behavior by tracing same-name cookie path/domain variants, overwrite order, and send collisions
- Cookie Incident Operational Checklist — Standardize response from triage to permanent fixes across storage failures, OAuth return issues, and same-name collisions