What Are the Main HTTP Methods Used in REST APIs?

What Are the Main HTTP Methods Used in REST APIs?

A ground-up, beginner-to-production walkthrough of GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS — what each verb really means, why the HTTP specification carved them out this way, how servers dispatch them internally, and how companies like Stripe, GitHub, Netflix, and Amazon S3 lean on these guarantees at scale to build cacheable, retry-safe, and secure APIs.

01

Introduction & History

Every time you open a website, tap “like” on a post, or update your profile picture, your browser or app is quietly firing off an HTTP request to a server somewhere in the world. Buried inside that request is a single word — GET, POST, PUT, PATCH, or DELETE — that tells the server exactly what kind of action you intend to perform. That word is called the HTTP method (or HTTP verb), and it is one of the oldest and most important ideas baked into the design of the web.

HTTP methods were not invented for REST APIs. They date back to 1991, when Tim Berners-Lee designed the first version of HTTP (HTTP/0.9) purely to fetch static documents from a server — there was only one method, an implicit GET. As the web grew, HTTP/1.0 (1996) and then HTTP/1.1 (1997, standardised in RFC 2068 and later RFC 2616) introduced a richer set of methods, because people wanted to do more than just read pages — they wanted to submit forms, upload files, and modify data on servers.

In 2000, Roy Fielding’s doctoral dissertation formalised an architectural style called REST (Representational State Transfer). Fielding, who had also co-authored the HTTP specification, observed that the existing HTTP methods already mapped beautifully onto the idea of manipulating “resources” (things like a user, an order, or a document) in a predictable, uniform way. REST didn’t invent new verbs — it gave the existing ones a disciplined meaning, and that discipline is what makes modern REST APIs predictable and easy to reason about.

i
Beginner Analogy

Think of HTTP methods like the verbs you’d use at a library counter: “Show me this book” (GET), “Add this new book to the shelf” (POST), “Replace this book entirely with a new edition” (PUT), “Just fix the torn page” (PATCH), and “Remove this book from the shelf” (DELETE). The librarian (server) understands your intent immediately just from the verb you use — you don’t need to write a paragraph explaining what you want.

It’s worth pausing on why this design choice mattered so much for the web’s growth. Before HTTP standardised a small, shared set of verbs, every network protocol tended to invent its own command vocabulary — FTP had commands like RETR and STOR, Gopher had its own menu-based navigation model, and countless proprietary client-server protocols each spoke a private dialect. HTTP’s designers made a deliberate bet: keep the verb vocabulary small, generic, and resource-oriented, and let the URL and the payload carry all the domain-specific meaning. That bet paid off enormously — it’s a big part of why a single piece of software, the web browser, can talk to virtually any web server on the planet without needing protocol-specific plugins for each site.

Today, the same small vocabulary — originally designed for fetching hypertext documents — powers everything from a weather app pulling a forecast, to a bank processing a funds transfer, to a self-driving car’s backend logging telemetry data. Understanding these methods deeply is therefore not a narrow technical detail; it’s foundational literacy for anyone building or consuming APIs.

1.1 A short timeline

YearMilestone
1991HTTP/0.9 released — a single implicit GET method, used only to fetch HTML documents.
1996HTTP/1.0 (RFC 1945) formally documents GET, POST, and HEAD.
1997HTTP/1.1 (RFC 2068, later RFC 2616) adds PUT, DELETE, OPTIONS, TRACE, and CONNECT.
2000Roy Fielding’s dissertation formalises REST, giving these methods disciplined, resource-oriented meaning.
2010RFC 5789 formally standardises PATCH as a dedicated partial-update method.
2014RFC 7231 consolidates and clarifies HTTP semantics, including precise definitions of safety and idempotency.
02

The Problem & Motivation

Imagine a world without standardised HTTP methods. Every API would need to invent its own way of saying “create a user” or “delete an order.” One team might build an endpoint called /createUser, another might use /user/new, another might use /addUser. A developer integrating with ten different services would need to learn ten different vocabularies. This is exactly the chaos that existed with many RPC-style (Remote Procedure Call) APIs before REST became popular.

HTTP methods solve this by separating what resource you’re acting on (the URL, e.g. /users/42) from what action you want to perform on it (the method, e.g. DELETE). This separation gives us:

  • Predictability — any developer who understands HTTP already knows that DELETE /users/42 removes user 42, without reading custom documentation.
  • Tooling support — browsers, caches, proxies, load balancers, and API gateways can all make intelligent decisions (like caching a GET response) purely by looking at the method, without understanding your business logic.
  • Safety guarantees — some methods promise not to change anything on the server, so tools can retry them freely, prefetch them, or crawl them without fear of side effects.
i
Real-World Motivation

Google’s web crawler can safely visit millions of pages using GET requests without worrying that it might accidentally delete data or place an order — because the HTTP specification guarantees GET is “safe.” That single guarantee, honoured by billions of servers worldwide, is what makes the entire web crawlable and cacheable.

There’s a second, subtler motivation too: reducing the cognitive load of API design itself. When a backend team sits down to design a new endpoint, they don’t need to invent a new verb from scratch and argue about naming conventions in a code review — they simply ask “is this a read, a create, a full update, a partial update, or a delete?” and the method falls out naturally. This constraint-driven design is why REST APIs, despite covering wildly different domains (payments, social media, IoT device management), tend to feel structurally familiar to any experienced developer picking them up for the first time.

Contrast this with the RPC-style alternative, where an API might expose endpoints like /api/doThing, /api/processRequest, or /api/handleUserAction. These names carry no inherent guarantee about safety or idempotency — a caller has to read documentation (or worse, source code) to know whether it’s safe to retry after a timeout. Multiply that uncertainty across dozens of endpoints and multiple teams, and you get systems that are fragile under network failure and expensive to onboard new engineers onto.

03

Core Concepts

Before looking at individual methods, you need to understand three foundational properties that the HTTP specification uses to classify every method. These three words — safe, idempotent, and cacheable — appear constantly in API design discussions, so let’s define each one from scratch. Once these three properties click into place, you’ll find that almost every practical decision about method choice, retry logic, caching strategy, and even security policy becomes a straightforward application of these ideas rather than something you need to memorise case by case.

3.1 Safe methods

What it means

A method is “safe” if calling it never changes the state of the server. It’s a read-only operation.

Why it matters: safe methods can be called by crawlers, monitoring tools, and browsers (like link prefetching) without any risk.

Analogy: reading a book in the library is safe — the book is unchanged no matter how many times you read it or how many people read it simultaneously.

Example: GET /products/101 just fetches product details; it doesn’t modify anything.

3.2 Idempotent methods

What it means

A method is “idempotent” if calling it once has the same effect on server state as calling it many times in a row.

Why it matters: if a network request times out and you don’t know whether it succeeded, an idempotent method is safe to simply retry — the end result will be identical either way.

Analogy: setting your alarm clock to 7:00 AM is idempotent — whether you set it once or five times, the alarm is still 7:00 AM at the end. Contrast this with “snooze the alarm by 5 minutes,” which is not idempotent, because repeating it keeps changing the outcome.

Example: PUT /users/42 with a full user object is idempotent — sending it twice leaves the user in the same final state as sending it once. POST /orders is typically not idempotent — calling it twice creates two separate orders.

3.3 Cacheable methods

What it means

A response is “cacheable” if it can be stored by a browser, proxy, or CDN and reused for a future identical request, instead of hitting the origin server again.

Why it matters: caching dramatically improves performance and reduces server load.

Analogy: photocopying a public notice board and handing out copies to people who ask, instead of walking them to the board every single time.

Example: GET /articles/99 responses are commonly cached with headers like Cache-Control.

HTTP Method Classification — Safe, Idempotent, Neither HTTP Method any of GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS Changes server state? (safety check) Safe methods no observable side effects GET · HEAD · OPTIONS Same result if repeated? (idempotency check) Idempotent safe to retry blindly PUT · DELETE Not idempotent retries risk duplicates POST · PATCH* No Yes Yes No *PATCH can be idempotent when it sets absolute values
Figure 1 — A simple two-question decision tree that classifies every HTTP method into safe, idempotent, or neither.
MethodSafe?Idempotent?Typically Cacheable?
GETYesYesYes
HEADYesYesYes
OPTIONSYesYesNo
POSTNoNoRarely (only if explicitly marked)
PUTNoYesNo
PATCHNoNo (in general)No
DELETENoYesNo

3.4 Methods and status codes go together

A method is only half the story — the HTTP status code returned in the response tells the client how the operation actually went. Well-designed APIs pair specific methods with specific expected status codes, and deviating from these conventions is a common source of confusion for API consumers.

MethodTypical Success CodeMeaning
GET200 OKResource found and returned in the body.
POST201 CreatedNew resource created; response often includes a Location header pointing to it.
PUT200 OK / 204 No ContentResource replaced; 204 if no body is returned.
PATCH200 OKResource partially updated, updated representation returned.
DELETE204 No ContentResource removed; nothing further to send back.

When something goes wrong, the method still matters for choosing the right error code — for example, calling PUT on a path where you lack permission should return 403 Forbidden, while calling any method with malformed request syntax returns 400 Bad Request, and calling a method that a resource simply doesn’t support (say, DELETE on a read-only audit-log endpoint) should return 405 Method Not Allowed along with an Allow header listing the methods that are actually supported.

3.5 Content negotiation and the request body

Methods that carry a request body (POST, PUT, PATCH) rely on the Content-Type header to tell the server how to parse that body — commonly application/json for modern REST APIs, though application/x-www-form-urlencoded and multipart/form-data remain common for traditional HTML forms and file uploads. On the response side, the Accept header lets a client ask for a specific representation format (JSON, XML, or even a custom vendor-specific media type), which is especially relevant for GET and POST responses in APIs that support multiple output formats for different classes of clients.

04

Architecture & Components

An HTTP method is not a standalone concept — it’s one piece of a larger structure called an HTTP request. Understanding where the method sits helps you understand how a server actually reads it.

PUT /api/v1/users/42 HTTP/1.1          <-- Request line: METHOD, path, HTTP version
Host: api.gauravsinghtech.com
Authorization: Bearer eyJhbGciOi...
Content-Type: application/json
Content-Length: 58

{"name": "Gaurav Sharma", "email": "g@gauravsinghtech.com"}

Here’s what each component does:

  • Method (PUT) — declares the intended action.
  • Request-URI / path (/api/v1/users/42) — identifies the resource being acted upon.
  • Headers — metadata like authentication tokens, content type, and caching hints.
  • Body — the actual payload, present for methods like POST, PUT, and PATCH; typically absent for GET and DELETE.

Architecturally, the method is the single field that every layer in the request path inspects: the browser, any corporate proxy, the load balancer, the API gateway, the web server (like Nginx), the application framework (like Spring Boot), and finally your controller code. Each layer can make routing or security decisions based purely on this one word — which is exactly why REST’s uniform interface constraint is so powerful.

i
Layer-by-layer

Because the method sits in the first line of every request, even a completely opaque intermediary that doesn’t understand your business domain (a caching proxy, a WAF, a rate-limiter) can still make correct decisions — cache the GET, throttle the POST, alert on the DELETE — without ever peeking at the URL or the body.

05

Internal Working — How a Server Actually Reads the Method

Let’s demystify what happens between “the method arrives over the wire” and “your business logic runs.” Using a Java Spring Boot application as an example (a stack commonly used in production REST services):

@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    @GetMapping("/{id}")
    public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
        // Safe, idempotent - just reads and returns
        return ResponseEntity.ok(userService.findById(id));
    }

    @PostMapping
    public ResponseEntity<UserDto> createUser(@RequestBody CreateUserRequest req) {
        // Not idempotent - creates a brand-new resource each call
        UserDto created = userService.create(req);
        return ResponseEntity.status(HttpStatus.CREATED).body(created);
    }

    @PutMapping("/{id}")
    public ResponseEntity<UserDto> replaceUser(@PathVariable Long id,
                                                 @RequestBody UserDto fullUser) {
        // Idempotent - replaces the entire resource
        return ResponseEntity.ok(userService.replace(id, fullUser));
    }

    @PatchMapping("/{id}")
    public ResponseEntity<UserDto> updateUser(@PathVariable Long id,
                                                @RequestBody Map<String, Object> fields) {
        // Partial update - only changes the given fields
        return ResponseEntity.ok(userService.applyPatch(id, fields));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

When a request hits your server, here’s the internal sequence:

  1. 1. Parse the request line

    The web server (e.g. embedded Tomcat inside Spring Boot) parses the raw TCP bytes and extracts the request line, splitting out the method, path, and HTTP version.

  2. 2. Hand off to the dispatcher

    The method and path are handed to the framework’s dispatcher (in Spring, the DispatcherServlet), which consults a routing table mapping (method, path pattern) pairs to controller methods.

  3. 3. Resolve the annotation shortcut

    Annotations like @GetMapping or @PostMapping are really just shorthand for @RequestMapping(method = RequestMethod.GET) — under the hood, Spring builds a lookup structure so that dispatching is close to O(1) for exact matches.

  4. 4. Match or reject

    If no controller method matches both the path and the method, the framework returns 404 Not Found (no path match) or 405 Method Not Allowed (path matches, but not that method).

  5. 5. Execute business logic

    The matched controller method executes your business logic and produces a response.

i
Why This Matters

The 405 status code exists precisely because the router first finds the resource by URL, then checks whether the method you used is permitted on it. This two-step check is a direct architectural consequence of separating “resource” from “action.”

5.1 Filters, interceptors, and the method-aware pipeline

Before your controller method even runs, most frameworks pass the request through a chain of filters or interceptors — and many of these components branch their behaviour based on the method. In Spring, a typical filter chain looks like this:

public class AuditLogFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                     HttpServletResponse response,
                                     FilterChain chain) throws IOException, ServletException {

        String method = request.getMethod();

        // Only audit-log mutating operations - GET traffic is far too
        // high-volume to log at this level of detail in most systems.
        boolean isMutating = method.equals("POST") || method.equals("PUT")
                           || method.equals("PATCH") || method.equals("DELETE");

        if (isMutating) {
            auditLogger.record(request.getRequestURI(), method,
                                SecurityContextHolder.getContext().getAuthentication());
        }
        chain.doFilter(request, response);
    }
}

This kind of method-aware branching is extremely common: authentication filters may skip token validation for OPTIONS preflight requests (since browsers don’t attach auth headers to those), rate-limiting filters may apply a much stricter quota to POST/PUT/DELETE than to GET, and caching filters may short-circuit entirely for any non-GET request.

5.2 What happens on a mismatch

It’s worth tracing exactly what the framework does when a client sends a method the server doesn’t expect. Using Spring Boot as the example again: the DispatcherServlet first attempts to find any handler mapping matching the URL pattern. If it finds one or more matches for the path but none for the specific method, it throws a HttpRequestMethodNotSupportedException, which Spring’s default exception handling converts into a 405 Method Not Allowed response, automatically populating the Allow header with the list of methods that are registered for that path — so a well-behaved client (or a curious developer using curl) can immediately see what’s permitted without guessing.

06

Data Flow & Lifecycle of a Request

Let’s trace a full request lifecycle, from a mobile app tapping “Save” to the database being updated.

Data Flow — PATCH Request Lifecycle From Client To Database Client API Gateway Load Balancer Spring Service Cache Database 1. PATCH /users/42 {“email”:”new@x.com”} 2. auth check · rate limiting 3. forward request 4. route to healthy instance 5. invalidate cached user:42 6. UPDATE users SET email=? WHERE id=42 7. row updated 8. 200 OK + updated user JSON 9. 200 OK 10. 200 OK returned to caller Every hop can branch on the method — the gateway throttles writes harder, the service invalidates cache because PATCH is not safe, and the DB performs a targeted UPDATE rather than a full row replace.
Figure 2 — A single PATCH request travels through the API gateway, load balancer, service, cache invalidation, and database, and finally the 200 OK returns along the same chain.

Notice how the method (PATCH) influences behaviour at every hop: the gateway may apply stricter rate limits to write methods than to GET; the service layer knows it must invalidate cache because this isn’t a safe method; and the database performs a targeted UPDATE rather than a full row replace, mirroring the “partial update” semantics of PATCH.

07

The Methods, One by One

Now let’s go through each method in depth — what it means, a beginner example, a software example, and a production example from a real system.

GET

Retrieve a resource or collection. Safe, idempotent, cacheable.

POST

Create a new resource, or trigger a non-idempotent action.

PUT

Replace a resource entirely with the given representation.

PATCH

Apply a partial modification to a resource.

DELETE

Remove a resource.

HEAD

Like GET, but returns only headers, no body.

OPTIONS

Discover which methods and headers a resource supports.

7.1 GET

Retrieve a resource without altering it

What: retrieves a representation of a resource without altering it. Why: it’s the default action of the web — clicking a link, loading a page, fetching a list.

Beginner example: typing a URL into a browser sends a GET request.

Software example: GET /api/v1/products?category=shoes&page=2 fetches page 2 of shoes.

Production example: Amazon’s product listing pages are backed by GET requests that are aggressively cached at CDN edge nodes, so millions of shoppers can browse without every request reaching Amazon’s origin servers.

7.2 POST

Submit data to be processed – usually to create

What: submits data to be processed, most commonly to create a new resource whose final URL is decided by the server. Why: you often don’t know the ID of a resource before it’s created — the server assigns it.

Beginner example: filling out a signup form and clicking “Register.”

Software example: POST /api/v1/orders with a cart payload creates a new order and returns its generated order ID.

Production example: Stripe’s POST /v1/charges endpoint creates a new payment charge — and because POST is not idempotent by default, Stripe layers an optional Idempotency-Key header on top so merchants can safely retry a charge request after a network timeout without double-charging a customer.

7.3 PUT

Replace a resource in its entirety

What: replaces a resource in its entirety with the payload you send. If a field is omitted from the payload, PUT typically clears it. Why: useful when the client has the complete, authoritative state of a resource and wants the server to match it exactly.

Beginner example: re-saving your entire profile form, including fields you didn’t change.

Software example: PUT /api/v1/documents/config.json uploading a full configuration file, overwriting whatever existed before.

Production example: Amazon S3’s object PUT API (PUT /bucket/key) replaces the object at that key entirely — there’s no concept of “partially updating” an S3 object, which is a textbook real-world application of true PUT semantics.

7.4 PATCH

Apply a partial set of changes

What: applies a partial set of changes to a resource, leaving unspecified fields untouched. Why: sending the entire resource just to change one field is wasteful and risks accidentally overwriting fields you didn’t intend to touch (especially in concurrent-editing scenarios).

Beginner example: changing only your phone number in a settings screen, without resubmitting your name and address.

Software example: PATCH /api/v1/users/42 with body {"phone": "9876543210"}.

Production example: GitHub’s REST API uses PATCH extensively — for instance, PATCH /repos/{owner}/{repo}/issues/{issue_number} lets you close an issue or change its title without resending the entire issue body, labels, and assignees.

7.5 DELETE

Remove the specified resource

What: removes the specified resource. Why: a dedicated, unambiguous verb for destructive operations, distinct from updates.

Beginner example: tapping the trash icon on a note-taking app.

Software example: DELETE /api/v1/carts/9/items/3 removes item 3 from cart 9.

Production example: Twitter/X’s API exposes DELETE /2/tweets/:id to delete a tweet — idempotent by design, since calling it a second time on an already-deleted tweet simply returns a “not found” rather than an error about repeated deletion.

7.6 HEAD

Identical to GET, but headers only

What: identical to GET, but the server returns only the headers, never a response body. Why: lets a client check things like content length, last-modified date, or whether a resource exists, without downloading it.

Beginner example: a download manager checking a file’s size before starting the download.

Software example: HEAD /files/report.pdf to check Content-Length before deciding whether to fetch it over a slow connection.

Production example: CDNs and monitoring tools like Pingdom often use HEAD requests for uptime checks, since they confirm a URL is reachable without wasting bandwidth on the full payload.

7.7 OPTIONS

Discover what a resource supports

What: asks the server which HTTP methods and headers are permitted on a given resource, without performing any action. Why: browsers use this automatically as a “preflight” check for cross-origin (CORS) requests.

Beginner example: you never trigger this manually — your browser does it silently before certain cross-site API calls.

Software example: a single-page app hosted on app.gauravsinghtech.com calling api.gauravsinghtech.com triggers an automatic OPTIONS preflight request to check if the actual PATCH request will be allowed.

Production example: every modern JavaScript framework (React, Angular, Vue) making cross-origin API calls relies on the browser’s automatic OPTIONS preflight mechanism to enforce CORS security policies before the “real” request is sent.

!
Lesser-Known Methods

Two rarely-used but standard methods are TRACE (echoes back the received request for diagnostic loop-back testing — commonly disabled in production for security reasons, since it can expose header data) and CONNECT (used to establish a tunnel, typically for HTTPS traffic through a proxy). REST APIs almost never expose these directly.

08

Pros, Cons & Trade-offs

Choosing the right method is often a design decision with real trade-offs, not just a matter of convention. The most common point of confusion is PUT vs PATCH.

PUT — Full Replace

  • Pros: simple mental model; client fully controls final state; naturally idempotent.
  • Cons: wasteful bandwidth for large resources when changing one field; risk of accidentally clearing fields the client “forgot” to include.

PATCH — Partial Update

  • Pros: bandwidth-efficient; safer for concurrent edits (less chance of clobbering another user’s change).
  • Cons: semantics aren’t strictly defined by the base spec (RFC 5789 leaves the patch document format open — e.g., JSON Merge Patch vs JSON Patch), which can lead to inconsistent implementations across APIs; generally not idempotent unless carefully designed.

Similarly, teams debate POST vs PUT for creation: use POST when the server assigns the resource ID (e.g., auto-incrementing database IDs); use PUT when the client decides the ID upfront (e.g., PUT /users/my-custom-username where the client names the resource).

DecisionChoose POST when…Choose PUT when…
Creating a resourceServer generates the ID/URLClient already knows the target URL
Retry safety neededRequires extra idempotency keyNaturally safe to retry
Triggering an action (e.g. “send email”)Correct choice — actions aren’t resourcesNot appropriate

8.1 The two flavours of PATCH

When teams do adopt PATCH, they usually pick between two standardised formats, and the choice has real trade-offs:

JSON Merge Patch (RFC 7396)

  • You send a partial JSON object; any key present overwrites the server’s value, and a key set to null deletes it.
  • Simple and intuitive, but can’t easily express operations like “insert into the middle of an array” or “delete this specific array element.”

JSON Patch (RFC 6902)

  • You send an explicit array of operations like {"op":"replace","path":"/email","value":"g@gauravsinghtech.com"}.
  • Far more expressive — it can target nested fields and array indices precisely — but noticeably more verbose and harder for API consumers to construct by hand.

Most public APIs favour JSON Merge Patch for its simplicity unless they specifically need array-level surgical precision, in which case JSON Patch is worth the added complexity.

8.2 Actions that don’t fit cleanly into CRUD

Not every operation maps neatly onto Create/Read/Update/Delete. What HTTP method should you use to “approve” a purchase order, “restart” a server, or “send” a password-reset email? The pragmatic REST convention is to model the action itself as a sub-resource and create it with POST — for example, POST /orders/88/approval or POST /servers/12/restarts. This keeps you within the uniform interface while still expressing operations that don’t naturally read as “replace” or “delete.” It’s a small but important escape hatch that keeps REST practical for real-world domains that are richer than simple record-keeping.

09

Performance & Scalability

HTTP methods have a direct, measurable impact on how a system scales. Because GET is safe and (usually) cacheable, it’s the method that benefits most from horizontal scaling tricks:

  • Read replicas — GET-heavy workloads can be routed to database read replicas, since they never modify data, freeing the primary database for writes.
  • Edge caching / CDNs — GET responses with proper Cache-Control headers can be served from edge locations close to the user, cutting latency from hundreds of milliseconds to single digits.
  • Connection reuse — because idempotent methods (GET, PUT, DELETE) are safe to retry, HTTP client libraries and load balancers can aggressively reuse or fail over connections without complex compensating logic.

Non-idempotent methods like POST require more careful scaling strategies — for example, using message queues to decouple the API layer from slow downstream processing, or requiring idempotency keys (as Stripe does) to make retries safe even though the raw method isn’t idempotent by specification.

i
Production Example

Netflix’s API gateway (Zuul) applies different scaling and circuit-breaker policies for GET-heavy “browse and discover” traffic versus POST/PUT-heavy “playback progress update” traffic, because the two have completely different retry-safety and caching characteristics.

Connection pooling in application servers (like the HikariCP pool commonly used with Spring Boot for database connections, or an HTTP client’s own connection pool for outbound calls) also benefits from method predictability. Because GET requests are read-only, a connection pool can route them to a larger pool of lightweight, read-only database connections — often pointed at replicas — while reserving a smaller, carefully-managed pool of write-capable connections for POST/PUT/PATCH/DELETE, reducing contention and improving overall throughput under load.

Batching is another performance technique closely tied to method choice. Some APIs expose a bulk endpoint like POST /users/batch-get specifically because a pure REST GET can’t easily carry a large list of IDs in a URL (URLs have practical length limits, often around 2000 characters in many browsers and proxies). This is a deliberate, documented exception to REST purity, made for a measurable performance reason — a good example of how production systems balance architectural idealism against real-world constraints.

10

High Availability & Reliability

Idempotency is the backbone of reliable distributed systems. In an unreliable network, a client can never be 100% sure whether a request reached the server before a timeout occurred. This is where the properties from Section 3 become operationally critical.

If a client calls DELETE /orders/88 and the connection drops before the response arrives, it’s perfectly safe to retry — worst case, the second call gets a 404 because the order is already gone, and no harm is done. But if that same client had called a non-idempotent POST /orders and retried blindly, it might create a duplicate order.

This is why production-grade systems that need to accept POST-based “create” calls in unreliable environments (e.g., payment processing, order placement) implement idempotency keys: the client generates a unique key per logical operation, and the server stores the result of the first request against that key, returning the cached result for any duplicate.

@PostMapping("/orders")
public ResponseEntity<OrderDto> createOrder(
        @RequestHeader("Idempotency-Key") String idempotencyKey,
        @RequestBody CreateOrderRequest req) {

    Optional<OrderDto> existing = idempotencyStore.find(idempotencyKey);
    if (existing.isPresent()) {
        return ResponseEntity.ok(existing.get()); // Safe replay
    }
    OrderDto order = orderService.create(req);
    idempotencyStore.save(idempotencyKey, order);
    return ResponseEntity.status(HttpStatus.CREATED).body(order);
}

Load balancers and service meshes also lean on method idempotency when deciding whether to automatically retry a failed request on a different backend instance. A well-configured Envoy or Istio proxy, for example, can be told to retry GET, PUT, and DELETE requests automatically on connection errors, since a retry can’t cause harm beyond what a single successful call would — but the same proxy is typically configured to never auto-retry a POST without an idempotency key present, precisely because a blind retry could create a duplicate resource. This single design decision, replicated across thousands of services in a large microservices deployment, is one of the primary reasons idempotent methods are so heavily favoured wherever business logic allows it.

Disaster recovery planning follows the same logic at a larger scale: when a data centre fails over to a backup region, in-flight requests are lost and clients typically retry automatically. Systems that predominantly rely on idempotent methods recover cleanly from this kind of failover with minimal data-integrity risk, while systems built heavily around non-idempotent POST calls without compensating idempotency keys often need extra reconciliation logic (like nightly deduplication jobs) to clean up any duplicate records created during the failover window.

11

Security

HTTP methods play a central role in API security design:

  • Method-based authorisation — it’s common to require a stricter role for DELETE and PUT than for GET. A “viewer” role might only be permitted GET requests, while an “admin” role gets full CRUD access.
  • CSRF (Cross-Site Request Forgery) — because browsers automatically attach cookies to requests, state-changing methods (POST, PUT, PATCH, DELETE) are the primary target of CSRF attacks; safe methods like GET should never be used to trigger state changes, precisely so they remain trustworthy to click or prefetch.
  • Method Override attacks — some older systems support a X-HTTP-Method-Override header to simulate PUT/DELETE from clients that can only send GET/POST (e.g., old HTML forms). If misconfigured, this can let an attacker bypass method-based firewall rules that only inspect the literal HTTP method.
  • TRACE method risk — as mentioned earlier, TRACE can be exploited in “Cross-Site Tracing” (XST) attacks to read HTTP-only cookies; it should be disabled on production web servers.
  • WAF and API gateway rules — Web Application Firewalls commonly apply stricter rate limiting and payload inspection to POST/PUT/PATCH bodies than to GET requests, since write paths carry higher risk.
!
Common Mistake

Using GET requests to perform destructive actions (e.g., GET /users/42/delete) is a serious anti-pattern — it breaks the safety contract of GET, meaning a web crawler, browser prefetcher, or antivirus link-scanner could accidentally trigger a deletion just by visiting the URL.

A real historical example of this exact mistake: early versions of some web-based admin panels implemented “delete” links as plain <a href="/delete?id=5"> tags. Because Internet Explorer’s link-prefetching feature and various corporate web-scanning proxies would pre-fetch every link on a page to check for malware, entire rows of production data were sometimes wiped out simply by an administrator opening a page — a now-famous cautionary tale in web development circles, and one of the clearest illustrations of why the “safe method” guarantee exists in the first place.

Rate limiting is another area where method matters. It’s common to configure far tighter per-minute quotas on POST, PUT, and DELETE than on GET, since abuse of write endpoints (e.g., mass account creation, spam posting, or bulk deletion) tends to be more damaging than abuse of read endpoints. Some API gateways also apply distinct authentication requirements per method — for instance, allowing anonymous GET access to public product listings while requiring a signed-in session token for any POST, PUT, PATCH, or DELETE on the same resource path.

12

Monitoring, Logging & Metrics

HTTP method is one of the most useful dimensions for slicing observability data. Most APM tools (Datadog, New Relic, Prometheus + Grafana) let you break down metrics by method:

  • Latency percentiles per method — GET requests are typically expected to be fast (tens of milliseconds); POST/PUT involving database writes are naturally slower, so alert thresholds should differ per method.
  • Error rate per method — a spike in 5xx errors specifically on DELETE calls might point to a cascading failure in a cleanup job, distinct from a broader outage.
  • Structured logging — access logs (e.g., Nginx or Spring’s logging filter) record the method alongside path, status code, and latency for every request, which is the raw material for building dashboards.
// Example structured log line
{"timestamp":"2026-07-19T10:22:01Z","method":"PATCH","path":"/api/v1/users/42",
 "status":200,"latencyMs":38,"traceId":"9f2a-11e0-88b3"}

Correlation IDs (or trace IDs) are typically generated once per request and propagated across microservices regardless of method, but the method itself is almost always one of the first fields captured in distributed tracing spans (e.g., in OpenTelemetry, the http.method attribute is a standard semantic convention).

When designing alerting rules, it’s worth setting separate thresholds and paging policies per method category. A sudden 10x increase in GET traffic might simply mean a marketing campaign went viral — annoying for capacity planning, but not necessarily an emergency. The same 10x increase concentrated on POST or DELETE calls to a payments or account-deletion endpoint is far more likely to indicate abuse, a bug in a client retry loop, or a security incident, and typically warrants immediate escalation. Mature observability setups define these thresholds explicitly per method-and-path combination rather than using a single blanket “requests per second” alert for the whole service.

Dashboards built for engineering leadership often break down a service’s request volume as a stacked chart by method over time — this single view can reveal usage pattern shifts (for example, a growing share of PATCH calls might indicate clients are migrating away from older, bandwidth-heavy PUT-based update flows) well before those shifts show up in any other metric.

13

Deployment & Cloud

In cloud environments, HTTP methods influence how infrastructure is configured:

  • API Gateways (AWS API Gateway, Kong, Apigee) let you define routes per method-and-path combination, applying different throttling limits, caching policies, and authorisers to GET versus POST/PUT/DELETE on the same resource path.
  • Load Balancers can be configured with health checks that use lightweight GET or HEAD requests (e.g., GET /health) so they don’t waste resources or accidentally trigger state changes while checking instance health.
  • CDN configuration (CloudFront, Cloudflare, Fastly) typically caches GET (and sometimes HEAD) responses only, while passing POST/PUT/PATCH/DELETE straight through to the origin, since caching a write operation makes no sense.
  • Blue-green and canary deployments often route read traffic (GET) to the new version first, since it’s lower-risk, before gradually shifting write traffic once confidence is established.
  • Infrastructure as code — tools like Terraform or AWS CDK let teams declare method-and-path routing rules for gateways and load balancers as version-controlled configuration, so changes to which methods are exposed publicly go through the same review process as application code changes.
  • Serverless functions — platforms like AWS Lambda behind API Gateway, or Google Cloud Functions, commonly bind one function per method-and-path combination, meaning the HTTP method literally determines which isolated unit of compute handles a request, with independent scaling and cold-start characteristics for GET versus POST/PUT/DELETE handlers on the same resource.

A practical operational detail worth knowing: many managed API gateways charge or throttle differently based on request volume by method category, since write-heavy traffic often triggers more expensive downstream processing (database writes, event publishing, third-party API calls) than read-heavy traffic. This makes method-level traffic analysis a genuine cost-optimisation exercise, not just an academic one.

14

Databases, Caching & Load Balancing

There’s a natural mapping between HTTP methods and database operations, often summarised as the CRUD-to-HTTP mapping:

HTTP MethodTypical DB OperationSQL Analogy
GETReadSELECT
POSTCreateINSERT
PUTFull Update / UpsertUPDATE (all columns) or REPLACE
PATCHPartial UpdateUPDATE ... SET col=val (specific columns)
DELETEDeleteDELETE FROM ... WHERE

This mapping is a convention, not a hard rule — many teams implement PUT as an “upsert” (create if missing, replace if it exists) since that’s often more useful in distributed systems where clients may retry a create after an ambiguous failure. Because GET dominates read caching, systems often place a cache layer (Redis, Memcached, or an in-process cache) directly in front of the database specifically for GET-served endpoints, and that cache is explicitly invalidated whenever a PUT, PATCH, or DELETE modifies the underlying row — a pattern called cache invalidation on write.

15

APIs & Microservices

In a microservices architecture, HTTP methods provide a shared vocabulary across teams that might otherwise design wildly inconsistent APIs. When Team A’s Order Service and Team B’s Inventory Service both follow REST conventions, a new engineer can predict how to interact with either service without reading extensive documentation.

API gateways in microservices architectures route based on method as well as path — for instance, a gateway might route all GET traffic for /products/* to a read-optimised replica cluster of the Product Service, while routing POST/PUT/DELETE to the primary write cluster, implementing a lightweight form of CQRS (Command Query Responsibility Segregation) purely through method-based routing rules.

Method semantics also matter for eventual consistency: a PUT or PATCH in one service might publish an event that several other services (search index, cache, analytics) consume asynchronously. Because PUT and DELETE are idempotent, downstream consumers can safely process the same event twice (which happens often in “at-least-once” delivery message queues like Kafka or SQS) without corrupting data.

The Saga pattern, commonly used to coordinate multi-step transactions across microservices without a shared database, also leans on method semantics. Each step in a saga typically maps to a REST call on a participating service (e.g., POST /inventory/reserve, POST /payments/charge), and each step’s compensating action — the operation that undoes it if a later step fails — is designed to be safely retriable, usually implemented as an idempotent PUT or DELETE (e.g., DELETE /inventory/reservations/{id} to release a reservation). Saga orchestrators depend heavily on this retry-safety, since network partitions between services are a routine occurrence in distributed systems, not an edge case.

16

Design Patterns & Anti-Patterns

16.1 Good patterns

  • Upsert via PUT — allow PUT to create a resource at a client-specified ID if it doesn’t exist yet, keeping full idempotency.
  • JSON Merge Patch (RFC 7396) — a standardised, predictable format for PATCH bodies, where any field present in the JSON overwrites the corresponding field on the server, and null explicitly deletes a field.
  • Idempotency keys for POST — as shown earlier, this lets you gain retry-safety even on a fundamentally non-idempotent method.

16.2 Anti-patterns

  • “Verb tunnelling” — routing every operation through POST regardless of semantics (e.g., POST /getUser, POST /deleteUser). This is essentially RPC wearing a REST costume, and it throws away all the benefits of caching, safety, and idempotency guarantees.
  • Using GET for state changes — as discussed in the security section, this is dangerous and violates the HTTP spec’s safety contract.
  • Inconsistent PATCH semantics — some teams implement PATCH as “replace the whole resource” (i.e., actually behaving like PUT), confusing API consumers who expect standard partial-update behaviour.
  • Overusing 200 OK for everything — pairing methods with the wrong status codes (e.g., returning 200 instead of 201 Created for a successful POST) reduces the self-descriptiveness that makes REST useful.
17

Best Practices & Common Mistakes

  • Match the method to its true semantics — don’t use POST just because it’s the “safe default” for anything you’re unsure about.
  • Return the correct status code for each method: 200 for successful GET/PUT/PATCH, 201 for successful POST that creates a resource, 204 for successful DELETE with no body, 405 when a method isn’t supported on a path.
  • Document idempotency guarantees explicitly in your API docs — don’t make consumers guess whether it’s safe to retry.
  • Use PATCH with a well-known format (JSON Merge Patch or JSON Patch, RFC 6902) rather than inventing your own partial-update convention.
  • Never expose TRACE or CONNECT on public-facing REST APIs.
  • Apply consistent authorisation rules per method across your entire API surface, not endpoint-by-endpoint on an ad-hoc basis — inconsistency here is a common source of security bugs.
  • Version your API (e.g., /api/v1/) so you can evolve method behaviour over time without breaking existing clients.

17.1 A practical testing checklist

Because method semantics carry real guarantees, it’s worth explicitly testing for them rather than assuming your implementation honours them. A solid test suite for any REST endpoint should verify:

  • Idempotency test — call PUT or DELETE twice in a row and assert the final server state and response are equivalent both times.
  • Safety test — call GET repeatedly and assert no observable side effects occur (no row counts changing, no emails sent).
  • 405 behaviour — call an unsupported method on a valid path and assert you get 405 with a correctly populated Allow header, not a generic 500 error.
  • Partial update correctness — for PATCH, assert that fields not included in the request body remain unchanged after the call.
  • Concurrent write test — simulate two PATCH calls racing on the same resource and confirm the outcome matches your documented conflict-resolution behaviour (last-write-wins, optimistic locking via ETag/If-Match, or an explicit conflict error).
@Test
void putIsIdempotent() {
    UserDto payload = new UserDto("Gaurav", "g@gauravsinghtech.com");

    ResponseEntity<UserDto> first  = restTemplate.exchange(
        "/api/v1/users/42", HttpMethod.PUT, new HttpEntity<>(payload), UserDto.class);
    ResponseEntity<UserDto> second = restTemplate.exchange(
        "/api/v1/users/42", HttpMethod.PUT, new HttpEntity<>(payload), UserDto.class);

    assertEquals(first.getBody(), second.getBody());
    assertEquals(HttpStatus.OK, second.getStatusCode());
}
18

Real-World / Industry Examples

Stripe
Uses POST for nearly all mutating operations paired with mandatory idempotency keys for payment safety, GET for retrieving resources, and DELETE for detaching payment methods or cancelling subscriptions.
GitHub
A textbook example of REST method usage — GET to list/read repos and issues, POST to create issues/PRs, PATCH to update issue state or PR details, DELETE to remove repos or comments.
Netflix
Heavily read-optimised (GET-dominant) API traffic for browsing/discovery, with POST/PUT calls concentrated around playback progress, ratings, and “my list” management — architected with different scaling tiers per method type.
Amazon S3
A near-perfect real-world mapping of REST semantics onto object storage: GET retrieves an object, PUT writes/overwrites it completely, DELETE removes it, and HEAD checks metadata like size and last-modified date without downloading the object body — no PATCH exists because S3 objects are treated as opaque blobs, not partially-editable records.
Slack
Slack’s Web API mixes REST conventions with some RPC-style method naming for historical reasons, but its newer endpoints follow standard verbs — POST to post a message, and dedicated update/delete endpoints that mirror PUT/DELETE semantics even where the literal HTTP method used is POST, illustrating how legacy API design decisions can persist for years after being made.

These examples highlight an important lesson: even large, sophisticated engineering organisations don’t always apply HTTP methods with textbook purity. Real APIs evolve under deadline pressure, backward-compatibility constraints, and client limitations (like old mobile app versions that can’t be forced to upgrade). Understanding the “ideal” REST mapping described throughout this guide gives you the vocabulary to recognise when and why a real system deviates from it — and to make informed trade-offs in your own designs rather than accidentally reinventing the same inconsistencies.

19

FAQ

Is PATCH always non-idempotent?

Not strictly — a PATCH that sets a field to an absolute value (e.g., “set status to CLOSED”) is idempotent, while a PATCH that expresses a relative change (e.g., “increment counter by 1”) is not. The HTTP spec doesn’t mandate idempotency for PATCH, so it depends entirely on how your API defines the patch semantics.

Can GET requests have a body?

Technically the HTTP specification doesn’t forbid it, but it’s strongly discouraged in practice — many servers, proxies, and caching layers ignore or strip GET bodies, so parameters should be passed via the query string instead.

What’s the difference between 200 and 204 for DELETE?

200 OK is returned when the response includes a body (for example, confirmation details); 204 No Content is returned when the deletion succeeded and there’s nothing further to send back — this is the more common convention for DELETE.

Why does my browser send an OPTIONS request I never asked for?

This is the CORS preflight mechanism — browsers automatically send OPTIONS before certain cross-origin requests (like PATCH with a JSON body) to confirm the server permits it, protecting users from unauthorised cross-site actions.

Why do some APIs only support POST and GET, ignoring PUT/PATCH/DELETE entirely?

Historically, plain HTML forms could only submit GET or POST — browsers didn’t support PUT, PATCH, or DELETE directly from a form element. Some older systems and certain corporate firewalls/proxies also block or mishandle the less common methods. To work around this, many frameworks support a method-override convention, where the client sends a POST request with a hidden field or header like X-HTTP-Method-Override: DELETE, and the server internally treats it as the overridden method. Modern JavaScript-based clients using fetch or XMLHttpRequest don’t have this limitation, so the workaround is increasingly rare in new systems.

Is it ever acceptable to put a body on a DELETE request?

The HTTP specification doesn’t forbid a body on DELETE, but it’s uncommon and many servers, proxies, and libraries either ignore it or handle it inconsistently. If you need to pass extra data for a delete operation (for example, a reason code), it’s generally more interoperable to use query parameters or a dedicated header instead of relying on a request body.

How do idempotency keys differ from idempotent methods?

An idempotent method (like PUT or DELETE) is idempotent by definition in the HTTP specification — no extra work is required. An idempotency key is an application-level pattern layered on top of an inherently non-idempotent method (like POST) to manually recreate the same retry-safety guarantee, typically by having the server remember the outcome of the first request under that key and replay it for duplicates.

What’s the difference between PATCH and PUT when only one field changes?

If you send PUT with only one field, a strictly spec-compliant server will treat the missing fields as “not present” and may clear them, potentially destroying data. PATCH, when implemented correctly, changes only the fields you explicitly include and leaves everything else untouched — this is precisely the scenario PATCH was introduced to handle cleanly.

20

Summary & Key Takeaways

  • HTTP methods are the verbs of the web — GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS each carry a specific, standardised meaning.
  • Three core properties — safe, idempotent, cacheable — determine how each method can be used, retried, and optimised by infrastructure.
  • REST didn’t invent these methods; it gave them disciplined, resource-oriented meaning, which is what makes modern APIs predictable across teams and companies.
  • Real production systems (Stripe, GitHub, Netflix, S3) lean heavily on these guarantees for caching, scaling, security, and reliable retries.
  • Choosing the right method — and honouring its safety and idempotency contract — is one of the highest-leverage decisions in API design.
i
Key Takeaway

When in doubt, ask: “Does this change server state?” (safe or not) and “If I repeat this exact call, does the outcome change?” (idempotent or not). Those two questions will correctly guide you to GET, POST, PUT, PATCH, or DELETE in almost every real-world API design decision.

As you design your own APIs, resist the temptation to treat HTTP methods as mere labels attached to endpoints after the fact. Every architectural benefit covered in this guide — caching, safe crawling, retry-safe clients, method-aware rate limiting, clean CRUD-to-database mapping, and predictable authorisation rules — flows directly from choosing methods that genuinely honour their documented semantics. A small amount of discipline at design time, deciding honestly whether an operation is safe, idempotent, and correctly scoped to a resource, pays for itself many times over in the reliability, security, and maintainability of the system you build on top of it.

What to remember

  • The seven core methods each declare a specific intent that every layer of the stack (browser, proxy, load balancer, service, database) can act on without reading your business logic.
  • Safe methods can be freely retried, prefetched, and crawled. Idempotent methods can be freely retried by clients, load balancers, and service meshes. Neither guarantee is optional if you want the corresponding infrastructure benefit.
  • POST covers everything CRUD can’t model cleanly (actions, jobs, resources whose ID is server-assigned) but should be paired with an idempotency key whenever the caller might retry.
  • PUT replaces, PATCH partially updates. Mixing them up is one of the most common REST design mistakes and one of the easiest to avoid once you know the distinction.
  • Status codes are half the contract. 201 for a successful POST that creates, 204 for a successful DELETE with no body, and 405 with an Allow header when a method isn’t supported are the boring-but-critical details that make your API predictable.
  • Method-aware infrastructure (caches, WAFs, gateways, service meshes) rewards you for staying inside the standard. Verb-tunnelling through POST throws those rewards away.
http methods rest api get post put patch delete head options idempotency idempotency key safe methods cacheable cors preflight rfc 7231 rfc 5789 rfc 7396 rfc 6902 json merge patch json patch stripe github api netflix zuul amazon s3 spring boot cqrs saga pattern api gateway rate limiting