Response header
< Access-Control-Allow-Methods: GET, POST, PUT, DELETEAccess-Control-Allow-Methods Header
Learn how Access-Control-Allow-Methods specifies which HTTP methods are permitted for cross-origin requests in CORS preflight responses.
- Direction
- Response
- Category
- CORS
- Spec
- Fetch Standard
On this page
TL;DR:
Access-Control-Allow-Methods: PUT, DELETEpermits those methods after a successful CORS preflight. It does not make an API read-only or authorize a user.GET,HEAD, andPOSTare 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:
| Endpoint | Intended operations | Methods advertised |
|---|---|---|
/api/posts | Read a collection; create a post | GET, POST |
/api/posts/123 | Read, replace, or delete one post | GET, PUT, DELETE |
/api/search | Read-only search | GET, 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.
| Method | Safe | Idempotent | CORS-safelisted |
|---|---|---|---|
| GET | Yes | Yes | Yes |
| HEAD | Yes | Yes | Yes |
| POST | No | No | Yes |
| PUT | No | Yes | No |
| DELETE | No | Yes | No |
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.
Related Headers
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
Related
Access-Control-Allow-Credentials Header
Learn how Access-Control-Allow-Credentials controls whether browsers expose responses when credentials (cookies, auth headers) are included in CORS requests.
Access-Control-Allow-Headers Header
Learn how Access-Control-Allow-Headers specifies which custom HTTP headers can be used during cross-origin requests in CORS preflight responses.
Access-Control-Allow-Origin Header: CORS Errors and Fixes
Access-Control-Allow-Origin explained: exact browser errors, why * fails with credentials, why you need Vary: Origin, and fixes for nginx, Express and Django.
Access-Control-Expose-Headers: Read Headers in fetch()
Fix response.headers.get() returning null on cross-origin fetch. Access-Control-Expose-Headers, safelisted headers, and why * fails with credentials.