Access-Control-Allow-Methods (tested)

Access-Control-Allow-Methods goes on the response to a CORS preflight and lists the methods the browser may use for the real request. It only matters for methods that trigger a preflight. We tried seven header values against six methods and logged what reached the server.

1. The results

Page at http://localhost:8973, cross-origin fetch() to http://127.0.0.1:8974. Every response carried Access-Control-Allow-Origin: * (or the exact origin plus Access-Control-Allow-Credentials: true in the credentials row). POST used Content-Type: text/plain, so it stayed a simple request. "OK" means the request reached the server and fetch resolved; "blocked" means only the OPTIONS preflight reached the server and fetch threw TypeError: Failed to fetch.

Access-Control-Allow-Methods on preflightGETPOSTPUTDELETEPATCHpatch
(absent)OK, no preflightOK, no preflightblockedblockedblockedblocked
GET, POSTOK, no preflightOK, no preflightblockedblockedblockedblocked
PUTOK, no preflightOK, no preflightOKblockedblockedblocked
put (lowercase)OK, no preflightOK, no preflightblockedblockedblockedblocked
*OK, no preflightOK, no preflightOKOKOKblocked (see 3)
* + credentials: 'include', exact ACAO, ACAC trueOK, no preflightOK, no preflightblockedblockedblockedblocked
PUT, DELETEOK, no preflightOK, no preflightOKOKblockedblocked

Every "OK" for PUT, DELETE and PATCH was two requests on the server: OPTIONS then the real method. Every "blocked" was the OPTIONS alone; the real request never left the browser. The console error was:

Access to fetch at 'http://127.0.0.1:8974/x3' from origin 'http://localhost:8973' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

2. What this means

3. The lowercase patch trap

fetch() uppercases only delete, get, head, options, post and put. Our server logged the preflight as Access-Control-Request-Method: patch, lowercase, and no header value we tried let it through, not even *. In the * row Chromium reported a different and misleading error, even though our preflight did carry Access-Control-Allow-Origin:

... has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

We did not test a server that answers literally patch. The simple fix is to write method: 'PATCH' in your code.

4. JSON POST needs a preflight, but not this header

A POST with Content-Type: application/json did send a preflight (OPTIONS with Access-Control-Request-Method: POST). Without Access-Control-Allow-Headers it was blocked with "Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response." With Access-Control-Allow-Headers: Content-Type it succeeded, both with no Access-Control-Allow-Methods at all and with Access-Control-Allow-Methods: GET. POST is safelisted, so the methods list did not need to mention it.

5. Answering OPTIONS in nginx (not executed)

This snippet is doc-based; we did not run it. Adjust the origin and the lists to your API.

location /api/ {
    if ($request_method = OPTIONS) {
        add_header Access-Control-Allow-Origin "https://app.example.com" always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE" always;
        add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
        add_header Access-Control-Max-Age 600 always;
        add_header Vary Origin always;
        return 204;
    }
    add_header Access-Control-Allow-Origin "https://app.example.com" always;
    add_header Vary Origin always;
    proxy_pass http://backend;
}

Two things to keep in mind: the actual response still needs Access-Control-Allow-Origin (the methods header only matters on the preflight), and some backends return 404 or 405 for OPTIONS, which fails the preflight before the methods list is even read.

Related: when the browser sends the Origin header, Access-Control-Max-Age (preflight caching), Access-Control-Allow-Credentials and allowing multiple origins in CORS.

Frequently asked questions

Do I need Access-Control-Allow-Methods for GET and POST?

No. In our Chromium 153 test, cross-origin GET and POST (with a simple content type) sent no preflight and succeeded with the header absent. Even a JSON POST, which is preflighted, succeeded without it once Access-Control-Allow-Headers allowed Content-Type.

Is Access-Control-Allow-Methods case-sensitive?

Yes. A preflight answered with 'put' did not allow a PUT request; Chromium blocked it with 'Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.'

Does Access-Control-Allow-Methods: * work?

Only for requests without credentials. Without credentials, * allowed PUT, DELETE and PATCH. With credentials: 'include' (and a correct exact origin plus Allow-Credentials: true), PUT, DELETE and PATCH were all blocked.

Why does my PATCH request fail CORS when PUT works?

Either PATCH is missing from your Access-Control-Allow-Methods list, or your code uses lowercase method: 'patch'. fetch() does not uppercase patch, and in our test a lowercase patch request was blocked even with *.

Where does Access-Control-Allow-Methods go, preflight or actual response?

On the preflight (OPTIONS) response; that is where the browser checks it. The actual response still needs Access-Control-Allow-Origin.

Written with AI assistance. Measured on 2026-10-01 in full Chromium 153.0.8010.12 (Playwright, channel 'chromium', fresh browser context per row). The page was served from http://localhost:8973 and a Node.js API on http://127.0.0.1:8974 (a different origin) logged the method of every request, OPTIONS preflights included, and answered preflights with 204 plus the Access-Control-Allow-Methods value under test. The nginx snippet and the notes on other browsers are doc-based, not executed. PlainHeaders is our extension; the other methods are free.