CodingDebuggingIntermediate30 minSaves 30 minutes

Diagnosing CORS Preflight Failures: Browser vs. cURL

Full-stack engineers can methodically troubleshoot CORS preflight issues where browser requests fail but cURL succeeds, by following a structured diagnostic playbook.

For full-stack engineers, this playbook diagnoses CORS preflight failures where browser requests fail but cURL succeeds. It provides a structured, hypothesis-driven approach to troubleshoot issues, covering credential handling, allowed headers, and browser caching with

READY-TO-USE PROMPT

Copy Prompt

prompt.txt
Role: Act as a seasoned full-stack engineer and diagnostic expert, specializing in network protocols and browser behavior.

Context: I am facing a persistent Cross-Origin Resource Sharing (CORS) preflight failure in my browser-based application. The `OPTIONS` request fails, preventing subsequent `POST`/`PUT`/`DELETE` requests from executing. Crucially, identical requests made using `curl` directly from the command line succeed without issue. This discrepancy points to a browser-specific or preflight-specific problem. My current hypothesis areas include credential handling, allowlisted headers, and browser caching mechanisms. The target endpoint is `{{request_url}}`. I have reviewed the network tab in my browser's developer tools, and the `OPTIONS` request shows an error, often a `CORS error` or `net::ERR_FAILED`. I can provide a description of the browser dev tools output if needed: `{{browser_devtools_screenshot_description}}`. The backend is developed using `{{backend_language_framework}}`.

Task: Develop a comprehensive debug playbook for diagnosing and resolving this specific CORS preflight failure scenario. The playbook should be structured to guide an engineer from symptom to resolution.

Constraints:
- Focus exclusively on the scenario where browser preflight fails but cURL succeeds.
- Prioritize actionable steps and command-line checks where applicable.
- Ensure the proposed fixes directly address the identified hypotheses.
- Maintain a diagnostic, hypothesis-driven, and log-aware tone.
- Include specific commands or code snippets where they aid diagnosis or resolution.
- The playbook must be comprehensive enough for an intermediate-level full-stack engineer.

Output Format: Provide the debug playbook using the following structure.

**Debug Playbook: CORS Preflight Failure (Browser vs. cURL)**

**Symptom:**
Browser-initiated `OPTIONS` preflight request fails with a CORS error, but equivalent `curl` requests to the same endpoint succeed. Subsequent actual data requests are blocked.

**Hypothesis List:**

1.  **Credential Handling Discrepancy:** The browser might be sending or expecting credentials (`withCredentials: true`) differently than cURL, or the server is not correctly configured to handle `Access-Control-Allow-Credentials`.
2.  **Missing or Mismatched `Access-Control-Allow-Headers`:** The browser's actual request headers (beyond simple headers) are not fully whitelisted in the server's `Access-Control-Allow-Headers` response for the `OPTIONS` request.
3.  **HTTP Method Mismatch for Preflight:** The server is not correctly responding to the `OPTIONS` method with the expected `Access-Control-Allow-Methods` header that includes the actual request method (e.g., `POST`).
4.  **Browser Caching of Preflight Responses:** The browser might be caching an erroneous or outdated `OPTIONS` response, especially if `Access-Control-Max-Age` is set incorrectly or too high.
5.  **Redirects or Intermediary Proxies:** A redirect or an intermediary proxy might be stripping or modifying CORS-related headers, specifically for `OPTIONS` requests, before they reach the intended server.
6.  **SSL/TLS Issues (Less Common for Preflight, but Possible):** Mismatches in SSL certificates or trust chains between the browser and the server, although typically manifesting earlier, can sometimes interfere.

**Checks (with commands/steps):**

1.  **Inspect Browser Network Tab for `OPTIONS` Request/Response:**
    *   Open Developer Tools (F12) -> Network tab.
    *   Filter by `OPTIONS` method.
    *   Examine Request Headers: Note `Origin`, `Access-Control-Request-Method`, `Access-Control-Request-Headers`.
    *   Examine Response Headers (if any): Look for `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, `Access-Control-Allow-Headers`, `Access-Control-Allow-Credentials`, `Access-Control-Max-Age`.
    *   Check Status Code: A 200 OK is expected for a successful preflight. Non-200 codes (e.g., 401, 403, 500) indicate server-side issues.
2.  **Verify `curl` Behavior with Detailed Headers:**
    *   Replicate the browser's `OPTIONS` request with `curl` to ensure it truly succeeds:
        `curl -v -X OPTIONS -H "Origin: <your-frontend-origin>" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: Content-Type, Authorization" {{request_url}}`
    *   Compare `curl`'s response headers to the browser's expected headers. Look for `Access-Control-*` headers.
3.  **Server-Side CORS Configuration Review:**
    *   Examine the backend code for CORS middleware or configuration (e.g., Express `cors` package, Spring Boot `WebMvcConfigurer`).
    *   Confirm `Access-Control-Allow-Origin` includes the frontend origin.
    *   Confirm `Access-Control-Allow-Methods` includes `OPTIONS` and the method of your actual request (e.g., `POST`).
    *   Confirm `Access-Control-Allow-Headers` explicitly lists all non-simple headers sent by the browser (e.g., `Content-Type`, `Authorization`, `X-Custom-Header`).
    *   Verify `Access-Control-Allow-Credentials` is set to `true` if your frontend uses `withCredentials: true`. If so, `Access-Control-Allow-Origin` *cannot* be `*`.
    *   Check `Access-Control-Max-Age` setting.
4.  **Clear Browser Cache and Site Data:**
    *   In Chrome: Developer Tools -> Application tab -> Storage -> Clear site data.
    *   In Firefox: Settings -> Privacy & Security -> Cookies and Site Data -> Clear Data.
    *   Test again after clearing.
5.  **Check for Redirects:**
    *   Use `curl -v {{request_url}}` to see if any redirects occur before the final endpoint.
    *   Ensure any proxies or load balancers are configured to pass CORS headers correctly.

**Likely Fixes:**

1.  **Credential Mismatch:**
    *   If frontend uses `withCredentials: true`, ensure backend sets `Access-Control-Allow-Credentials: true` and `Access-Control-Allow-Origin` is a specific origin, not `*`.
    *   If credentials are not needed, remove `withCredentials: true` from frontend fetch/XHR calls.
2.  **Missing Headers:**
    *   Add all necessary headers (e.g., `Authorization`, `Content-Type`, custom headers) to `Access-Control-Allow-Headers` in your backend CORS configuration.
    *   Example for Express: `app.use(cors({ origin: 'your-frontend-origin', methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'] }));`
3.  **Method Not Allowed:**
    *   Ensure `OPTIONS` and your actual request method (e.g., `POST`) are explicitly listed in `Access-Control-Allow-Methods` on the server.
4.  **Stale Preflight Cache:**
    *   Reduce `Access-Control-Max-Age` during development, or remove it temporarily to disable preflight caching.
    *   Ensure `Access-Control-Max-Age` is not set to a negative value or an excessively long duration in production if issues persist.
5.  **Proxy/Redirect Interference:**
    *   Configure proxies/load balancers to preserve `Origin`, `Access-Control-Request-*` headers on incoming requests and `Access-Control-Allow-*` headers on responses.
    *   Ensure any redirects are handled gracefully and don't strip headers.

**Verification:**

1.  After applying a fix, clear browser cache and retry the browser-based request.
2.  Confirm the `OPTIONS` request in the browser's network tab now returns a `200 OK` status with all expected `Access-Control-*` headers.
3.  Verify the subsequent actual data request (e.g., `POST`) also succeeds.

Estimated results

DifficultyIntermediate
Setup time30 min
Time saved30 minutes
Best modelsClaude, ChatGPT, Gemini
Best audienceSoftware Development, Web Development

Editor's note

Why this prompt matters

CORS preflight failures are a common source of frustration for full-stack engineers, often leading to prolonged debugging sessions. The challenge intensifies when a request works perfectly via curl but consistently fails in a browser environment. This specific discrepancy points to subtle issues related to browser security policies, preflight header handling, or caching, rather than a fundamental server misconfiguration.

This workflow provides a methodical approach for diagnosing these elusive browser-specific CORS preflight problems. It's designed for engineers who have already confirmed their backend logic is sound via command-line tools but are still blocked by browser-initiated OPTIONS request failures. By following a structured playbook, developers can move beyond guesswork and systematically identify the root cause, whether it's related to credential handling, header mismatches, or browser caching. Reach for this playbook when your browser's network tab shows a failed OPTIONS request, while curl indicates a successful connection to the same endpoint.

Anatomy

Prompt engineering breakdown

Role

Act as a seasoned full-stack engineer and diagnostic expert, specializing in network protocols and browser behavior.

Context

The user is experiencing a CORS preflight failure in a browser application, where `OPTIONS` requests fail, but identical `curl` requests succeed. The problem is specific to browser/preflight behavior, with hypotheses around credential handling, allowlisted headers, and caching for `{{request_url}}` and `{{backend_language_framework}}`.

Goal

Develop a comprehensive debug playbook structured from symptom to resolution for this specific CORS preflight failure scenario.

Constraints

The playbook must focus exclusively on browser preflight failures where cURL succeeds, prioritize actionable steps and command-line checks, ensure fixes address identified hypotheses, maintain a diagnostic and log-aware tone, include specific commands, and be comprehensive for intermediate full-stack engineers.

Output format

The output must be a 'Debug Playbook: CORS Preflight Failure (Browser vs. cURL)' containing sections for Symptom, Hypothesis List, Checks (with commands/steps), Likely Fixes, and Verification.

Why this structure works

Role priming establishes the AI's persona as an expert, ensuring a high-quality, authoritative response. Explicit constraints narrow the scope to the exact problem, preventing irrelevant information. The detailed structured output requirement guarantees the playbook follows a logical diagnostic flow, making it immediately actionable for the user.

Pick your version

Prompt variations

BeginnerWorks with any model

When you're new to web development or debugging CORS, needing a straightforward, step-by-step guide without complex jargon.

prompt.txt
As a friendly web mentor, help me understand and fix a common web issue. My website can't talk to my server from the browser, saying 'CORS error,' especially for actions like sending data. But when I use a simple tool called `curl` to talk to `{{request_url}}`, it works fine. I think it might be about how my browser handles login details, what information it's allowed to send, or if it's using old cached info. Please give me a simple checklist to follow. Tell me what to look for in my browser's developer tools, what settings to check on my server (built with `{{backend_language_framework}}`), and how to clear my browser's memory. I need clear instructions on what to do and how to check if it's fixed.
ProfessionalBest with claude

For experienced full-stack engineers seeking an in-depth, structured diagnostic and resolution process for complex CORS preflight issues.

prompt.txt
Assume the role of a senior network diagnostician. I'm investigating a critical CORS preflight anomaly: browser-initiated `OPTIONS` requests consistently fail with `CORS error` or `net::ERR_FAILED` when targeting `{{request_url}}`, yet direct `curl` invocations for the same endpoint succeed. This points to a browser-specific preflight interaction issue. My working hypotheses center on `Access-Control-Allow-Credentials` discrepancies, unlisted `Access-Control-Request-Headers`, `Access-Control-Allow-Methods` mismatches, and potential browser-side caching (`Access-Control-Max-Age`) of preflight responses. The backend is `{{backend_language_framework}}`. Provide a comprehensive, actionable debug playbook. Structure it with clear sections: Symptom, detailed Hypothesis List, methodical Checks (with precise commands and browser dev tool steps), targeted Likely Fixes addressing each hypothesis, and a Verification process. Focus on the `OPTIONS` request flow and server-side CORS configuration.
Short VersionWorks with any model

When you need a quick reference or a concise mental model for debugging CORS preflight failures where cURL works.

prompt.txt
Diagnose a CORS preflight failure where browser `OPTIONS` requests fail for `{{request_url}}`, but `curl` succeeds. Focus on `withCredentials` settings, `Access-Control-Allow-Headers` and `Access-Control-Allow-Methods` in the server's `{{backend_language_framework}}` configuration, and browser `Access-Control-Max-Age` caching. Check the browser network tab for `OPTIONS` request/response headers and status codes. Verify server-side CORS rules. Clear browser cache and retest. This discrepancy often highlights a specific browser-server preflight handshake issue.
EnterpriseBest with gemini

When debugging CORS issues in a production environment, requiring consideration for security policies, compliance, system impact, and stakeholder communication.

prompt.txt
As a principal infrastructure and security architect, address a critical CORS preflight failure impacting our enterprise application at `{{request_url}}`. Browser `OPTIONS` requests are failing, blocking critical business functions, while `curl` requests bypass the issue. This suggests a nuanced browser security policy or preflight handling problem. Our `{{backend_language_framework}}` backend must adhere to strict internal security and compliance policies. Develop a debug playbook that not only diagnoses the technical root cause (credentials, headers, caching) but also outlines steps for incident documentation, potential security audit implications, impact assessment on user experience, and a communication plan for stakeholders. Include checks for proxy/WAF interference and ensure all proposed fixes align with our security baselines and regulatory requirements. Provide clear verification steps to confirm resolution and prevent recurrence.

What you'll get

Expected output

Debug Playbook: CORS Preflight Failure (Browser vs. cURL)

Symptom: Browser-initiated OPTIONS preflight request fails with a CORS error, but equivalent curl requests to the same endpoint succeed. Subsequent actual data requests are blocked.

Hypothesis List:

  1. Credential Handling Discrepancy: The browser might be sending or expecting credentials (withCredentials: true) differently than cURL, or the server is not correctly configured to handle Access-Control-Allow-Credentials.
  2. Missing or Mismatched `Access-Control-Allow-Headers`: The browser's actual request headers (beyond simple headers) are not fully whitelisted in the server's Access-Control-Allow-Headers response for the OPTIONS request.
  3. HTTP Method Mismatch for Preflight: The server is not correctly responding to the OPTIONS method with the expected Access-Control-Allow-Methods header that includes the actual request method (e.g., POST).
  4. Browser Caching of Preflight Responses: The browser might be caching an erroneous or outdated OPTIONS response, especially if Access-Control-Max-Age is set incorrectly or too high.
  5. Redirects or Intermediary Proxies: A redirect or an intermediary proxy might be stripping or modifying CORS-related headers, specifically for OPTIONS requests, before they reach the intended server.
  6. SSL/TLS Issues (Less Common for Preflight, but Possible): Mismatches in SSL certificates or trust chains between the browser and the server, although typically manifesting earlier, can sometimes interfere.

Checks (with commands/steps):

  1. Inspect Browser Network Tab for `OPTIONS` Request/Response:

* Open Developer Tools (F12) -> Network tab. * Filter by OPTIONS method. * Examine Request Headers: Note Origin, Access-Control-Request-Method, Access-Control-Request-Headers. * Examine Response Headers (if any): Look for Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Allow-Credentials, Access-Control-Max-Age. * Check Status Code: A 200 OK is expected for a successful preflight. Non-200 codes (e.g., 401, 403, 500) indicate server-side issues.

  1. Verify `curl` Behavior with Detailed Headers:

* Replicate the browser's OPTIONS request with curl to ensure it truly succeeds: curl -v -X OPTIONS -H "Origin: <your-frontend-origin>" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: Content-Type, Authorization" {{request_url}} * Compare curl's response headers to the browser's expected headers. Look for Access-Control-* headers.

  1. Server-Side CORS Configuration Review:

* Examine the backend code for CORS middleware or configuration (e.g., Express cors package, Spring Boot WebMvcConfigurer). * Confirm Access-Control-Allow-Origin includes the frontend origin. * Confirm Access-Control-Allow-Methods includes OPTIONS and the method of your actual request (e.g., POST). * Confirm Access-Control-Allow-Headers explicitly lists all non-simple headers sent by the browser (e.g., Content-Type, Authorization, X-Custom-Header). * Verify Access-Control-Allow-Credentials is set to true if your frontend uses withCredentials: true. If so, Access-Control-Allow-Origin *cannot* be *. * Check Access-Control-Max-Age setting.

  1. Clear Browser Cache and Site Data:

* In Chrome: Developer Tools -> Application tab -> Storage -> Clear site data. * In Firefox: Settings -> Privacy & Security -> Cookies and Site Data -> Clear Data. * Test again after clearing.

  1. Check for Redirects:

* Use curl -v {{request_url}} to see if any redirects occur before the final endpoint. * Ensure any proxies or load balancers are configured to pass CORS headers correctly.

Likely Fixes:

  1. Credential Mismatch:

* If frontend uses withCredentials: true, ensure backend sets Access-Control-Allow-Credentials: true and Access-Control-Allow-Origin is a specific origin, not *. * If credentials are not needed, remove withCredentials: true from frontend fetch/XHR calls.

  1. Missing Headers:

* Add all necessary headers (e.g., Authorization, Content-Type, custom headers) to Access-Control-Allow-Headers in your backend CORS configuration. * Example for Express: app.use(cors({ origin: 'your-frontend-origin', methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'] }));

  1. Method Not Allowed:

* Ensure OPTIONS and your actual request method (e.g., POST) are explicitly listed in Access-Control-Allow-Methods on the server.

  1. Stale Preflight Cache:

* Reduce Access-Control-Max-Age during development, or remove it temporarily to disable preflight caching. * Ensure Access-Control-Max-Age is not set to a negative value or an excessively long duration in production if issues persist.

  1. Proxy/Redirect Interference:

* Configure proxies/load balancers to preserve Origin, Access-Control-Request-* headers on incoming requests and Access-Control-Allow-* headers on responses. * Ensure any redirects are handled gracefully and don't strip headers.

Verification:

  1. After applying a fix, clear browser cache and retry the browser-based request.
  2. Confirm the OPTIONS request in the browser's network tab now returns a 200 OK status with all expected Access-Control-* headers.
  3. Verify the subsequent actual data request (e.g., POST) also succeeds.

Under the hood

Why this prompt works

This prompt is effective due to several targeted prompt engineering techniques. First, role priming establishes the persona of a "seasoned full-stack engineer and diagnostic expert." This directs the model to generate a technically accurate, experienced, and practical response, avoiding generic or superficial advice. Second, explicit constraints are used to narrow the focus precisely to the "browser preflight fails but cURL succeeds" scenario, preventing the model from veering into broader CORS explanations. Constraints also mandate actionable steps and command-line checks, ensuring the output is immediately useful.

The most critical technique here is structured output. By defining a rigid "Output Format" with specific headings like "Symptom," "Hypothesis List," "Checks," "Likely Fixes," and "Verification," the prompt forces the model to produce a comprehensive, organized debug playbook. This structure ensures all key diagnostic stages are covered systematically, which is far more effective than a free-form query. It transforms the output into a ready-to-use resource for engineers, directly addressing the problem with a logical, step-by-step resolution path.

Model fit

Best AI models for this prompt

Claude

Claude excels at generating structured diagnostic playbooks, breaking down complex issues into logical steps. Its ability to maintain context across a detailed request makes it suitable for producing comprehensive guides with specific checks and fixes. It's particularly good at explaining the rationale behind each step and anticipating potential edge cases. See the full Claude hub for deeper guidance.

ChatGPT

ChatGPT is effective for this task due to its strong general knowledge base in web technologies and its capability to provide executable code snippets for various languages and frameworks. It can quickly generate the necessary curl commands or backend configuration examples required for debugging CORS issues. Its strength lies in providing clear, concise instructions for each step of the diagnostic process. See the full ChatGPT hub for deeper guidance.

Gemini

Gemini performs well when synthesizing technical information into a structured, actionable format like a debug playbook. It can process the detailed scenario of browser vs. cURL CORS behavior and produce a comprehensive list of hypotheses, checks, and fixes. Its strength is in organizing complex information into a clear, easy-to-follow guide for engineers. See the full Gemini hub for deeper guidance.

When to use

  • When browser-initiated OPTIONS preflight requests fail, but curl to the same endpoint succeeds.
  • When diagnosing specific OPTIONS method failures that block subsequent data requests.
  • When Access-Control-* response headers from the server seem inconsistent or incomplete in the browser.
  • When frontend withCredentials: true is suspected to cause preflight rejection.
  • When browser caching of CORS responses might be masking current server configuration.

When not to use

  • When CORS failures occur for both browser and curl requests, indicating a fundamental server-side misconfiguration.
  • When debugging simple GET requests that do not trigger an OPTIONS preflight.
  • When the issue is not related to CORS, but general network connectivity or DNS resolution.
  • When the backend has no CORS configuration implemented yet; begin with basic setup first.

Get more from it

Pro tips

  • 1

    Always compare browser and `curl` request/response headers side-by-side to spot subtle differences, avoiding misdiagnosis.

  • 2

    Temporarily disable `Access-Control-Max-Age` during development to prevent stale preflight caches from masking current configuration changes.

  • 3

    Use `curl -v` to see full request/response details, including redirects, which helps identify intermediary proxy issues.

  • 4

    Pay close attention to `Origin` header values. A mismatch here is a frequent cause of `Access-Control-Allow-Origin` rejection.

  • 5

    Confirm all non-standard or custom headers sent by the client are explicitly listed in `Access-Control-Allow-Headers` on the server.

  • 6

    Remember `Access-Control-Allow-Credentials: true` requires a specific `Access-Control-Allow-Origin`, not `*`.

Don't ship this

Common mistakes

  • `Access-Control-Allow-Origin` is `*` but `Access-Control-Allow-Credentials` is `true`.

    Fix — Set `Access-Control-Allow-Origin` to the specific frontend domain when credentials are used, as `*` is not allowed.

  • Forgetting to include `OPTIONS` in `Access-Control-Allow-Methods` on the server-side.

    Fix — Explicitly add `OPTIONS` alongside other methods (e.g., `POST`, `PUT`) to the server's allowed methods list.

  • Not listing all custom or `Authorization` headers in `Access-Control-Allow-Headers`.

    Fix — Inspect browser request headers and ensure every non-simple header is present in the server's `Access-Control-Allow-Headers`.

  • Browser caching a bad preflight response due to a high `Access-Control-Max-Age` value.

    Fix — Clear browser site data and consider reducing `Access-Control-Max-Age` during development to quickly see changes.

  • Frontend sending `withCredentials: true` when the backend isn't configured for it.

    Fix — Either configure the backend to handle credentials or remove `withCredentials: true` if not needed.

  • Debugging `OPTIONS` failures without checking the actual `Access-Control-Request-Headers` sent by the browser.

    Fix — Always inspect the browser's network tab to identify the exact headers sent during the preflight request.

People also ask

Frequently asked questions

Q.Can this playbook help with `GET` requests failing due to CORS?

This playbook specifically targets OPTIONS preflight failures, which affect POST, PUT, DELETE. GET requests typically don't trigger preflights unless custom headers are involved, so other debugging steps might be needed.

Q.What if my backend uses a reverse proxy like Nginx or an API Gateway?

Ensure your proxy configuration passes all Origin and Access-Control-Request-* headers through to your backend. Also confirm it doesn't strip Access-Control-Allow-* headers from the backend's responses before sending them to the client.

Q.Is `Access-Control-Max-Age` an important header to configure?

Yes, it tells the browser how long to cache preflight results. A high value can mask underlying issues, while a low value (or zero) ensures preflights run often, which is useful for debugging.

Q.Why does `curl` work but the browser fails, even for seemingly identical requests?

Browsers strictly enforce the CORS specification, including the OPTIONS preflight. curl does not inherently enforce CORS, making it a valuable tool for isolating server-side responses from browser-specific behaviors.

Q.How do I know which headers are 'simple' versus those needing preflight?

Simple headers are a limited set like Accept, Content-Type (only application/x-www-form-urlencoded, multipart/form-data, text/plain). Any other header, or Content-Type with other values, triggers a preflight OPTIONS request.

Q.What if my server sends a 404 or 500 for the `OPTIONS` request itself?

A 404 means the OPTIONS route isn't handled by your server. A 500 indicates a server-side error during preflight processing. Address these server-level issues first, as they prevent any CORS headers from being sent.

Version 1.0Last reviewed July 20, 2026
Reviewed by PromptInFlow Editorial Team