Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Revision 1105079 of HTTP conditional requests

  • Revision slug: Web/HTTP/Conditional_requests
  • Revision title: HTTP conditional requests
  • Revision id: 1105079
  • Created:
  • Creator: teoli
  • Is current revision? No
  • Comment

Revision Content

{{HTTPSidebar}}

HTTP has a concept of conditional requests, where the result, and even the success, of a request can be changed by comparing the affected resources with the value of a validator. Such requests can be useful to validate the content of a cache, and sparing a useless control, to verify the integrity of a document, like when resuming a download, or to prevent to lose updates when uploading or modifying a document on the server.

Principles

HTTP conditional requests are requests that are executed differently depending on the value of specific headers. These headers defines a precondition and the result of the request will be different if the precondition is matched or not.

The different behaviors are defined by the method of the request used and by the set of header used for a precondition:

  • for {{glossary("safe")}} methods, like {{HTTPMethod("GET")}}, that usually try to fetch a document, the conditional request can be used to send back the document only if relevant, and therefore to spare bandwidth.
  • for {{glossary("safe", "unsafe")}} methods, like {{HTTPMethod("PUT")}}, that usually upload a document, the conditional request can be used to upload the document only if the original it is based on is the same that is stored on the server.

Validators

With the notable exception of {{HTTPHeader("If-Range")}}, all conditional headers try to check if the resource stored on the server matches a specific version. To achieve this, the conditional requests needs to indicate the version of the resource. As transmitted the whole resource to compare it byte to byte is impracticable (and not even always what is wanted!), the request transmit a value describing the version: such values are called validators and are of two kinds:

  • the date of last modification of the document, the last-modified date.
  • an opaque string, uniquely identifying each version, called the entity tag or the etag.

Comparing versions of the same resource is a bit tricky: depending of the context, there are two kind of equality checks. Strong validation is used when byte to byte identity is expected, for example when resuming a download. Weak validation is used when the user-agent need only to determine if the two resources have the same content, even if they are minor difference (like different ads, or a footer with a different date).

The kind of validation is indepeneant of the validator used; both {{HTTPHeader("Last-Modified")}} and {{HTTPHeader("ETag")}} allow for both type of validation though the complexity to implement it on the server side may vary. HTTP uses strong validation by default, and it specifies when weak validation can be used.

Strong validation

Strong validation consists in guaranteeing that the resource is byte to byte identical to the one it is compared too. This is mandatory for some conditional headers, and the default for the others. Strong validation is very strict and may be difficult to guarantee at the server level, but guarantee no data loss at any time, sometimes at the expense of some performance.

It is quite difficult to have a unique identifier for strong validation with {{HTTPHeader("Last-Modified")}}. Often this is done using an {{HTTPHeader("ETag")}} with the MD5 hash of the resource (or a derivative).

Weak validation

Weak validation differs from strong validation as it considers two version of the document as identical if the content is equivalent. For example, a page that would differ from another only by a different date in its footer, or by different advertisement, would be considered as identical to the other with weak validation, but will be considered as different with strong validation. Building a system of etags that makes weak validation may be complex as it involves knowing the importance of the different elements of a page, but is very useful to optimize performance when caching information.

Conditional headers

Several HTTP headers, called conditional headers, lead to conditional requests. These are:

{{HTTPHeader("If-Match")}}
Succeeds if the {{HTTPHeader("ETag")}} of the distant resource is equal to one listed in this header. By default, unless the etag is prefixed with 'W/' performs a strong validation.
{{HTTPHeader("If-None-Match")}}
Succeeds if the {{HTTPHeader("ETag")}} of the distant resource is different to each listed in this header. By default, unless the etag is prefixed with 'W/' performs a strong validation.
{{HTTPHeader("If-Modified-Since")}}
 
{{HTTPHeader("If-Unmodified-Since")}}
{{HTTPHeader("If-Range")}}

Use cases

Cache update

The most common use case for conditional requests is the updating of a cache. With an empty cache, or without a cache, the requested resources is sent back with a status of {{HTTPStatus("200")}} OK.

The request issued when the cache is empty triggers the resource to be downloaded, with both validator value sent as headers. The cache is then filled.

Together with the resource, the validators are sent in the headers. Here both {{HTTPHeader("Last-Modified")}} and {{HTTPHeader("ETag")}} are sent, but it could have been only one of them. These validators are cached with the resource (like all headers) and will be used to craft conditional requests once the cache becomes stale.

As long as the cache is not stale, no requests are issued at all. But once, it has become stale, this is mostly controlled by the {{HTTPHeader("Cache-Control")}} header, the client doesn't use directly the cached value but issues a conditional request, with the value of the validator used as parameter of the {{HTTPHeader("If-Modified-Since")}} and {{HTTPHeader("If-Match")}}.

If the resource has not been changed, the server sent back a {{HTTPStatus("304")}} Not Modified response, which makes the cache fresh again and the client use the cached resource. Although their is response/request round-trip that consumes some resources, this is more efficient than to transmit the whole resource over the wire again.

With a stale cache, the conditional request is sent. The server can determine if the resource changed, and, as in this case, decide not to send it again as it is the same.

If the resource has changed, the server just sent back a {{HTTPStatus("200")}} OK response, with the new version of the resource, like if the request wasn't conditional and the client use this new resource (and cache it).

In the case where the resource was changed, it is sent back as if the request wasn't conditional.

Beside setting the validators on the server side, this mechanism is transparent: all the browsers manage a cache and send such conditional requests without any special work to be done by the Web developers.

Integrity of a partial download

Partial downloading of files is a functionality of HTTP that allows to resume previously operation, saving bandwidth and time by keeping the already obtained information.

A download has been stopped and only partial content has been retrieved.

A server supporting partial downloads advertised it by sending the {{HTTPHeader("Accept-Ranges")}} header. When this happens, the client can resume a download by sending a {{HTTPHeader("Ranges")}} header with the missing ranges.

The client resumes the requests by indicating the range he needs and preconditions checking the validators of the partially obtained request.

The principle is pretty simple, but there is one potential problem: if the resource downloaded has been modified between the two downloads, the ranges obtained will correspond to two different versions of the resource and the final document will be corrupted.

To prevent this, conditional requests are used. For ranges, they are two ways of doing it. The more flexible one makes use of {{HTTPHeader("If-Modified-Since")}} and {{HTTPHeader("If-Match")}} and the server returns an error if the precondition is failed; the client then restarts the download from the beginning.

When the partially downloaded resource has been modified, the preconditions will fail and the resource will have to be downloaded again completely.

Even if this method works, it adds an extra response/request exchange when the document has been changed. This impairs the performance and HTTP has an extra specific header to avoid this, {{HTTPHeader("If-Range")}}.

The If-Range headers allows the server to directly send back the complete resource if it has been modified, no need to send a 412 error and wait for the client to re-initiate the download.

These solution is more efficient, but slightly less flexible (only one etag can be used in the condition), though this added flexibility is rarely needed.

Avoiding the lost update problem with optimistic locking

A common operation in Web applications is to update a remote document. This is very common in any file system or source control applications, but any application that allow to store remote resources need such a mechanism. Similarly, common Web sites like wikis and other CMS have such a need.

Using the {{HTTPMethod("PUT")}} method, it is very easy to implement this. The client first reads the original files, modifies it and finally pushes it to the server.

Updating a file with a PUT is very simple when concurrency is not involved.

Unfortunately, things get a little bit awry as soon as we take into account concurrency. While a client is modifying locally its new copy of the resource, a second client can fetch the same resource and do the same on its side. What happens then is very unfortunate: when they commit back, the modifications of the first client to push are discarded by the next push, as the second client is unaware of the new changes. The decision about who wins is not communicated to the other party, but which client's changes will be kept will vary with the speed they commit, which depends on the performance of the clients, of the server, and even of the human editing the document at the client: the winner will change from one time to another. This is a {{glossary("race condition")}} and leads to problematic behaviors difficult to detect and to debug.

When several clients update the same resource in parallel, we are facing a race condition: the slowest win, and the others don't even know they lost. Problematic!

There is no way in dealing with this problem without annoying one of the two clients. But lost updates and race conditions are to be avoided: we want a predictable results and that the clients are notified when their changes are rejected.

Conditional requests allowed to implement very easily the optimistic locking algorithm (used by most wikis or source control systems). The idea is to allow all clients to get copies of the resource, to let them modify it locally and to control concurrency by allowing the first client submitting an update to do it successfully, all subsequent updates based on the now obsolete version of the resource are rejected.

Conditional requests allow to implement optimistic locking: now the quickest wins, and the others get an error.

This is implemented using the {{HTTPHeader("If-Match")}} or {{HTTPHeader("If-Unmodified-Since")}} headers. If the etag doesn't match the original file, or if the file has been modified since it has been obtained, the change is simply rejected with a {{HTTPStatus("412")}} Precondition Failed error. It is up to the client to then deal with the error, either by notifying the human behind to start again, this time on the newest version, or by helping the person by showing him a "diff" and letting the human choose the changes to keep.

Dealing with the first upload of a resource

The first upload of a resource is an edge case of the previous: like for any update of a resource, it is subject to a race condition if two clients try to perform it at the same time (or almost). To prevent this, conditional requests can be used: by adding {{HTTPHeader("If-None-Match")}} with the special value of '*', representing any etag, the request will succeed only if the resource didn't existed before.

Like for a regular upload, the first upload of a resource is subject to a race condition: If-None-Match can prevent it.

If-None-Match will work only with HTTP/1.1-compliant server (and later). If you don't know if the server will be compliant, you need first to issue a {{HTTPMethod("HEAD")}} request to the resource to check it.

Conclusion

Conditional requests are a key feature of HTTP and allows to build efficient and complex application. For caching, or resuming downloads, the only work requested to webmasters is to configure the server correctly (setting up correct etags in some environment can be tricky), and Web developers have nothing to do as the browser will serve the right conditional requests.

For locking mechanisms, it is the opposite, Web devs need to issue request with the proper headers, while webmaster can rely most of the time on the application to make the check for them.

In both cases, conditional requests are a fundamental feature of the Web.

Revision Source

<p>{{HTTPSidebar}}</p>

<p class="summary">HTTP has a concept of <em>conditional requests</em>, where the result, and even the success, of a request can be changed by comparing the affected resources with the value of a <em>validator</em>. Such requests can be useful to validate the content of a cache, and sparing a useless control, to verify the integrity of a document, like when resuming a download, or to prevent to lose updates when uploading or modifying a document on the server.</p>

<h2 id="Principles">Principles</h2>

<p>HTTP conditional requests are requests that are executed differently depending on the value of specific headers. These headers defines a precondition and the result of the request will be different if the precondition is matched or not.</p>

<p>The different behaviors are defined by the method of the request used and by the set of header used for a precondition:</p>

<ul>
 <li>for {{glossary("safe")}} methods, like {{HTTPMethod("GET")}}, that usually try to fetch a document, the conditional request can be used to send back the document only if relevant, and therefore to spare bandwidth.</li>
 <li>for {{glossary("safe", "unsafe")}} methods, like {{HTTPMethod("PUT")}}, that usually upload a document, the conditional request can be used to upload the document only if the original it is based on is the same that is stored on the server.</li>
</ul>

<h2 id="Validators">Validators</h2>

<p>With the notable exception of {{HTTPHeader("If-Range")}}, all conditional headers try to check if the resource stored on the server matches a specific version. To achieve this, the conditional requests needs to indicate the version of the resource. As transmitted the whole resource to compare it byte to byte is impracticable (and not even always what is wanted!), the request transmit a value describing the version: such values are called <em>validators</em> and are of two kinds:</p>

<ul>
 <li>the date of last modification of the document, the <em>last-modified</em> date.</li>
 <li>an opaque string, uniquely identifying each version, called the <em>entity tag</em> or the <em>etag</em>.</li>
</ul>

<p>Comparing versions of the same resource is a bit tricky: depending of the context, there are two kind of equality checks. <em>Strong validation</em> is used when byte to byte identity is expected, for example when resuming a download. <em>Weak validation</em> is used when the user-agent need only to determine if the two resources have the same content, even if they are minor difference (like different ads, or a footer with a different date).</p>

<p>The kind of validation is indepeneant of the validator used; both {{HTTPHeader("Last-Modified")}} and {{HTTPHeader("ETag")}} allow for both type of validation though the complexity to implement it on the server side may vary. HTTP uses strong validation by default, and it specifies when weak validation can be used.</p>

<h3 id="Strong_validation">Strong validation</h3>

<p id="sect1">Strong validation consists in guaranteeing that the resource is byte to byte identical to the one it is compared too. This is mandatory for some conditional headers, and the default for the others. Strong validation is very strict and may be difficult to guarantee at the server level, but guarantee no data loss at any time, sometimes at the expense of some performance.</p>

<p>It is quite difficult to have a unique identifier for strong validation with {{HTTPHeader("Last-Modified")}}. Often this is done using an {{HTTPHeader("ETag")}} with the MD5 hash of the resource (or a derivative).</p>

<h3 id="Weak_validation">Weak validation</h3>

<p>Weak validation differs from strong validation as it considers two version of the document as identical if the content is equivalent. For example, a page that would differ from another only by a different date in its footer, or by different advertisement, would be considered as identical to the other with weak validation, but will be considered as different with strong validation. Building a system of etags that makes weak validation may be complex as it involves knowing the importance of the different elements of a page, but is very useful to optimize performance when caching information.</p>

<h2 id="Conditional_headers">Conditional headers</h2>

<p>Several HTTP headers, called conditional headers, lead to conditional requests. These are:</p>

<dl>
 <dt>{{HTTPHeader("If-Match")}}</dt>
 <dd>Succeeds if the {{HTTPHeader("ETag")}} of the distant resource is equal to one listed in this header. By default, unless the etag is prefixed with <code>'W/'</code> performs a strong validation.</dd>
 <dt>{{HTTPHeader("If-None-Match")}}</dt>
 <dd>Succeeds if the {{HTTPHeader("ETag")}} of the distant resource is different to each listed in this header. By default, unless the etag is prefixed with <code>'W/'</code> performs a strong validation.</dd>
 <dt>{{HTTPHeader("If-Modified-Since")}}</dt>
 <dd>&nbsp;</dd>
 <dt>{{HTTPHeader("If-Unmodified-Since")}}</dt>
 <dt>{{HTTPHeader("If-Range")}}</dt>
</dl>

<h2 id="Use_cases">Use cases</h2>

<h3 id="Cache_update">Cache update</h3>

<p>The most common use case for conditional requests is the updating of a cache. With an empty cache, or without a cache, the requested resources is sent back with a status of {{HTTPStatus("200")}} <code>OK</code>.</p>

<p><img alt="The request issued when the cache is empty triggers the resource to be downloaded, with both validator value sent as headers. The cache is then filled." src="https://mdn.mozillademos.org/files/13729/Cache1.png" style="height:265px; width:741px" /></p>

<p>Together with the resource, the validators are sent in the headers. Here both {{HTTPHeader("Last-Modified")}} and {{HTTPHeader("ETag")}} are sent, but it could have been only one of them. These validators are cached with the resource (like all headers) and will be used to craft conditional requests once the cache becomes stale.</p>

<p>As long as the cache is not stale, no requests are issued at all. But once, it has become stale, this is mostly controlled by the {{HTTPHeader("Cache-Control")}} header, the client doesn't use directly the cached value but issues a <em>conditional request</em>, with the value of the validator used as parameter of the {{HTTPHeader("If-Modified-Since")}} and {{HTTPHeader("If-Match")}}.</p>

<p>If the resource has not been changed, the server sent back a {{HTTPStatus("304")}} <code>Not Modified</code> response, which makes the cache fresh again and the client use the cached resource. Although their is response/request round-trip that consumes some resources, this is more efficient than to transmit the whole resource over the wire again.</p>

<p><img alt="With a stale cache, the conditional request is sent. The server can determine if the resource changed, and, as in this case, decide not to send it again as it is the same." src="https://mdn.mozillademos.org/files/13731/HTTPCache2.png" style="height:265px; width:741px" /></p>

<p>If the resource has changed, the server just sent back a {{HTTPStatus("200")}}<code> OK</code> response, with the new version of the resource, like if the request wasn't conditional and the client use this new resource (and cache it).</p>

<p><img alt="In the case where the resource was changed, it is sent back as if the request wasn't conditional." src="https://mdn.mozillademos.org/files/13733/HTTPCache3.png" /></p>

<p>Beside setting the validators on the server side, this mechanism is transparent: all the browsers manage a cache and send such conditional requests without any special work to be done by the Web developers.</p>

<h3 id="Integrity_of_a_partial_download">Integrity of a partial download</h3>

<p>Partial downloading of files is a functionality of HTTP that allows to resume previously operation, saving bandwidth and time by keeping the already obtained information.</p>

<p><img alt="A download has been stopped and only partial content has been retrieved." src="https://mdn.mozillademos.org/files/13735/HTTPResume1.png" style="height:397px; width:764px" /></p>

<p>A server supporting partial downloads advertised it by sending the {{HTTPHeader("Accept-Ranges")}} header. When this happens, the client can resume a download by sending a {{HTTPHeader("Ranges")}} header with the missing ranges.</p>

<p><img alt="The client resumes the requests by indicating the range he needs and preconditions checking the validators of the partially obtained request." src="https://mdn.mozillademos.org/files/13737/HTTPResume2.png" /></p>

<p>The principle is pretty simple, but there is one potential problem: if the resource downloaded has been modified between the two downloads, the ranges obtained will correspond to two different versions of the resource and the final document will be corrupted.</p>

<p>To prevent this, conditional requests are used. For ranges, they are two ways of doing it. The more flexible one makes use of {{HTTPHeader("If-Modified-Since")}} and {{HTTPHeader("If-Match")}} and the server returns an error if the precondition is failed; the client then restarts the download from the beginning.</p>

<p><img alt="When the partially downloaded resource has been modified, the preconditions will fail and the resource will have to be downloaded again completely." src="https://mdn.mozillademos.org/files/13739/HTTPResume3.png" /></p>

<p>Even if this method works, it adds an extra response/request exchange when the document has been changed. This impairs the performance and HTTP has an extra specific header to avoid this, {{HTTPHeader("If-Range")}}.</p>

<p><img alt="The If-Range headers allows the server to directly send back the complete resource if it has been modified, no need to send a 412 error and wait for the client to re-initiate the download." src="https://mdn.mozillademos.org/files/13741/HTTPResume4.png" style="height:263px; width:770px" /></p>

<p>These solution is more efficient, but slightly less flexible (only one etag can be used in the condition), though this added flexibility is rarely needed.</p>

<h3 id="Avoiding_the_lost_update_problem_with_optimistic_locking">Avoiding the lost update problem with optimistic locking</h3>

<p>A common operation in Web applications is to <em>update</em> a remote document. This is very common in any file system or source control applications, but any application that allow to store remote resources need such a mechanism. Similarly, common Web sites like wikis and other CMS have such a need.</p>

<p>Using the {{HTTPMethod("PUT")}} method, it is very easy to implement this. The client first reads the original files, modifies it and finally pushes it to the server.</p>

<p><img alt="Updating a file with a PUT is very simple when concurrency is not involved." src="https://mdn.mozillademos.org/files/13743/HTTPLock1.png" /></p>

<p>Unfortunately, things get a little bit awry as soon as we take into account concurrency. While a client is modifying locally its new copy of the resource, a second client can fetch the same resource and do the same on its side. What happens then is very unfortunate: when they commit back, the modifications of the first client to push are discarded by the next push, as the second client is unaware of the new changes. The decision about who wins is not communicated to the other party, but which client's changes will be kept will vary with the speed they commit, which depends on the performance of the clients, of the server, and even of the human editing the document at the client: the winner will change from one time to another. This is a {{glossary("race condition")}} and leads to problematic behaviors difficult to detect and to debug.</p>

<p><img alt="When several clients update the same resource in parallel, we are facing a race condition: the slowest win, and the others don't even know they lost. Problematic!" src="https://mdn.mozillademos.org/files/13749/HTTPLock2.png" style="height:504px; width:904px" /></p>

<p>There is no way in dealing with this problem without annoying one of the two clients. But lost updates and race conditions are to be avoided: we want a predictable results and that the clients are notified when their changes are rejected.</p>

<p>Conditional requests allowed to implement very easily the <em>optimistic locking algorithm</em> (used by most wikis or source control systems). The idea is to allow all clients to get copies of the resource, to let them modify it locally and to control concurrency by allowing the first client submitting an update to do it successfully, all subsequent updates based on the now obsolete version of the resource are rejected.</p>

<p><img alt="Conditional requests allow to implement optimistic locking: now the quickest wins, and the others get an error." src="https://mdn.mozillademos.org/files/13751/HTTPLock3.png" style="height:471px; width:904px" /></p>

<p>This is implemented using the {{HTTPHeader("If-Match")}} or {{HTTPHeader("If-Unmodified-Since")}} headers. If the etag doesn't match the original file, or if the file has been modified since it has been obtained, the change is simply rejected with a {{HTTPStatus("412")}} <code>Precondition Failed</code> error. It is up to the client to then deal with the error, either by notifying the human behind to start again, this time on the newest version, or by helping the person by showing him a "diff" and letting the human choose the changes to keep.</p>

<h3 id="Dealing_with_the_first_upload_of_a_resource">Dealing with the first upload of a resource</h3>

<p>The first upload of a resource is an edge case of the previous: like for any update of a resource, it is subject to a race condition if two clients try to perform it at the same time (or almost). To prevent this, conditional requests can be used: by adding {{HTTPHeader("If-None-Match")}} with the special value of <code>'*'</code>, representing any etag, the request will succeed only if the resource didn't existed before.</p>

<p><img alt="Like for a regular upload, the first upload of a resource is subject to a race condition: If-None-Match can prevent it." src="https://mdn.mozillademos.org/files/13753/HTTPFirst.png" style="height:311px; width:895px" /></p>

<p><code>If-None-Match</code> will work only with HTTP/1.1-compliant server (and later). If you don't know if the server will be compliant, you need first to issue a {{HTTPMethod("HEAD")}} request to the resource to check it.</p>

<h2 id="Conclusion">Conclusion</h2>

<p>Conditional requests are a key feature of HTTP and allows to build efficient and complex application. For caching, or resuming downloads, the only work requested to webmasters is to configure the server correctly (setting up correct etags in some environment can be tricky), and Web developers have nothing to do as the browser will serve the right conditional requests.</p>

<p>For locking mechanisms, it is the opposite, Web devs need to issue request with the proper headers, while webmaster can rely most of the time on the application to make the check for them.</p>

<p>In both cases, conditional requests are a fundamental feature of the Web.</p>
Revert to this revision