Loading learning content...
When you click a link, submit a form, or update your profile on any website, your browser is silently speaking a language that has powered the World Wide Web since 1991. That language is HTTP—the Hypertext Transfer Protocol—and at its grammatical core are HTTP methods, the verbs that express what action you want to perform on a resource.
HTTP methods are deceptively simple on the surface. A GET retrieves data. A POST submits data. But beneath this simplicity lies a rich semantic framework that, when properly understood and applied, enables the construction of scalable, cacheable, and interoperable web systems. Misunderstanding these methods leads to security vulnerabilities, broken caches, confused APIs, and systems that scale poorly.
This page provides a comprehensive, authoritative examination of the four most critical HTTP methods: GET, POST, PUT, and DELETE. We will explore not just what they do, but why they're designed this way, how their semantic properties enable architectural patterns like REST, and what happens when developers use them incorrectly.
By the end of this page, you will: • Understand the precise semantics of GET, POST, PUT, and DELETE • Master the concepts of idempotency and safety in HTTP methods • Recognize how method properties enable caching and scalability • Apply correct method selection in API design • Identify common misuses and their consequences
Before diving into individual methods, we must understand the conceptual framework that governs all HTTP methods. This framework is defined in RFC 9110 (HTTP Semantics) and distinguishes methods by two fundamental properties: safety and idempotency.
HTTP operates on a resource-centric model. Every URL identifies a resource—which could be a document, an image, a user profile, or an abstract concept like "today's weather forecast." HTTP methods express the action you wish to perform on that resource.
This is fundamentally different from Remote Procedure Call (RPC) models, where you call functions with parameters. In HTTP:
| Method | Safe? | Idempotent? | Request Body | Response Body | Primary Use Case |
|---|---|---|---|---|---|
| GET | ✅ Yes | ✅ Yes | Not recommended | Yes | Retrieve resource representation |
| HEAD | ✅ Yes | ✅ Yes | Not recommended | No | Retrieve headers only (metadata) |
| POST | ❌ No | ❌ No | Yes | Yes | Submit data for processing |
| PUT | ❌ No | ✅ Yes | Yes | Optional | Replace resource entirely |
| PATCH | ❌ No | ❌ No* | Yes | Yes | Partial resource modification |
| DELETE | ❌ No | ✅ Yes | Optional | Optional | Remove resource |
| OPTIONS | ✅ Yes | ✅ Yes | No | Optional | Describe communication options |
| TRACE | ✅ Yes | ✅ Yes | No | Yes | Diagnostic loop-back test |
Safety and idempotency aren't just academic classifications—they have concrete infrastructure implications. Safe methods can be prefetched and cached aggressively. Idempotent methods can be automatically retried on network failures. Non-safe, non-idempotent methods require careful handling to avoid duplicate processing.
A method is safe if it does not cause any state change on the server.
When a client makes a safe request, it has reasonable confidence that no modification will occur on the server. This doesn't mean the server does nothing—it might log the request, update analytics counters, or trigger side effects. But from the client's perspective, the resource itself remains unchanged.
Why safety matters:
A method is idempotent if making the same request multiple times produces the same end state as making it once.
This is subtly different from "returns the same response." Consider deleting a resource:
The responses differ, but the final state is identical—the resource is deleted. That's idempotency.
Why idempotency matters:
The GET method is the workhorse of the World Wide Web. When you type a URL into your browser's address bar or click a hyperlink, you're issuing a GET request. It's so fundamental that many developers take it for granted—yet misunderstanding GET leads to security vulnerabilities, broken bookmarks, and non-cacheable applications.
The GET method requests transfer of a current selected representation for the target resource.
In plain language: GET asks the server for a copy of the resource identified by the URL.
| Property | Value | Implication |
|---|---|---|
| Safe | Yes | GET should never modify resource state |
| Idempotent | Yes | Multiple identical GETs produce same result |
| Cacheable | Yes | Responses can be cached by proxies and browsers |
| Request Body | Technically allowed but not recommended | Most servers ignore GET bodies; avoid using them |
| Response Body | Expected | Contains the resource representation |
1234567891011121314151617181920212223242526272829
GET /api/users/12345 HTTP/1.1Host: api.example.comAccept: application/jsonAccept-Language: en-US,en;q=0.9Accept-Encoding: gzip, deflate, brCache-Control: max-age=0If-None-Match: "etag-abc123"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... --- Server Response --- HTTP/1.1 200 OKContent-Type: application/json; charset=utf-8Content-Length: 256ETag: "etag-abc123"Cache-Control: max-age=3600, privateLast-Modified: Sat, 18 Jan 2025 12:00:00 GMTVary: Accept-Encoding, Accept-Language { "id": 12345, "username": "johndoe", "email": "john@example.com", "createdAt": "2024-01-15T10:30:00Z", "profile": { "displayName": "John Doe", "avatar": "/avatars/12345.png" }}Request Line: GET /api/users/12345 HTTP/1.1
Essential Headers:
Host: Required in HTTP/1.1; identifies the target domainAccept: Preferred content types (content negotiation)Accept-Encoding: Supported compression algorithmsIf-None-Match: ETag for conditional requests (caching)Authorization: Credentials for protected resourcesGET's safety and idempotency enable aggressive caching, which is HTTP's killer performance feature. Consider a typical web application:
/products/shoesCache-Control: max-age=3600Result: One database query serves thousands of users.
This only works because GET is safe. If GET could modify data, caches couldn't serve responses to different users—each might expect different modifications.
Never design endpoints where GET requests modify data. Examples of violations:
• GET /api/users/123/delete — Deletes user 123
• GET /api/checkout?complete=true — Processes payment
• GET /api/counter/increment — Increments a counter
Consequences: • Web crawlers will trigger unintended modifications • Browser prefetching causes data corruption • Caching becomes impossible • Users accidentally trigger actions by clicking links
GET requests commonly include query parameters for filtering, sorting, and pagination. These are appended to the URL after a ? character:
GET /api/products?category=electronics&sort=price&order=asc&page=2&limit=20
Parameter Types:
category=electronics, minPrice=100, inStock=truesort=price, order=asc|descpage=2, limit=20, or cursor-based: cursor=eyJpZCI6MTAwfQfields=id,name,price (reduce payload)include=reviews,manufacturer (embed related resources)While HTTP doesn't specify a URL length limit, practical constraints exist:
| Component | Typical Limit |
|---|---|
| Internet Explorer (legacy) | 2,083 characters |
| Modern Browsers | 64KB - 2MB |
| Apache/Nginx defaults | 8,192 characters |
| CDNs | Typically 8KB |
Best Practice: Keep URLs under 2,000 characters. If you need more complex queries, consider POST with a request body for query specification (though this breaks REST conventions).
The POST method is HTTP's general-purpose tool for submitting data to a server for processing. Unlike GET, POST is designed to be neither safe nor idempotent—it explicitly acknowledges that the request may modify server state and that repeating it may produce different results.
The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics.
This intentionally vague definition reflects POST's versatility. POST can:
| Property | Value | Implication |
|---|---|---|
| Safe | No | POST typically causes server-side effects |
| Idempotent | No | Repeating POST may create duplicate resources |
| Cacheable | Only if explicit headers specify | Requires Cache-Control or Expires |
| Request Body | Yes | Contains the data to process |
| Response Body | Usually | Contains operation result or created resource |
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
# Example 1: Creating a new resourcePOST /api/users HTTP/1.1Host: api.example.comContent-Type: application/jsonContent-Length: 156Accept: application/jsonAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... { "username": "janedoe", "email": "jane@example.com", "password": "securePassword123!", "profile": { "displayName": "Jane Doe" }} --- Server Response --- HTTP/1.1 201 CreatedLocation: /api/users/12346Content-Type: application/json { "id": 12346, "username": "janedoe", "email": "jane@example.com", "createdAt": "2025-01-18T12:15:00Z"} # Example 2: Form submissionPOST /login HTTP/1.1Host: www.example.comContent-Type: application/x-www-form-urlencodedContent-Length: 45 username=janedoe&password=securePassword123! # Example 3: File upload (multipart)POST /api/files HTTP/1.1Host: api.example.comContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW ------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="file"; filename="report.pdf"Content-Type: application/pdf (binary file content)------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="description" Monthly sales report------WebKitFormBoundary7MA4YWxkTrZu0gW--In RESTful APIs, POST is the standard method for creating new resources when the server determines the resource's identifier. Consider:
POST /api/articles
The client is saying: "Create a new article; you (the server) decide its ID."
The server responds with:
/api/articles/9876)Contrast with PUT: If the client knows the target ID, PUT is more appropriate:
PUT /api/articles/9876
POST's non-idempotency creates a classic problem: What happens if a network error occurs after the server processes the request, but before the client receives the response?
Client: POST /orders (create order)
↓ (request reaches server)
Server: Creates order #5001, sends 201 response
↓ (network drops response)
Client: Timeout! Did it work? Retry POST?
↓ (client retries)
Server: Creates order #5002 (duplicate!)
Solutions:
Idempotency Keys: Client sends a unique key with each request
POST /orders
Idempotency-Key: 7a3b2c1d-unique-uuid
Server stores processed keys; rejects duplicates with 409 Conflict.
Two-Phase Create:
Client-Generated IDs: Use PUT with client-generated UUID
PUT /orders/550e8400-e29b-41d4-a716-446655440000
Stripe's API requires idempotency keys for all POST requests involving charges. If a charge request times out, clients can safely retry with the same idempotency key—Stripe guarantees processing occurs exactly once. The key remains valid for 24 hours, after which you can reuse it for a different request.
This pattern should be standard for any API involving payments, orders, or irreversible operations.
POST requests must specify the format of the body data using the Content-Type header. Common values:
| Content-Type | Use Case | Example Body |
|---|---|---|
application/json | API communication | {"key": "value"} |
application/x-www-form-urlencoded | HTML form submission | key=value&key2=value2 |
multipart/form-data | File uploads | Binary data with boundaries |
text/plain | Simple text data | Plain text content |
application/xml | Legacy SOAP services | <root><key>value</key></root> |
| Status Code | Meaning | When to Use |
|---|---|---|
| 200 OK | Operation processed | The request was processed; response contains result |
| 201 Created | Resource created | New resource created; include Location header |
| 202 Accepted | Queued for processing | Async operations; processing will complete later |
| 204 No Content | Success, no body | Operation succeeded; nothing to return |
| 303 See Other | Redirect to result | After POST, redirect GET to result page (PRG pattern) |
The PUT method requests that the server replace the target resource with the enclosed representation. Unlike POST (which submits data for processing), PUT explicitly states the desired final state of a resource.
The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message content.
This definition highlights PUT's declarative semantics: the client is saying "make this resource look exactly like this."
| Property | Value | Implication |
|---|---|---|
| Safe | No | PUT modifies (or creates) the resource |
| Idempotent | Yes | Multiple identical PUTs produce the same result |
| Cacheable | No | PUT responses are not cacheable |
| Request Body | Yes | Contains the complete new resource state |
| Response Body | Optional | May return updated resource or be empty |
12345678910111213141516171819202122232425262728293031323334353637
PUT /api/users/12345 HTTP/1.1Host: api.example.comContent-Type: application/jsonContent-Length: 215Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...If-Match: "etag-abc123" { "id": 12345, "username": "johndoe", "email": "john.updated@example.com", "profile": { "displayName": "Jonathan Doe", "avatar": "/avatars/12345.png", "bio": "Software engineer passionate about distributed systems", "location": "San Francisco, CA" }} --- Server Response (Resource Updated) --- HTTP/1.1 200 OKContent-Type: application/jsonETag: "etag-xyz789" { "id": 12345, "username": "johndoe", "email": "john.updated@example.com", "updatedAt": "2025-01-18T12:30:00Z"} --- Server Response (Resource Created) --- HTTP/1.1 201 CreatedLocation: /api/users/12345ETag: "etag-new456"The difference between PUT and POST is fundamental to HTTP semantics but often confused:
POST: "Process this data according to your own rules"
PUT: "Make this resource look exactly like this"
Example: Creating a user
# POST: Server decides the ID
POST /api/users
{"username": "alice"}
→ 201 Created, Location: /api/users/789
# PUT: Client specifies the ID
PUT /api/users/alice-uuid-12345
{"username": "alice"}
→ 201 Created (if new) or 200 OK (if exists)
PUT's idempotency is its key advantage for reliability. Consider:
Client: PUT /articles/42 with new content
↓ (request reaches server)
Server: Updates article, sends 200 OK
↓ (network drops response)
Client: Timeout! Did it work? Safe to retry.
↓ (client retries)
Server: Updates article again with same content → 200 OK
The final state is identical whether PUT executed once or twice. This enables:
To prevent lost updates (two clients updating simultaneously, one overwrites the other), use conditional requests:
PUT /api/documents/42
If-Match: "version-etag-123"
The server only processes the PUT if the current ETag matches. If another client updated the document, the ETag changed, and the server returns:
HTTP/1.1 412 Precondition Failed
The client must then fetch the latest version, merge changes, and retry.
Many APIs incorrectly implement PUT as a partial update (only changing fields present in the request body). This violates HTTP semantics and causes problems:
• Clients can't reliably null out fields • No way to distinguish "remove this field" from "didn't send this field" • Caching intermediaries may get confused • OpenAPI/Swagger specifications become misleading
If you need partial updates, use PATCH instead. If you've already built PUT as partial update, document it clearly as non-standard.
The DELETE method requests that the origin server remove the association between the target resource and its current functionality. It's HTTP's explicit mechanism for resource removal.
The DELETE method requests that the origin server remove the association between the target resource and its current functionality.
Note that DELETE doesn't necessarily require physical deletion—it requests removal from the HTTP-accessible namespace.
| Property | Value | Implication |
|---|---|---|
| Safe | No | DELETE modifies server state |
| Idempotent | Yes | Deleting twice produces same end state |
| Cacheable | No | DELETE responses shouldn't be cached |
| Request Body | Technically allowed but rare | Most implementations ignore it |
| Response Body | Optional | May return confirmation or be empty |
123456789101112131415161718192021222324252627282930313233343536373839404142
# Basic DELETE requestDELETE /api/users/12345 HTTP/1.1Host: api.example.comAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... --- Response: Success, No Content --- HTTP/1.1 204 No ContentDate: Sat, 18 Jan 2025 12:45:00 GMT --- Response: Success with Confirmation --- HTTP/1.1 200 OKContent-Type: application/json { "deleted": true, "id": 12345, "deletedAt": "2025-01-18T12:45:00Z"} # Conditional DELETE (only if ETag matches)DELETE /api/documents/42 HTTP/1.1Host: api.example.comIf-Match: "etag-abc123" # DELETE already-deleted resourceDELETE /api/users/12345 HTTP/1.1Host: api.example.com --- Response: Idempotent Success (resource already gone) --- HTTP/1.1 404 Not FoundContent-Type: application/json { "error": "User not found", "code": "USER_NOT_FOUND"} # Alternative: 204 even for missing resource (stricter idempotency)HTTP/1.1 204 No ContentDELETE is idempotent because the end state is the same regardless of how many times you send the request: the resource is gone.
Initial State: Resource /users/123 exists
First DELETE:
→ Server removes /users/123
→ Response: 200 OK or 204 No Content
→ State: /users/123 does not exist
Second DELETE:
→ Server looks for /users/123, not found
→ Response: 404 Not Found (or 204 No Content)
→ State: /users/123 still does not exist
Same final state → Idempotent.
Design Choice: Should DELETE on a missing resource return 404 or 204?
| Approach | Response | Reasoning |
|---|---|---|
| 404 Not Found | "The resource you tried to delete doesn't exist" | Honest error; client knows resource is gone |
| 204 No Content | "Request processed; resource is not present" | Stricter idempotency; same response regardless |
Both are valid. 404 is more common; 204 provides cleaner retry semantics.
Many applications need to retain deleted data for auditing, recovery, or regulatory compliance. Two patterns:
Hard Delete: Resource is physically removed
DELETE FROM users WHERE id = 12345;
Soft Delete: Resource marked as deleted but retained
UPDATE users SET deleted_at = NOW() WHERE id = 12345;
For soft delete with visibility:
Alternatively, treat deletion status as a resource property and use PATCH/PUT to toggle it.
Deleting a resource may have implications for related resources:
Scenario: DELETE /api/departments/42
What happens to employees in department 42?
| Strategy | Behavior | Trade-offs |
|---|---|---|
| Cascade Delete | Also delete all employees | Clean but data loss |
| Nullify Foreign Key | Set employees' deptId to NULL | Orphaned records |
| Restrict | Reject if employees exist | Safe but inconvenient |
| Soft Delete Cascade | Soft-delete department and employees | Recoverable, complex |
API Design Consideration: The API should communicate what will happen:
# Option 1: Fail fast
DELETE /departments/42
→ 409 Conflict: "Cannot delete department with 15 assigned employees"
# Option 2: Explicit cascade
DELETE /departments/42?cascade=true
→ 200 OK: {"deleted": ["department:42", "employees:101-115"]}
# Option 3: Two-step
POST /departments/42/prepare-deletion
→ {"impact": {"employees": 15}, "confirmationToken": "abc123"}
DELETE /departments/42?confirmationToken=abc123
Selecting the correct HTTP method is a design decision with far-reaching implications. This section provides a decision framework for method selection.
| Scenario | Correct Method | Why Not Others |
|---|---|---|
| Retrieve a resource | GET | Only GET is safe and cacheable for retrieval |
| Create resource (server assigns ID) | POST | Server determines resource URI |
| Create resource (client knows ID) | PUT | Client specifies complete URI; idempotent |
| Replace resource entirely | PUT | Declarative: 'make it look like this' |
| Update specific fields only | PATCH | PUT requires complete representation |
| Delete a resource | DELETE | Explicit deletion semantics |
| Check if resource exists | HEAD | GET without body; efficient |
| Discover supported methods | OPTIONS | Returns Allow header with methods |
| Submit form data | POST | Form data modifies state |
| Idempotent state change | PUT or DELETE | Safely retriable |
Avoid these incorrect method usages:
• GET /api/users/123/delete — Use DELETE, not GET with action in URL
• POST /api/users/123/update — Use PUT or PATCH to the resource directly
• POST /api/search (with body) — Use GET with query params unless query is huge
• PUT /api/users (collection) — PUT targets a specific resource, not collections
• DELETE /api/users?ids=1,2,3 (bulk) — Consider POST to a batch endpoint instead
Safe? Idempotent? Cacheable?
GET ✅ Yes ✅ Yes ✅ Yes
HEAD ✅ Yes ✅ Yes ✅ Yes
OPTIONS ✅ Yes ✅ Yes ❌ No
TRACE ✅ Yes ✅ Yes ❌ No
POST ❌ No ❌ No ⚠️ If specified
PUT ❌ No ✅ Yes ❌ No
PATCH ❌ No ❌ No ❌ No
DELETE ❌ No ✅ Yes ❌ No
Key Insight: When you need a retriable, safe operation that causes no state change, you must use GET or HEAD. When you need a retriable operation that modifies state, you must use PUT or DELETE. POST should be reserved for operations that are truly non-idempotent (like creating resources with server-generated IDs or triggering complex workflows).
We've explored the four fundamental HTTP methods in depth. Let's consolidate the essential knowledge:
You now have a comprehensive understanding of HTTP's core methods: GET, POST, PUT, and DELETE. These methods form the vocabulary of RESTful APIs and web communication. In the next page, we'll explore HTTP headers—the metadata layer that enables content negotiation, caching, authentication, and more.