372
@fg_stall ·

CORS preflight passes in curl but the browser still blocks my PATCH Backend

GET and POST to the same API work fine from the browser. PATCH gets blocked with the usual message about the preflight response not passing the access control check. If I hit the OPTIONS endpoint with curl I get a 204 and headers that look correct to me. Express with the cors middleware, API on a different origin to the frontend, auth via a bearer token in a header.

7 answers Share
Report

Answering anonymously — a moderator will review it first.

  • @stack_trace_sy · 4mo ago · 3 replies

    curl doesn't enforce CORS, it just prints headers, so it will always look fine. Read the actual browser message rather than the summary line, it usually names the missing piece.

    My first guess is Access-Control-Allow-Methods. Plenty of setups end up with GET, POST, HEAD and PATCH simply isn't in the list, which passes for your working verbs and fails for this one.

    Second guess is Access-Control-Allow-Headers needing to list authorization and content-type explicitly. A bearer token makes the request non-simple, so both have to be allowed by name.

    Third, and this one is sneaky: check your auth middleware runs after the CORS middleware. If something is answering OPTIONS with a 401 because there's no token on the preflight, curl won't care and the browser will refuse.

    335
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @stack_trace_sy · 4mo ago

      Classic. While you're there, set maxAge to something like 600 so the browser stops sending a preflight before every single write.

      90
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
    • @fg_stall · 4mo ago

      It was the methods list. The middleware was mounted with an explicit methods array from two years ago and PATCH was never added.

      130
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @hotswap_hana · 4mo ago · 2 replies

    Worth knowing that the CORS headers have to be on the real response as well as the preflight. A lot of setups add them to OPTIONS only, then the PATCH itself succeeds server-side and the browser throws the result away, which looks identical from the front end and is very confusing when you check the database and the row has changed.

    205
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @night_train_ned · 4mo ago

      This one cost me an entire afternoon once. The write had happened four times.

      93
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @midnight_pager · 4mo ago

    Check for a redirect on the preflight too. If your route is /api/things/42 and something normalises a trailing slash with a 301, the browser will not follow a redirect on an OPTIONS preflight, it just fails. Look at the status code in the network tab rather than assuming it's 204.

    156
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @boring_portfolio · 4mo ago

    Longer term suggestion: reverse proxy the API onto the same origin as the frontend and CORS stops being part of your architecture. Every project I've moved to that setup has permanently deleted a category of bug from its life.

    137
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report