Engrain by ByteRush

WEB.14.02 Mid-level· 12 minCore — asked often

CORS in depth: preflights, credentials and fixing the errors

How CORS lets a server allow cross-origin reads, when the browser sends an OPTIONS preflight, every Access-Control header, why * cannot be used with cookies, why curl ignores CORS, and how to fix common errors.

Comes up in: Web fundamentals, Recruiter screen, Debugging

Learn#

Ayesha’s React app at app.example calls her API at api.example. It works in Postman and curl, but the browser console says “blocked by CORS policy”. The API is not broken; the browser is protecting users.

CORS (cross-origin resource sharing) is how a server tells the browser which other origins may read its responses. The same-origin policy blocks those reads by default (WEB.14.01); CORS headers make exceptions. Postman and curl are not browsers, so they ignore CORS entirely.

A “simple” request (GET, HEAD or POST with only basic headers and form-like content types) is sent directly, and the response’s Access-Control-Allow-Origin decides whether the page may read it.

Anything else, such as PUT, DELETE, JSON bodies or an Authorization header, first triggers a preflight request: an OPTIONS request asking permission. Only if the answer allows it does the browser send the real request.

A PUT with JSON: preflight firstStep 1 of 4

Network

    empty

    app.example sends fetch(api.example/orders, { method: 'PUT', headers: { 'Content-Type': 'application/json' } }). PUT and JSON are not "simple".

    Read the steps as text
    1. app.example sends fetch(api.example/orders, { method: 'PUT', headers: { 'Content-Type': 'application/json' } }). PUT and JSON are not "simple".
    2. So the browser first sends a preflight: OPTIONS with Origin, Access-Control-Request-Method: PUT and -Headers: content-type.
    3. The API answers 204 with Allow-Origin: https://app.example, Allow-Methods: PUT, Allow-Headers: content-type, Max-Age: 600.
    4. Now the real PUT is sent. Its response also carries Allow-Origin, so the page may read it. The preflight is cached for 10 minutes.

    Predict#

    Predict first

    Your API returns Access-Control-Allow-Origin: * and the app calls fetch(url, { credentials: 'include' }). What happens?

    Interview angle#

    Say it like this — about 30 seconds

    “CORS lets a server opt in to cross-origin reads. Simple requests, meaning GET, HEAD or POST with basic headers and form content types, are sent directly, and the browser checks Access-Control-Allow-Origin on the response. Anything else, such as PUT, a JSON body or an Authorization header, triggers an OPTIONS preflight first. With credentials, the server must echo the exact origin and send Allow-Credentials true; a wildcard is not allowed. Postman and curl ignore CORS because only browsers enforce it.”

    Stays on your device. Nothing is uploaded.
    Follow-up What is CORS, and what triggers a preflight request?
    A server opt-in for cross-origin reads. Preflight is triggered by non-simple methods, non-safelisted headers, or content types other than form or text/plain.
    Follow-up Why does my request work in Postman or curl but fail in the browser?
    Only browsers enforce CORS; the server is reachable, but it did not send headers allowing your origin.
    Follow-up Why can't you use Access-Control-Allow-Origin: * with cookies?
    It would let any website read responses made with a user’s cookies; browsers require the exact origin plus Allow-Credentials: true.

    Practice#

    On your own

    Write the browser’s two CORS decisions: does this request need a preflight, and may the page read the response?

    Done when

    • Simple methods, safelisted headers and form-like content types skip the preflight; anything else needs one.
    • Without credentials, the exact origin or * allows reading.
    • With credentials, only the exact origin plus allow-credentials: true allows reading.
    Your solution
    Ctrl/⌘ + Enter

    Remember#

    Try to answer each card before you open it. Then add them to your reviews.

    Who enforces CORS?

    The browser. curl and Postman ignore it.

    Name three things that trigger a preflight.

    A PUT or DELETE method, a JSON content type, an Authorization header.

    What does a credentialed CORS response need?

    The exact origin in Allow-Origin, and Allow-Credentials: true.

    What does Access-Control-Max-Age do?

    Lets the browser cache the preflight result for that many seconds.

    Go deeper

    Four layers. Stop at the one your interview needs; each ends with a quick check.

    The core ideaEvery round
    On responses: Allow-Origin (the origin or *), Allow-Credentials, Allow-Methods and Allow-Headers (for preflights), Expose-Headers (headers JavaScript may read), and Max-Age (how long to cache the preflight) Source: MDN: Cross-Origin Resource Sharing (CORS).
    Check yourself: Which header lets JavaScript read a custom response header?

    Access-Control-Expose-Headers.

    How it works underneathSenior rounds

    The credentials mode controls cookies: omit never, same-origin (default) only to your own origin, include cross-origin too, which then requires the stricter CORS rules.
    Add Vary: Origin when you echo the origin, so caches do not serve one origin’s answer to another.

    Check yourself: What is fetch's default credentials mode?

    same-origin.

    Edge cases and trapsWhere people slip
    The browser hides failed responses completely; JavaScript sees “TypeError: Failed to fetch”. Read the console message: missing Allow-Origin, a preflight that returned 401 or 404 (OPTIONS must succeed without auth), * with credentials, or a redirect during preflight.
    Check yourself: Why does a CORS error hide the real status code?

    The browser withholds the whole response, so JavaScript sees only a network error.

    In productionStaff-level answers
    In development, a dev-server proxy (Vite’s server.proxy) sends /api to the backend, so the browser sees one origin. In production, either serve the API on the same origin or allow an exact list of origins on the server Source: Fetch Standard: CORS protocol.
    Check yourself: Why use a dev-server proxy?

    The browser then talks to one origin, so CORS is not needed in development.

    Finished the five steps? Mark it, and its cards join your reviews.

    Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.

    Interview questions this answers

    • What is CORS and what triggers a preflight request?
    • Why does my request work in Postman/curl but fail in the browser?
    • Why can't you use Access-Control-Allow-Origin: * with cookies?