How HTTP Works

Response header

< Access-Control-Allow-Methods: GET, POST, PUT, DELETE

Access-Control-Allow-Methods Header

Learn how Access-Control-Allow-Methods specifies which HTTP methods are permitted for cross-origin requests in CORS preflight responses.

Reviewed 5 min readintermediate2 sourcesTry itMarkdown
Direction
Response
Category
CORS
Spec
Fetch Standard
On this page

TL;DR: Access-Control-Allow-Methods: PUT, DELETE permits those methods after a successful CORS preflight. It does not make an API read-only or authorize a user. GET, HEAD, and POST are CORS-safelisted; omitting them does not deny them.

What is Access-Control-Allow-Methods?

This is the server’s answer to Access-Control-Request-Method in an OPTIONS preflight. A browser checks a non-safelisted method against the returned list before sending the actual request. The header is not the HTTP Allow header used to describe a resource’s supported methods.

How Access-Control-Allow-Methods Works

Illustrative header excerpts for a DELETE preflight:

OPTIONS /api/posts/123 HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: authorization
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: DELETE
Access-Control-Allow-Headers: Authorization

The method and the Authorization header need permission. The actual DELETE response still needs Access-Control-Allow-Origin; methods permission alone is insufficient.

Syntax

Access-Control-Allow-Methods: GET, HEAD, POST, PUT, PATCH, DELETE

Method tokens are case-sensitive. Use PATCH, not patch. * has wildcard meaning only when credentials mode is not include; with include, it is a literal method name. MDN describes both the wildcard and safelisted methods.

Common Examples

For a collection accepting JSON POSTs, return GET, POST. For an individual record accepting replacement and deletion, return GET, PUT, DELETE. Listing GET and POST can document intent, but does not add enforcement for those safelisted methods.

These policies describe different endpoints, rather than a universal API-wide list:

EndpointIntended operationsMethods advertised
/api/postsRead a collection; create a postGET, POST
/api/posts/123Read, replace, or delete one postGET, PUT, DELETE
/api/searchRead-only searchGET, HEAD

A JSON POST can preflight because of Content-Type, even though POST itself is safelisted. It still needs permission for that header.

Real-World Scenarios

An API returning only Access-Control-Allow-Methods: GET can still receive a cross-origin form POST. A read-only endpoint must reject writes in its route handler. CORS controls browser access, while non-browser clients can call the route independently of these headers.

For a read-only Express route, enforce the method independently of CORS. This fragment belongs before any write handlers:

app.use('/api/search', cors({
  origin: 'https://app.example.com',
  methods: ['GET', 'HEAD']
}))
app.use('/api/search', (req, res, next) => {
  if (req.method === 'GET' || req.method === 'HEAD') return next()
  res.set('Allow', 'GET, HEAD').status(405).end()
})
app.get('/api/search', (_req, res) => res.json({ results: [] }))

Application-level CORS middleware consumes valid preflight requests before the method guard. The guard rejects an actual POST, including one sent by curl. RFC 9110 requires Allow in a 405 response.

Server Implementation

Configure an existing Express app with the cors package:

const cors = require('cors')
app.use('/api/posts', cors({
  origin: 'https://app.example.com',
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
}))

Place this before the API handlers. The middleware handles OPTIONS preflights; you still need real handlers for the methods advertised.

A narrower route can have a different policy from the surrounding API. Register it before a broad middleware that would answer its OPTIONS request:

const readPolicy = cors({
  origin: 'https://app.example.com',
  methods: ['GET', 'HEAD']
})
app.options('/api/audit', readPolicy)
app.get('/api/audit', readPolicy, (_req, res) => res.json({ events: [] }))

This is only the CORS and read-handler portion; authorization still belongs on the actual audit route. If an earlier app.use(cors(...)) already handles OPTIONS, this per-route policy will never answer that preflight.

For FastAPI, configure the list explicitly on the application serving these routes:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://app.example.com"],
    allow_methods=["GET", "PUT", "DELETE"],
    allow_headers=["Content-Type", "Authorization"],
)

FastAPI’s middleware options do not create PUT or DELETE endpoints. Keep the advertised list aligned with the handlers in that application.

Getting Access-Control-Allow-Methods right

Handle OPTIONS, but do not add OPTIONS to the list merely because the preflight uses it. The list describes the subsequent request. OPTIONS belongs there only if you intend to permit an actual cross-origin OPTIONS request.

A preflight must return a successful status such as 204, not an authentication redirect or a 401. Authenticate and authorize the actual operation independently.

HTTP Methods Overview

CORS-safelisted is not synonymous with safe: POST is safelisted but is not a safe method. GET and HEAD are safe; PUT and DELETE are idempotent but can change state. Idempotence refers to the intended effect of repeating a request, not identical response bodies or statuses. RFC 9110 defines these properties.

MethodSafeIdempotentCORS-safelisted
GETYesYesYes
HEADYesYesYes
POSTNoNoYes
PUTNoYesNo
DELETENoYesNo

A repeated DELETE may return 404 after the first request removed the record; that does not change DELETE’s idempotent semantics. Do not infer retry safety from the CORS safelist.

Common Method Combinations

Return a list for the specific endpoint rather than copying every method into every preflight response. Advertising DELETE on a route that has no DELETE handler makes preflight succeed only to leave the actual request failing, often with 405.

Separate public reads from authenticated writes when they have different consumers. A public catalogue can allow GET, HEAD, while a management application permits PUT, DELETE on record routes. Ensure the management handlers still check the user’s role. An allowlisted frontend origin does not imply that every user of that frontend has write permission.

Testing Access-Control-Allow-Methods

curl -i -X OPTIONS https://api.example.com/api/posts/123 \
  -H 'Origin: https://app.example.com' \
  -H 'Access-Control-Request-Method: DELETE' \
  -H 'Access-Control-Request-Headers: authorization'

Compare the method with the returned list in browser Network. Do not manually set Access-Control-Request-Method in fetch: it is a forbidden request header. CORS failures surface to JavaScript as network errors, not a reliable message identifying which header failed. Inspect the console and OPTIONS response instead.

After inspecting OPTIONS, test a deletion against a disposable record in the browser:

const response = await fetch('https://api.example.com/api/posts/123', {
  method: 'DELETE',
  headers: { Authorization: 'Bearer REPLACE_WITH_TOKEN' }
})
if (!response.ok) throw new Error(`DELETE returned HTTP ${response.status}`)

A readable 405 means the browser sent the operation and the application rejected its method. A rejected fetch needs Network/Console inspection: a denied preflight, a missing CORS header on the DELETE response, and a connection failure can all look like network errors to application code.

Frequently asked questions

What is Access-Control-Allow-Methods?

This CORS header lists HTTP methods allowed for cross-origin requests. It is sent in preflight responses to tell browsers which methods are permitted.

Which methods need to be listed?

Simple methods (GET, HEAD, POST) work without listing. List methods like PUT, DELETE, PATCH that trigger preflight. Example: Access-Control-Allow-Methods: GET, POST, PUT, DELETE.

Can I use wildcard for methods?

The wildcard applies only when the request credentials mode is not include. With include, list the intended non-safelisted methods explicitly, even if no cookie exists.

Why is my PUT/DELETE request failing?

These methods trigger preflight. Ensure your OPTIONS response includes Access-Control-Allow-Methods listing PUT or DELETE. The preflight must succeed before the actual request.

Sources

  1. MDN Web Docs: Access-Control-Allow-Methodsdeveloper.mozilla.org
  2. Fetch Standard: CORSfetch.spec.whatwg.org

Keep going

Browse /search