POST
Submits data to a resource, often creating a new subordinate resource or triggering a side effect.
What it does
POST submits data to a resource for it to process — most often creating a new subordinate resource ("create this order"), but really it's the HTTP method for "do whatever the target resource defines processing this content means." That vagueness is deliberate: POST is the general-purpose method for anything that doesn't fit GET's read-only contract or PUT's "replace this exact resource" contract.
This flexibility is also POST's biggest problem. Because it can mean almost anything, intermediaries (browsers, proxies, caches) treat every POST as potentially unsafe and non-cacheable by default — even a POST that's genuinely just a read (like a search-with-complex-filters endpoint).
Semantics
| Property | Value | Why it matters |
|---|---|---|
| Safe | No | May change server state |
| Idempotent | No | Submitting the same POST twice can create two resources (classic "double-click bought two tickets" bug) |
| Cacheable | Conditionally | Only if the response explicitly includes freshness info (Cache-Control, Expires) — not cached by default like GET |
| Request body | Yes | The submitted data |
| Response body | Yes | Usually the created/processed resource, or a status message |
Syntax & example request
POST /orders HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"item": "widget", "quantity": 3}
HTTP/1.1 201 Created
Content-Type: application/json
Location: /orders/981
{"id": 981, "item": "widget", "quantity": 3}
curl example
curl -X POST https://api.example.com/orders \
-H "Content-Type: application/json" \
-d '{"item": "widget", "quantity": 3}'
Common status codes returned
- 200 OK — processed, response body has the result → see 200
- 201 Created — new resource created,
Locationheader points to it → see 201 - 202 Accepted — request accepted for async processing, not done yet → see 202
- 204 No Content — processed successfully, nothing to return → see 204
POST vs PUT vs QUERY
| POST | PUT | QUERY | |
|---|---|---|---|
| Safe | No | No | Yes |
| Idempotent | No | Yes | Yes |
| Typical use | Create / process | Replace a known resource | Read with a complex body |
| Target URI meaning | "process this data" | "this exact resource" | "read via this body" |
The rule of thumb: if you know the exact URI of the resource you're modifying and repeating the request is harmless, that's PUT. If the server decides the URI (like assigning a new order ID) or repeating causes duplicates, that's POST. If you're reading — not writing — and just needed a body for a complex filter, that's QUERY, not POST.
Real-world usage
- Creating resources:
POST /users,POST /orders - Submitting HTML forms
- Triggering actions that don't map cleanly to CRUD:
POST /orders/42/cancel,POST /login - File uploads (
multipart/form-data) - The de facto "everything else" method in RPC-style and GraphQL APIs (a single
POST /graphqlhandles all operations)
Security considerations
Because POST isn't idempotent, retries (network blips, double-clicks, browser back-button resubmission) are dangerous — duplicate charges, duplicate orders. Standard mitigations: idempotency keys (client generates a unique request ID, server dedupes), disabling the submit button after click, and using PRG (Post/Redirect/Get) so a page refresh doesn't resubmit the form.
CSRF protection matters specifically for POST (and other unsafe methods) — since POST can mutate state, a malicious site can trick a browser into submitting one on the user's behalf using their session cookie, unless you have CSRF tokens or SameSite cookies in place.
FAQ
Why can't I just cache POST responses like GET?
You can — but only opt-in, never by default. A POST response is only cacheable if it carries freshness headers explicitly, because caches can't assume a processing request produces the same result twice (a POST to /orders doesn't return the same order every time).
Is POST the right method for search endpoints?
It's the common workaround when filters are too complex for a GET query string, but it's semantically wrong — a search is a read, and POST tells every intermediary "assume this isn't safe to retry or cache." The purpose-built fix is the new QUERY method (RFC 10008), which gives searches GET-like safety and cacheability with a POST-like body.
Does POST always create a resource?
No — that's just the most common use. POST is officially defined as "process this content according to the resource's own semantics," which also covers actions, form submissions, and RPC calls that don't create anything at all.
Fun fact
POST has been in HTTP since HTTP/1.0 (1996), making it exactly as old as GET and HEAD — but its use case exploded decades later with the rise of REST APIs and, later, single-endpoint RPC-style APIs like GraphQL, where POST ends up doing double duty as both the "write" and (misused) "read" method.