RFC 7232

Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests

πŸ“… published June 2014, from ietf

Title Block πŸŽ€

okay bestie, this one is RFC 7232, and it's called Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests πŸ’… It's a Standards Track doc, so it's an actual Internet standard and not just a vibe. It came out in June 2014 and has ISSN 2070-1721. The editors are R. Fielding (Adobe) and J. Reschke (greenbytes), both marked Ed. for editor. This RFC obsoletes RFC 2616, which means it replaces that older one. No updates are listed in the title block.

Abstract 🌸

HTTP is a stateless application-level protocol built for distributed, collaborative, hypertext information systems ✨ This document defines conditional requests for HTTP/1.1. That covers metadata header fields that show state changes, request header fields that set preconditions on that state, and the rules for building responses when one or more of those preconditions evaluates to false πŸ’–

Status of This Memo πŸ“œ

This is an Internet Standards Track document πŸ¦‹

It's a product of the Internet Engineering Task Force (IETF) and represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). If you want more info on Internet Standards, check out Section 2 of RFC 5741 🧚

For the current status, any errata, and how to send feedback, go to http://www.rfc-editor.org/info/rfc7232 πŸ”—

Copyright (c) 2014 IETF Trust and the people listed as document authors. All rights reserved 🌈

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info), as they stood on the publication date. Read them carefully, because they spell out your rights and restrictions for this doc. Code Components pulled from this document have to include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as the Simplified BSD License describes πŸ“

Heads up: some of this material comes from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The people who control the copyright in some of that material may not have given the IETF Trust permission to allow modifications outside the IETF Standards Process. Without an adequate license from those copyright holders, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process. The one exceptions are formatting it for publication as an RFC and translating it into languages other than English πŸ™ˆ

Table of Contents πŸ“š

   1. Introduction ....................................................4
      1.1. Conformance and Error Handling .............................4
      1.2. Syntax Notation ............................................4
   2. Validators ......................................................5
      2.1. Weak versus Strong .........................................5
      2.2. Last-Modified ..............................................7
           2.2.1. Generation ..........................................7
           2.2.2. Comparison ..........................................8
      2.3. ETag .......................................................9
           2.3.1. Generation .........................................10
           2.3.2. Comparison .........................................10
           2.3.3. Example: Entity-Tags Varying on
                  Content-Negotiated Resources .......................11
      2.4. When to Use Entity-Tags and Last-Modified Dates ...........12
   3. Precondition Header Fields .....................................13
      3.1. If-Match ..................................................13
      3.2. If-None-Match .............................................14
      3.3. If-Modified-Since .........................................16
      3.4. If-Unmodified-Since .......................................17
      3.5. If-Range ..................................................18
   4. Status Code Definitions ........................................18
      4.1. 304 Not Modified ..........................................18
      4.2. 412 Precondition Failed ...................................19
   5. Evaluation .....................................................19
   6. Precedence .....................................................20
   7. IANA Considerations ............................................22
      7.1. Status Code Registration ..................................22
      7.2. Header Field Registration .................................22
   8. Security Considerations ........................................22
   9. Acknowledgments ................................................23
   10. References ....................................................24
      10.1. Normative References .....................................24
      10.2. Informative References ...................................24
   Appendix A. Changes from RFC 2616 .................................25
   Appendix B. Imported ABNF .........................................25
   Appendix C. Collected ABNF ........................................26
   Index .............................................................27

1. Introduction ✨

okay so conditional requests are HTTP requests [RFC7231] that come with one or more header fields saying "check this precondition first" 🫢 The precondition gets tested before the method's semantics are applied to the target resource. This doc defines the HTTP/1.1 conditional request mechanisms using the architecture, syntax notation, and conformance criteria from [RFC7230] πŸ’…

Conditional GET requests are the most efficient mechanism for HTTP cache updates [RFC7234] πŸ”₯ Conditionals can also go on state-changing methods like PUT and DELETE, which stops the "lost update" problem. That's when one client accidentally overwrites the work of another client that was working in parallel 😭

Conditional request preconditions are based on either the state of the target resource as a whole (its current value set) or the state as seen in a representation you got earlier (one value in that set) πŸ¦‹ A resource can have multiple current representations, and each one can have its own observable state. The conditional request mechanisms assume that the mapping from requests to a "selected representation" (Section 3 of [RFC7231]) stays consistent over time, if the server wants to use conditionals. Still, if that mapping is inconsistent and the server can't pick the right representation, nothing bad happens when the precondition evaluates to false πŸ™ˆ

The conditional request preconditions this spec defines (Section 3) get evaluated when they apply to the recipient (Section 5), following their order of precedence (Section 6) πŸ‘‘

1.1. Conformance and Error Handling πŸŽ€

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119] 🧚

Conformance criteria and the considerations for error handling are defined in Section 2.5 of [RFC7230] πŸ’–

1.2. Syntax Notation πŸ“

This spec uses the Augmented Backus-Naur Form (ABNF) notation from [RFC5234] with a list extension, which is defined in Section 7 of [RFC7230] 🌸 That extension lets you define comma-separated lists compactly using the # operator, which is kind of like how * means repetition. Appendix B describes rules imported from other documents. Appendix C shows the full grammar with all the list operators expanded into standard ABNF notation ✨

2. Validators πŸ’…

This spec defines two forms of metadata that people commonly use to observe resource state and test for preconditions: modification dates (Section 2.2) and opaque entity tags (Section 2.3) 🫢 Other metadata that reflects resource state has been defined by various HTTP extensions, like Web Distributed Authoring and Versioning (WebDAV, [RFC4918]). Those are outside the scope of this spec 🌈 When a resource metadata value gets used inside a precondition, it's called a "validator" πŸ”₯

2.1. Weak versus Strong πŸ’…

Okay so validators come in two flavors: strong or weak πŸŽ€ Weak ones are super easy to generate but kinda useless for comparing stuff, while strong ones are ideal for comparisons but can be really hard (sometimes literally impossible) to make efficiently 😭 So instead of forcing every resource to use the same strength, HTTP tells you which type is in use and sets rules for when a weak one is allowed to act as a precondition 🫢

A "strong validator" is representation metadata that flips its value whenever the representation data changes in a way that would show up in the payload body of a 200 (OK) response to GET ✨

A strong validator can also change for other reasons, like when a semantically important bit of the representation metadata gets updated (Content-Type, for example). Still, it's really in the origin server's best interest to only change the value when it's actually needed to invalidate the stored responses sitting in remote caches and authoring tools 🧚

Cache entries can hang around for arbitrarily long periods, no matter what their expiration times say πŸ™ˆ So a cache might try to validate an entry using a validator it got way back in the distant past. A strong validator is unique across all versions of all representations of a particular resource over time πŸ’œ But there's no uniqueness promise across representations of different resources. The same strong validator could be used for representations of multiple resources at the same time, and that does NOT mean those representations are equivalent πŸ‘€

There are a bunch of strong validators in the wild. The best ones come from strict revision control, where every change to a representation gets a unique node name and revision identifier before that representation becomes available to GET πŸ”₯ A collision-resistant hash function applied to the representation data also works, as long as the data is available before the response header fields get sent and the digest doesn't need to be recalculated every time a validation request comes in ✨ Now, if a resource has distinct representations that differ only in their metadata (which can happen with content negotiation over media types that share the same data format), then the origin server needs to fold extra information into the validator so those representations can be told apart 🌈

On the flip side, a "weak validator" is representation metadata that might not change for every change to the representation data πŸ¦‹ That weakness can come from limits in how the value is calculated (like clock resolution), from not being able to guarantee uniqueness across every possible representation, or from the resource owner wanting to group representations into a self-chosen set of equivalence instead of tracking every unique sequence of data πŸ“ An origin server SHOULD change a weak entity-tag whenever it decides prior representations aren't acceptable as a stand-in for the current one. Basically, a weak entity-tag ought to change whenever the origin server wants caches to throw out old responses 🫠

For example, picture a weather report whose content changes every second based on live measurements. The origin server might group those into sets of equivalent representations that all share one weak validator, so cached copies stay valid for a reasonable stretch of time (which could even be adjusted on the fly based on server load or weather quality) 🌸 Similarly, a representation's modification time, if it's only defined to one-second resolution, might be a weak validator. That's because the representation could be modified twice within a single second and get retrieved between those two changes 😭

Also, a validator is weak if two or more representations of a given resource share it at the same time, unless those representations have identical representation data πŸ’… For instance, if the origin server sends the same validator for a representation with a gzip content coding applied as it does for one with no content coding, then that validator is weak. Two simultaneous representations can share a strong validator, though, if they only differ in representation metadata, like when two different media types are available for the same representation data πŸŽ€

Strong validators work for all conditional requests, including cache validation, partial content ranges, and "lost update" avoidance πŸ† Weak validators are only usable when the client doesn't need exact equality with previously obtained representation data, such as when validating a cache entry or limiting a web traversal to recent changes 🫢


2.2. Last-Modified πŸ—“οΈ

The "Last-Modified" header field in a response gives a timestamp for the date and time the origin server believes the selected representation was last modified, as determined at the end of handling the request ⏰

Last-Modified = HTTP-date

Here's an example of how it's used:

Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT

2.2.1. Generation ✨

An origin server SHOULD send Last-Modified for any selected representation whose last modification date can be reasonably and consistently determined. Why? Because using it in conditional requests and when evaluating cache freshness ([RFC7234]) can cut down HTTP traffic on the Internet by a lot, and it can really help with service scalability and reliability πŸš€

A representation is usually the sum of many parts sitting behind the resource interface 🧩 The last-modified time would normally be the most recent time any of those parts changed. How that value gets figured out for a given resource is an implementation detail and is outside the scope of this specification πŸ€·β€β™€οΈ What matters to HTTP is how recipients of the Last-Modified header field can use its value to make conditional requests and check whether a locally cached response is still valid πŸ’–

An origin server SHOULD grab the Last-Modified value of the representation as close as possible to the moment it generates the Date field value for its response. This lets a recipient make an accurate assessment of the representation's modification time, especially if the representation changes right around the time the response is generated ⏱️

An origin server with a clock MUST NOT send a Last-Modified date that's later than the server's time of message origination (Date). If the last modification time comes from implementation-specific metadata that evaluates to some time in the future (according to the origin server's clock), then the origin server MUST replace that value with the message origination date. This way, a future modification date can't mess up cache validation πŸ™…β€β™€οΈ

An origin server without a clock MUST NOT assign Last-Modified values to a response, unless those values were associated with the resource by some other system or user that has a reliable clock ⏳

2.2.2. Comparison πŸ”

A Last-Modified time, when it's used as a validator in a request, is implicitly weak UNLESS it's possible to deduce that it's strong. Here are the rules for that πŸ‘€

  • The validator is being compared by an origin server to the actual current validator for the representation, and
  • That origin server reliably knows that the associated representation did not change twice during the second covered by the presented validator.

or

  • The validator is about to be used by a client in an If-Modified-Since, If-Unmodified-Since, or If-Range header field, because the client has a cache entry for the associated representation, and
  • That cache entry includes a Date value, which gives the time when the origin server sent the original response, and
  • The presented Last-Modified time is at least 60 seconds before the Date value.

or

  • The validator is being compared by an intermediate cache to the validator stored in its cache entry for the representation, and
  • That cache entry includes a Date value, which gives the time when the origin server sent the original response, and
  • The presented Last-Modified time is at least 60 seconds before the Date value.

This method relies on a neat fact: if two different responses were sent by the origin server during the same second but both had the same Last-Modified time, then at least one of those responses would have a Date value equal to its Last-Modified time 🧠 The arbitrary 60-second limit is a safety buffer for the case where the Date and Last-Modified values come from different clocks, or get generated at slightly different times while the response is being prepared πŸ›‘οΈ An implementation MAY use a value larger than 60 seconds, if it believes 60 seconds is too short πŸ’œ


2.3. ETag 🏷️

The "ETag" header field in a response gives the current entity-tag for the selected representation, as determined at the end of handling the request 🎯 An entity-tag is an opaque validator for telling apart multiple representations of the same resource, whether those representations exist because the resource's state changed over time, because content negotiation makes several representations valid at once, or because of both 🌈 An entity-tag is an opaque quoted string, possibly with a weakness indicator stuck on the front πŸŽ€

ETag       = entity-tag

entity-tag = [ weak ] opaque-tag
weak       = %x57.2F ; "W/", case-sensitive
opaque-tag = DQUOTE *etagc DQUOTE
etagc      = %x21 / %x23-7E / obs-text
           ; VCHAR except double quotes, plus obs-text
Note: Previously, opaque-tag was defined to be a quoted-string ([RFC2616], Section 3.11), so some recipients might do backslash unescaping. Servers therefore ought to avoid backslash characters in entity tags. πŸ™ˆ

An entity-tag can be more reliable for validation than a modification date in situations where it's inconvenient to store modification dates, where the one-second resolution of HTTP date values isn't enough, or where modification dates aren't consistently maintained πŸ“…

Examples:

ETag: "xyzzy"
ETag: W/"xyzzy"
ETag: ""

An entity-tag can be either a weak or a strong validator, and strong is the default πŸ’ͺ If an origin server provides an entity-tag for a representation, and the way that entity-tag was generated doesn't satisfy all the characteristics of a strong validator (Section 2.1), then the origin server MUST mark the entity-tag as weak by prefixing its opaque value with "W/" (case-sensitive). That's the rule, bestie πŸ’–

2.3.1. Generation πŸŽ€

okay so the whole point of entity-tags is that only the service author really knows their resource inside out, so they're the one who can pick the most accurate and efficient way to check for changes πŸ’…. Whatever method they choose gets boiled down to a simple sequence of octets so it's easy to compare ✨. Since the value is opaque, the client doesn't need to know how each entity-tag was built 🫢.

For example, a resource with implementation-specific versioning on every change might use an internal revision number, maybe paired with a variance identifier for content negotiation, to tell representations apart πŸ¦‹. Other implementations might go with a collision-resistant hash of the representation content, a mix of various file attributes, or a modification timestamp with sub-second resolution 🌸.

An origin server SHOULD send an ETag for any selected representation for which detection of changes can be reasonably and consistently determined. The reason is that entity-tags used in conditional requests and when evaluating cache freshness ([RFC7234]) can cut HTTP network traffic down a lot, and that's a huge deal for service scalability and reliability πŸ”₯.

2.3.2. Comparison πŸ’–

There are two entity-tag comparison functions, and which one you use depends on whether the comparison context allows weak validators πŸ‘‘:

  • Strong comparison: two entity-tags are equivalent if both are not weak and their opaque-tags match character-by-character.
  • Weak comparison: two entity-tags are equivalent if their opaque-tags match character-by-character, regardless of either or both being tagged as "weak".

Here's a little cheat sheet showing what both functions say for a set of entity-tag pairs πŸ“:

+--------+--------+-------------------+-----------------+
| ETag 1 | ETag 2 | Strong Comparison | Weak Comparison |
+--------+--------+-------------------+-----------------+
| W/"1"  | W/"1"  | no match          | match           |
| W/"1"  | W/"2"  | no match          | no match        |
| W/"1"  | "1"    | no match          | match           |
| "1"    | "1"    | match             | match           |
+--------+--------+-------------------+-----------------+

2.3.3. Example: Entity-Tags Varying on Content-Negotiated Resources 🌈

Picture a resource that gets content negotiation (Section 3.4 of [RFC7231]), where the representations sent back for a GET request vary based on the Accept-Encoding request header field (Section 5.3.4 of [RFC7231]) πŸ’œ:

>> Request:

  GET /index HTTP/1.1
  Host: www.example.com
  Accept-Encoding: gzip

In this case, the response might or might not use the gzip content coding. If it doesn't, the response might look like this 🧚:

>> Response:

  HTTP/1.1 200 OK
  Date: Fri, 26 Mar 2010 00:05:00 GMT
  ETag: "123-a"
  Content-Length: 70
  Vary: Accept-Encoding
  Content-Type: text/plain

  Hello World!
  Hello World!
  Hello World!
  Hello World!
  Hello World!

And an alternative representation that does use gzip content coding would be this one πŸŽ€:

>> Response:

  HTTP/1.1 200 OK
  Date: Fri, 26 Mar 2010 00:05:00 GMT
  ETag: "123-b"
  Content-Length: 43
  Vary: Accept-Encoding
  Content-Type: text/plain
  Content-Encoding: gzip

  ...binary data...
Note: content codings are a property of the representation data, so a strong entity-tag for a content-encoded representation has to be different from the entity tag of an unencoded representation. That keeps things from getting mixed up during cache updates and range requests πŸ™ˆ. By contrast, transfer codings (Section 4 of [RFC7230]) only apply during message transfer and don't result in distinct entity-tags ✨.

2.4. When to Use Entity-Tags and Last-Modified Dates πŸ¦‹

In 200 (OK) responses to GET or HEAD, here's what an origin server does πŸ’…:

  • SHOULD send an entity-tag validator unless it is not feasible to generate one.
  • MAY send a weak entity-tag instead of a strong entity-tag, if performance considerations support the use of weak entity-tags, or if it is unfeasible to send a strong entity-tag.
  • SHOULD send a Last-Modified value if it is feasible to send one.

Put simply, the preferred behavior for an origin server is to send both a strong entity-tag and a Last-Modified value in successful responses to a retrieval request πŸ’–.

And here's what a client does 🫢:

  • MUST send that entity-tag in any cache validation request (using If-Match or If-None-Match) if an entity-tag has been provided by the origin server.
  • SHOULD send the Last-Modified value in non-subrange cache validation requests (using If-Modified-Since) if only a Last-Modified value has been provided by the origin server.
  • MAY send the Last-Modified value in subrange cache validation requests (using If-Unmodified-Since) if only a Last-Modified value has been provided by an HTTP/1.0 origin server. The user agent SHOULD provide a way to disable this, in case of difficulty.
  • SHOULD send both validators in cache validation requests if both an entity-tag and a Last-Modified value have been provided by the origin server. This lets both HTTP/1.0 and HTTP/1.1 caches respond appropriately 🌸.

3. Precondition Header Fields πŸ”₯

This section defines the syntax and semantics of HTTP/1.1 header fields for applying preconditions on requests. Section 5 defines when the preconditions are applied, and Section 6 defines the order of evaluation when more than one precondition is present 🧚.

3.1. If-Match πŸ’Œ

The "If-Match" header field makes the request method conditional on the recipient origin server either having at least one current representation of the target resource, when the field-value is "*", or having a current representation of the target resource that has an entity-tag matching a member of the list of entity-tags provided in the field-value πŸ‘‘.

An origin server MUST use the strong comparison function when comparing entity-tags for If-Match (Section 2.3.2), since the client intends this precondition to prevent the method from being applied if there have been any changes to the representation data πŸ”’.

  If-Match = "*" / 1#entity-tag

Examples:

  If-Match: "xyzzy"
  If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
  If-Match: *

If-Match is most often used with state-changing methods (e.g., POST, PUT, DELETE) to prevent accidental overwrites when multiple user agents might be acting in parallel on the same resource. In other words, it helps prevent the "lost update" problem 😭. It can also be used with safe methods to abort a request if the selected representation doesn't match one already stored (or partially stored) from a prior request πŸ“.

An origin server that receives an If-Match header field MUST evaluate the condition prior to performing the method (Section 5). If the field-value is "*", the condition is false if the origin server does not have a current representation for the target resource. If the field-value is a list of entity-tags, the condition is false if none of the listed tags match the entity-tag of the selected representation πŸ”.

An origin server MUST NOT perform the requested method if a received If-Match condition evaluates to false. Instead, 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. That covers the case where the change the user agent asked for has already succeeded, but the user agent might not know it, perhaps because the prior response was lost or a compatible change was made by some other user agent πŸ¦‹. In that latter case, the origin server MUST NOT send a validator header field in the response unless it can verify that the request is a duplicate of an immediately prior change made by the same user agent πŸ™ˆ.

The If-Match header field can be ignored by caches and intermediaries, because it isn't applicable to a stored response πŸ’….

3.2. If-None-Match 🧚

The "If-None-Match" header field makes the request method conditional on a recipient cache or origin server either not having any current representation of the target resource, when the field-value is "*", or having a selected representation with an entity-tag that does not match any of those listed in the field-value πŸ’–.

A recipient MUST use the weak comparison function when comparing entity-tags for If-None-Match (Section 2.3.2), since weak entity-tags can be used for cache validation even if there have been changes to the representation data 🌈.

  If-None-Match = "*" / 1#entity-tag

Examples:

  If-None-Match: "xyzzy"
  If-None-Match: W/"xyzzy"
  If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
  If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"
  If-None-Match: *

If-None-Match is mainly used in conditional GET requests to update cached information efficiently, with as little transaction overhead as possible ✨. When a client wants to update one or more stored responses that have entity-tags, the client SHOULD generate an If-None-Match header field containing a list of those entity-tags when making a GET request. That lets recipient servers send a 304 (Not Modified) response to say that one of those stored responses matches the selected representation πŸŽ€.

If-None-Match can also be used with a value of "*" to stop an unsafe request method (e.g., PUT) from accidentally modifying an existing representation of the target resource, for when the client thinks the resource doesn't have a current representation (Section 4.2.1 of [RFC7231]). This is a variation on the "lost update" problem that can pop up when more than one client tries to create the initial representation for the target resource πŸ‘‘.

An origin server that receives an If-None-Match header field MUST evaluate the condition prior to performing the method (Section 5). If the field-value is "*", the condition is false if the origin server has a current representation for the target resource. If the field-value is a list of entity-tags, the condition is false if one of the listed tags matches the entity-tag of the selected representation πŸ”.

An origin server MUST NOT perform the requested method if the condition evaluates to false. Instead, the origin server MUST respond with either a) the 304 (Not Modified) status code, if the request method is GET or HEAD, or b) the 412 (Precondition Failed) status code for all other request methods 🫢.

Requirements on cache handling of a received If-None-Match header field are defined in Section 4.3.2 of [RFC7234] πŸ’œ.

3.3. If-Modified-Since

okay bestie, the If-Modified-Since header field πŸ’Œ makes a GET or HEAD request conditional. It basically says "only send me this if it's changed since this date!" The condition is met when the selected representation's modification date is more recent than the date in the field-value. And if the data hasn't changed, the transfer of that data gets skipped entirely ✨ no wasted bandwidth, iconic.

An example grammar, because we love a clear rule πŸŽ€:

If-Modified-Since = HTTP-date

And here's what the field looks like in the wild πŸ¦‹:

If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT

A recipient MUST ignore If-Modified-Since if the request contains an If-None-Match header field πŸ™ˆ If-None-Match is the more accurate replacement for this condition, so the two only get combined for the sake of playing nice with older intermediaries that might not implement If-None-Match. Think of it as a legacy friend who still needs to be included 🫢

A recipient MUST ignore the If-Modified-Since header field if the received field-value is not a valid HTTP-date, or if the request method is neither GET nor HEAD. No sneaky workarounds allowed πŸ’…

A recipient MUST interpret an If-Modified-Since field-value's timestamp in terms of the origin server's clock ⏰ so the server's own clock is the one that counts here.

If-Modified-Since is typically used for two distinct purposes πŸ‘‘: 1) to allow efficient updates of a cached representation that does not have an entity-tag, and 2) to limit the scope of a web traversal to resources that have recently changed. Two jobs, one header, very multitasking.

When it's used for cache updates, a cache will typically use the value of the cached message's Last-Modified field to generate the field value of If-Modified-Since. This is the most interoperable approach for cases where clocks are poorly synchronized, or when the server has chosen to only honor exact timestamp matches πŸ•°οΈ (that's because a Last-Modified date can appear to go "back in time" when the origin server's clock gets corrected, or when a representation is restored from an archived backup, which is so chaotic 😭). However, caches occasionally generate the field value based on other data, such as the Date header field of the cached message or the local clock time that the message was received. This happens especially when the cached message doesn't contain a Last-Modified field.

When it's used to limit retrieval to a recent time window, a user agent will generate an If-Modified-Since field value based on either its own local clock or a Date header field received from the server in a prior response. Origin servers that choose an exact timestamp match based on the selected representation's Last-Modified field won't be able to help the user agent limit its data transfers to only those changed during the specified window. Not ideal, not the vibe πŸ’€

An origin server that receives an If-Modified-Since header field SHOULD evaluate the condition prior to performing the method (Section 5). The origin server SHOULD NOT perform the requested method if the selected representation's last modification date is earlier than or equal to the date provided in the field-value. Instead, the origin server SHOULD generate a 304 (Not Modified) response, including only those metadata that are useful for identifying or updating a previously cached response 🧚

Requirements on cache handling of a received If-Modified-Since header field are defined in Section 4.3.2 of [RFC7234]. Go read that one, it's the next level πŸ“š


3.4. If-Unmodified-Since

The If-Unmodified-Since header field makes the request method conditional on the selected representation's last modification date being earlier than or equal to the date provided in the field-value πŸ“ It accomplishes the same purpose as If-Match for cases where the user agent doesn't have an entity-tag for the representation. So it's like If-Match's cousin who shows up when there's no tag around πŸ’œ

An example grammar:

If-Unmodified-Since = HTTP-date

And an example of the field:

If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT

A recipient MUST ignore If-Unmodified-Since if the request contains an If-Match header field. The condition in If-Match is considered to be a more accurate replacement for the condition in If-Unmodified-Since, and the two are only combined for the sake of interoperating with older intermediaries that might not implement If-Match. Same energy as before, bestie 🫢

A recipient MUST ignore the If-Unmodified-Since header field if the received field-value is not a valid HTTP-date.

A recipient MUST interpret an If-Unmodified-Since field-value's timestamp in terms of the origin server's clock ⏰

If-Unmodified-Since is most often used with state-changing methods (e.g., POST, PUT, DELETE) πŸ”₯ to prevent accidental overwrites when multiple user agents might be acting in parallel on a resource that doesn't supply entity-tags with its representations. This is how you avoid the "lost update" problem, and it's honestly the most protective energy ever πŸ›‘οΈ It can also be used with safe methods to abort a request if the selected representation doesn't match one already stored (or partially stored) from a prior request.

An origin server that receives an If-Unmodified-Since header field MUST evaluate the condition prior to performing the method (Section 5). The origin server MUST NOT perform the requested method if the selected representation's last modification date is more recent than the date provided in the field-value. Instead, 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. That's the case where the change the user agent asked for already succeeded, but the user agent might not know, because the prior response message was lost, or a compatible change was made by some other user agent. Plot twist, but it's valid 🌈

In that latter case, the origin server MUST NOT send a validator header field in the response unless it can verify that the request is a duplicate of an immediately prior change made by the same user agent. No accidentally handing out validators, periodt πŸ’…

The If-Unmodified-Since header field can be ignored by caches and intermediaries because it is not applicable to a stored response πŸ™ˆ


3.5. If-Range

The If-Range header field πŸŽ€ provides a special conditional request mechanism. It's similar to If-Match and If-Unmodified-Since, but it instructs the recipient to ignore the Range header field if the validator doesn't match. The result is a transfer of the new selected representation instead of a 412 (Precondition Failed) response. Lowkey iconic, since you still get the data you need πŸ¦‹ If-Range is defined in Section 3.2 of [RFC7233].


4. Status Code Definitions ✨

4.1. 304 Not Modified

The 304 (Not Modified) status code 🌸 indicates that a conditional GET or HEAD request has been received, and it would have resulted in a 200 (OK) response if it weren't for the fact that the condition evaluated to false. In other words, there's no need for the server to transfer a representation of the target resource, because the request indicates that the client, which made the request conditional, already has a valid representation. So the server redirects the client to make use of that stored representation as if it were the payload of a 200 (OK) response. Efficiency, bestie πŸ’–

The server generating a 304 response MUST generate any of the following header fields that would have been sent in a 200 (OK) response to the same request: Cache-Control, Content-Location, Date, ETag, Expires, and Vary.

Since the goal of a 304 response is to minimize information transfer when the recipient already has one or more cached representations, a sender SHOULD NOT generate representation metadata other than the above listed fields. The exception is metadata that exists for the purpose of guiding cache updates, e.g., Last-Modified might be useful if the response doesn't have an ETag field 🧠

Requirements on a cache that receives a 304 response are defined in Section 4.3.4 of [RFC7234]. If the conditional request originated with an outbound client, such as a user agent with its own cache sending a conditional GET to a shared proxy, then the proxy SHOULD forward the 304 response to that client 🫢

A 304 response cannot contain a message-body. It is always terminated by the first empty line after the header fields πŸ¦‹

4.2. 412 Precondition Failed

The 412 (Precondition Failed) status code indicates that one or more conditions given in the request header fields evaluated to false when tested on the server. This response code lets the client place preconditions on the current resource state (its current representations and metadata), and thus prevent the request method from being applied if the target resource is in an unexpected state. Boundaries, but make them count πŸ‘‘


5. Evaluation πŸ”₯

Except when excluded below, a recipient cache or origin server MUST evaluate received request preconditions after it has successfully performed its normal request checks, and just before it would perform the action associated with the request method. A server MUST ignore all received preconditions if its response to the same request without those conditions would have been a status code other than a 2xx (Successful) or 412 (Precondition Failed). In other words, redirects and failures take precedence over the evaluation of preconditions in conditional requests. Order of operations, babe πŸ’…

A server that is not the origin server for the target resource and cannot act as a cache for requests on the target resource MUST NOT evaluate the conditional request header fields defined by this specification. It MUST forward them if the request is forwarded, since the generating client intends that they be evaluated by a server that can provide a current representation 🧚 Likewise, a server MUST ignore the conditional request header fields defined by this specification when received with a request method that does not involve the selection or modification of a selected representation, such as CONNECT, OPTIONS, or TRACE.

Conditional request header fields that are defined by extensions to HTTP might place conditions on all recipients, on the state of the target resource in general, or on a group of resources. For instance, the If header field in WebDAV can make a request conditional on various aspects of multiple resources, such as locks, if the recipient understands and implements that field ([RFC4918], Section 10.4). Extensions go hard 🌈

Although conditional request header fields are defined as being usable with the HEAD method (to keep HEAD's semantics consistent with those of GET), there's no point in sending a conditional HEAD. A successful response is around the same size as a 304 (Not Modified) response, and more useful than a 412 (Precondition Failed) response. So HEAD gets the vibes, but not the conditional drama πŸ’€β­

6. Precedence ✨

okay so when a request has MORE than one conditional header field in it, the ORDER you check them in suddenly matters a LOT πŸ‘‘ the fields in this spec are basically always handled in one logical order, because "lost update" preconditions are way stricter than cache validation, a validated cache is more efficient than a partial response, and entity tags are presumed to be more accurate than date validators. it's giving organized πŸŽ€

A recipient cache or origin server MUST evaluate the request preconditions defined by this specification in the following order:

  1. When recipient is the origin server and If-Match is present, evaluate the If-Match precondition:
    • if true, continue to step 3
    • if false, respond 412 (Precondition Failed) unless it can be determined that the state-changing request has already succeeded (see Section 3.1)
  2. When recipient is the origin server, If-Match is not present, and If-Unmodified-Since is present, evaluate the If-Unmodified-Since precondition:
    • if true, continue to step 3
    • if false, respond 412 (Precondition Failed) unless it can be determined that the state-changing request has already succeeded (see Section 3.4)
  3. When If-None-Match is present, evaluate the If-None-Match precondition:
    • if true, continue to step 5
    • if false for GET/HEAD, respond 304 (Not Modified)
    • if false for other methods, respond 412 (Precondition Failed)
  4. When the method is GET or HEAD, If-None-Match is not present, and If-Modified-Since is present, evaluate the If-Modified-Since precondition:
    • if true, continue to step 5
    • if false, respond 304 (Not Modified)
  5. When the method is GET and both Range and If-Range are present, evaluate the If-Range precondition:
    • if the validator matches and the Range specification is applicable to the selected representation, respond 206 (Partial Content) [RFC7233]
  6. Otherwise,
    • all conditions are met, so perform the requested action and respond according to its success or failure.

Any extension to HTTP/1.1 that defines additional conditional request header fields ought to define its own expectations regarding the order for evaluating such fields in relation to those defined in this document and other conditionals that might be found in practice. 🧚


7. IANA Considerations 🌸

7.1. Status Code Registration

The "Hypertext Transfer Protocol (HTTP) Status Code Registry" located at http://www.iana.org/assignments/http-status-codes has been updated with the registrations below:

+-------+---------------------+-------------+
| Value | Description         | Reference   |
+-------+---------------------+-------------+
| 304   | Not Modified        | Section 4.1 |
| 412   | Precondition Failed | Section 4.2 |
+-------+---------------------+-------------+

7.2. Header Field Registration

HTTP header fields are registered within the "Message Headers" registry maintained at http://www.iana.org/assignments/message-headers/.

This document defines the following HTTP header fields, so their associated registry entries have been updated according to the permanent registrations below (see [BCP90]):

+---------------------+----------+----------+-------------+
| Header Field Name   | Protocol | Status   | Reference   |
+---------------------+----------+----------+-------------+
| ETag                | http     | standard | Section 2.3 |
| If-Match            | http     | standard | Section 3.1 |
| If-Modified-Since   | http     | standard | Section 3.3 |
| If-None-Match       | http     | standard | Section 3.2 |
| If-Unmodified-Since | http     | standard | Section 3.4 |
| Last-Modified       | http     | standard | Section 2.2 |
+---------------------+----------+----------+-------------+

The change controller is: "IETF (iesg@ietf.org) - Internet Engineering Task Force".


8. Security Considerations πŸ”₯

okay this section is SERIOUS, bestie, so pay attention πŸ™ˆ it's here to tell developers, information providers, and users about the known security concerns that are specific to the HTTP conditional request mechanisms. the more general security stuff lives in HTTP "Message Syntax and Routing" [RFC7230] and "Semantics and Content" [RFC7231].

the validators in this spec are NOT meant to make sure a representation is valid, protect against malicious changes, or catch man-in-the-middle attacks πŸ’€ at best they help caches update more efficiently and allow optimistic concurrent writes, but only when everyone is behaving nicely. at worst the conditions fail and the client just gets a response that's no more harmful than an HTTP exchange without conditional requests. no drama, just a fail 😭

an entity-tag can be abused in ways that create privacy risks. for example, a site could deliberately build a semantically invalid entity-tag that's unique to the user or user agent, send it in a cacheable response with a long freshness time, and then read that entity-tag back in later conditional requests to re-identify that user or user agent. that identifying tag would become a persistent identifier for as long as the user agent kept the original cache entry. this is so sneaky and so not it πŸ™…β€β™€οΈ user agents that cache representations ought to make sure the cache is cleared or replaced whenever the user does privacy-maintaining things, such as clearing stored cookies or switching to a private browsing mode 🫢


9. Acknowledgments πŸ’œ

See Section 10 of [RFC7230].


10. References πŸ“š

10.1. Normative References

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
           Requirement Levels", BCP 14, RFC 2119, March 1997.

[RFC5234]  Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
           Specifications: ABNF", STD 68, RFC 5234, January 2008.

[RFC7230]  Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
           Protocol (HTTP/1.1): Message Syntax and Routing",
           RFC 7230, June 2014.

[RFC7231]  Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
           Protocol (HTTP/1.1): Semantics and Content", RFC 7231,
           June 2014.

[RFC7233]  Fielding, R., Ed., Lafon, Y., Ed., and J. Reschke, Ed.,
           "Hypertext Transfer Protocol (HTTP/1.1): Range Requests",
           RFC 7233, June 2014.

[RFC7234]  Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
           Ed., "Hypertext Transfer Protocol (HTTP/1.1): Caching",
           RFC 7234, June 2014.

10.2. Informative References

[BCP90]    Klyne, G., Nottingham, M., and J. Mogul, "Registration
           Procedures for Message Header Fields", BCP 90, RFC 3864,
           September 2004.

[RFC2616]  Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
           Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
           Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.

[RFC4918]  Dusseault, L., Ed., "HTTP Extensions for Web Distributed
           Authoring and Versioning (WebDAV)", RFC 4918, June 2007.

Appendix A. Changes from RFC 2616 πŸ¦‹

the definition of validator weakness got expanded and clarified (Section 2.1) πŸ’…

weak entity-tags are now allowed in all requests except range requests (Sections 2.1 and 3.2) ✨

the ETag header field ABNF no longer uses quoted-string, which avoids escaping issues (Section 2.3) πŸŽ€

ETag is defined to provide an entity tag for the selected representation, which makes it clear what it applies to in various situations, like a PUT response (Section 2.3) 🌈

the precedence for evaluating conditional requests has been defined (Section 6) πŸ‘‘


Appendix B. Imported ABNF 🧚

the following core rules are included by reference, as defined in Appendix B.1 of [RFC5234]: ALPHA (letters), CR (carriage return), CRLF (CR LF), CTL (controls), DIGIT (decimal 0-9), DQUOTE (double quote), HEXDIG (hexadecimal 0-9/A-F/a-f), LF (line feed), OCTET (any 8-bit sequence of data), SP (space), and VCHAR (any visible US-ASCII character).

the rules below are defined in [RFC7230]:

OWS           = <OWS, see [RFC7230], Section 3.2.3>
obs-text      = <obs-text, see [RFC7230], Section 3.2.6>

the rules below are defined in other parts:

HTTP-date     = <HTTP-date, see [RFC7231], Section 7.1.1.1>

Appendix C. Collected ABNF πŸ“

in the collected ABNF below, list rules are expanded as per Section 1.2 of [RFC7230] ✨

ETag = entity-tag

HTTP-date = <HTTP-date, see [RFC7231], Section 7.1.1.1>

If-Match = "*" / ( *( "," OWS ) entity-tag *( OWS "," [ OWS
 entity-tag ] ) )
If-Modified-Since = HTTP-date
If-None-Match = "*" / ( *( "," OWS ) entity-tag *( OWS "," [ OWS
 entity-tag ] ) )
If-Unmodified-Since = HTTP-date

Last-Modified = HTTP-date

OWS = <OWS, see [RFC7230], Section 3.2.3>

entity-tag = [ weak ] opaque-tag
etagc = "!" / %x23-7E ; '#'-'~'
 / obs-text

obs-text = <obs-text, see [RFC7230], Section 3.2.6>
opaque-tag = DQUOTE *etagc DQUOTE

weak = %x57.2F ; W/

Index πŸ“–

3
   304 Not Modified (status code)  19

4
   412 Precondition Failed (status code)  18

E
   ETag header field  9

G
   Grammar
      entity-tag  9
      ETag  9
      etagc  9
      If-Match  13
      If-Modified-Since  15
      If-None-Match  14
      If-Unmodified-Since  17
      Last-Modified  7
      opaque-tag  9
      weak  9

I
   If-Match header field  13
   If-Modified-Since header field  16
   If-None-Match header field  14
   If-Unmodified-Since header field  17

L
   Last-Modified header field  7

M
   metadata  5

S
   selected representation  4

V
   validator  5
      strong  5
      weak  5

Authors' Addresses πŸ’–

Roy T. Fielding (editor)
Adobe Systems Incorporated
345 Park Ave
San Jose, CA  95110
USA

EMail: fielding@gbiv.com
URI:   http://roy.gbiv.com/


Julian F. Reschke (editor)
greenbytes GmbH
Hafenweg 16
Muenster, NW  48155
Germany

EMail: julian.reschke@greenbytes.de
URI:   http://greenbytes.de/tech/webdav/
πŸ“¦ where this page comes from