On idempotence in HTTP

Today I came across a blogpost arguing idempotence of the HTTP DELETE method, and how this works in practice.

The blogpost itself is rather confusing, as it corrects and changes itself in a few edits, but the last conclusion as of 11am today is as follows:

When perfoming a HTTP DELETE method on a resource that succeeds, and afterwards performing the exact same HTTP DELETE method again, the HTTP status code should not change.

This conclusion is incorrect, and frankly it was suprising to see that both on that blog, as well as the reddit thread people were confused about the correct answer here.

Hence this post.

HTTP and Idempotence

The following HTTP methods are all considered idempotent:

  • PUT
  • DELETE
  • GET
  • HEAD
  • OPTIONS
  • TRACE

Httpbis defines idempotence as follows:

Idempotent methods are distinguished because the request can be repeated automatically if a communication failure occurs before the client is able to read the server’s response. For example, if a client sends a PUT request and the underlying connection is closed before any response is received, then it can establish a new connection and retry the idempotent request because it knows that repeating the request will have the same effect even if the original request succeeded. Note, however, that repeated failures would indicate a problem within the server.

To be fair, this definitition alone is not entirely clear on wether the response to subsequent requests needs to be identical.

However, this can be inferred from other parts of the specification.

A much clearer example of an idempotent request with varying responses is PUT. See for example the following (simplified) sequence of requests.

PUT /resource HTTP/1.1
If-Match: "1111-1111"
HTTP/1.1 200 OK
ETag: "2222-2222"
PUT /resource HTTP/1.1
If-Match: "1111-1111"
HTTP/1.1 412 Precondition Failed
ETag: "2222-2222"

As you can see the second request, although identical, resulted in a different HTTP response. This is the only correct response.

A server can not remember if a particular client made the request before, because of the state-less nature of HTTP.

This behavior also extends to DELETE. DELETE with an If-Match header must return 412 Precondition Failed if the resource was already deleted.

If no If-Match header was provided, the server must return 404 Not Found, or in some situations 410 Gone if the server kept track of deleted resources.

Accoding to Nicoon on reddit, this was even incorrectly stated in O’Reilly’s RESTful Web Services Cookbook. An excerpt:

“The DELETE method is idempotent. This implies that the server must return response code 200 (OK) even if the server deleted the resource in a previous”

source

I guess this is just a subtle reminder that beceause it uses dead trees as medium, it doesn’t necessarily it’s correct ;).

The book implies with that statement that a REST server must actually keep track of every previously deleted resource, just so it can return a 2xx status code and pretend that the DELETE succeeded again. This is absolutely false.

tl;dr

When a HTTP method is idempotent, this means that when you do a HTTP request once, the outcome is the same as performing the identical HTTP request more than once.

Outcome in this sentence does not refer to the literal HTTP response, which is usually different anyway as most HTTP servers send back a Date: header.

Outcome refers to the state of the resource on the server. If I do the same DELETE more than once, the first DELETE that comes through will return a 2xx status code, and any subsequent DELETE will return a 4xx status code because the resource has already been removed.

On HTTP 200 vs 204

This was also addressed in the blog post, and I felt it was worth explaining the difference here too.

204 No Content is often used as the standard response to a successful DELETE. However, 200 OK is also a perfectly valid response code.

The only different between 200 OK and 204 No Content is that with 200 some response body is expected, and with 204 No Content no response is allowed to be sent back.

So it is also perfectly acceptable to send back a 204 as a response to a PUT or POST request. There’s also nothing in the specification that seems to argue against the idea of using a 204 as a response to a GET request, As long as the Content-Length of the response is 0.

Web mentions

Comments

  • Anna Filina

    I agree with your point. It's actually easy to argue this with the following 3 requests (assume PUT is for "modify" operations):
    PUT /resource/1
    DELETE /resource/1
    PUT /resource/1
    It would make absolutely no sense in the 3rd request to send a 200 OK, because the resource is clearly not available.

    • Evert

      Evert

      Bit of an odd example though, because PUT is not strictly for updates, it's also used for creation of new resources. So the last PUT could also return a 201.

      • Anna Filina

        It's not strictly for updates. I just used that scenario as an example to further support the point.

      • gggeek

        Strongly agree. We should use PATCH for updates, and only use PUT for create :-)

        • Anna Filina

          Support for PATCH is still not widespread. I need to support some very old tech in most of my projects, so that's usually not an option.

        • willdurand

          PATCH is tricky to use as you should send a **diff**, not just a set of key/values to update. Actually, a better alternative would be to PUT specific parts of your resource (e.g. PUT /resources/1/price)

          • gggeek

            I am not against using sub-urls for parts of the resource, but it poses a problem wrt caching reverse proxies: when I publish a new version of resource 1, if I allow usage of sub-urls even for GETs (which is name for smaller payloads/faster interfaces) I am now forced to tell to the reverse proxy to purge resources/1/* as well as resources/1 - and not all of them might be smart enough to support that

            • willdurand

              Use "sub-urls" for write only, not read.

      • VK

        Don't we use POST for creation of new resource ? PUT or PATCH for updates. The difference is that PUT request has the complete resource with modifications and PATCH with only parts of the resource that has modifications.

        • Evert

          Evert

          POST should only be used in cases where the client can not, or is not allowed to determine the url of the new resource

  • Marten Gajda

    This behavior also extends to DELETE. DELETE with an If-Match header must
    return 412 Precondition Failed if the resource was already deleted.


    This doesn't seem to be true any longer. https://tools.ietf.org/html... explicitly states that a server is allowed to return a 2xx response if the outcome would be the current state on the server:


    the origin server MUST respond with either a) the 412 (Precondition Failed) status code
    or b) one of the 2xx (Successful) status codes if the origin server
    has verified that a state change is being requested and the final
    state is already reflected in the current state of the target resource


    So if you request a DELETE on a non-existing resource, the server can actually return 200 OK.
    The same would be true if you PUT the same resource twice.

    • Evert

      Evert

      That's super interesting! I had no idea that this was the case. I really like this