Are browsers supposed to handle 304 responses automagically?
Asked Answered
U

2

10

Might be a silly question, but I haven't found any clear answer yet.

My server handles ETag caching for some quite big JSON responses we have, returning 304 NOT MODIFIED with an empty body if the If-None-Match header contains the same hash as the one newly generated (shallow ETags).

Are browsers supposed to handle this automagically, or do the in-browser client apps consuming the API asynchronously need to implement some logic to handle such responses (i.e. use the cached version if 304 is responded, create/update the cached version otherwise)?

Because so far, I've manually implemented this logic client-side, but I'm wondering whether I just reinvented a square wheel...


In other words, with the Cache-Control header for example, the in-browser client apps don't need to parse the value, check for max-age for instance, stores it somehow, setup a timeout, etc.: everything is handled ahead by the browsers directly. The question is: are browsers supposed to behave the same way when they receive a 304?


Here is how I wrote my client so far (built with AngularJS, running in browsers):

myModule

  .factory("MyRepository", ($http) => {

    return {
      fetch: (etag) => {
        return $http.get(
          "/api/endpoint",
          etag ? { headers: { "If-None-Match": etag } } : undefined
        );
      }
    };

  })

  .factory("MyService", (MyRepository, $q) => {

    let latestEtag = null;
    let latestVersion = null;

    return {
      fetch: () => {
        return MyRepository
          .fetch(latestEtag)
          .then((response) => {
            latestEtag = response.headers("ETag");
            latestVersion = response.data;
            return angular.copy(latestVersion);
          })
          .catch((response) => {
            return 304 === error.status
              ? angular.copy(latestVersion)
              : $q.reject(response)
          });
      }
    };

  });

So basically, is the above logic effectively needed, or am I supposed to be able to simply use $http.get("/api/endpoint") directly?

This code above is working fine, which seems to mean that it needs to be handled programmatically, although I've never seen such "custom" implementations on the articles I read.

Undervalue answered 10/4, 2018 at 10:28 Comment(7)
"in-browser client apps" - do you mean JavaScript running on an HTML page? Show how you issue the request and consume the response.Ambrosio
@Ambrosio Yes, I updated my question.Undervalue
What happened when you tested it? Or reviewed your historical logs?Ablation
@Ablation The code I posted is working fine, which seems to mean that it needs to be handled programmatically, although I've never seen such "custom" implementations on the articles I read.Undervalue
Pretty sure browsers do the magic, facing a 304 not modified nothing is downloaded at all, only the header was. BUT you can bypass this behavior by setting Cache-Control: no-cache from both the server or the client. You can also force the server to always serve «new content» by setting a new date, which will lead to a new etags at each request. Pretty sure the behavior exist since long -> pragma.Ambivert
@Cryptopat But I do want this behavior to happen, i.e. browsers doing the magic. What's happening currently, is that I do receive a 304 (see my catch block), just as if browsers were not doing the magic.Undervalue
When in doubt in anything, remove everything that can produce a bug. In your case angular has a bad behavior. When winning time lead to lose it later, while feeding google database by all it can keep. To me this last reason is enough to avoid it at all prices.Ambivert
P
6

The 304 responses are automagically handled by browser as such

So I created a simple page

<html>
<head>
<script src="./axios.min.js"></script>
<script src="./jquery-3.3.1.js"></script>
</head>
<body>
<h1>this is a test</page>
</body>
</html>

and the added a test.json file

root@vagrant:/var/www/html# cat test.json
{

"name": "tarun"

}

And then in nginx added below

location ~*  \.(jpg|jpeg|png|gif|ico|css|js|json)$ {
   expires 365d;
}

Now the results

AXIOS

AXIOS

As you can see the first request is 200 and second one 304 but there is no impact on the JS code

jQuery

jQuery response

Same thing with jQuery as well

From the curl you can see that server didn't send anything on the 2nd 304 request

$ curl -v 'http://vm/test.json' -H 'If-None-Match: "5ad71064-17"' -H 'DNT: 1' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.9' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' -H 'Accept: */*' -H 'Referer: http://vm/' -H 'X-Requested-With: XMLHttpRequest' -H 'Connection: keep-alive' -H 'If-Modified-Since: Wed, 18 Apr 2018 09:31:16 GMT' --compressed
*   Trying 192.168.33.100...
* TCP_NODELAY set
* Connected to vm (192.168.33.100) port 80 (#0)
> GET /test.json HTTP/1.1
> Host: vm
> If-None-Match: "5ad71064-17"
> DNT: 1
> Accept-Encoding: gzip, deflate
> Accept-Language: en-US,en;q=0.9
> User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
> Accept: */*
> Referer: http://vm/
> X-Requested-With: XMLHttpRequest
> Connection: keep-alive
> If-Modified-Since: Wed, 18 Apr 2018 09:31:16 GMT
>
< HTTP/1.1 304 Not Modified
< Server: nginx
< Date: Wed, 18 Apr 2018 09:42:45 GMT
< Last-Modified: Wed, 18 Apr 2018 09:31:16 GMT
< Connection: keep-alive
< ETag: "5ad71064-17"
<
* Connection #0 to host vm left intact

So you don't need to handle a 304, browser will do that work for you.

Permenter answered 18/4, 2018 at 9:44 Comment(0)
C
3

Yes, probably all modern major browsers handle response validation using conditional requests well. Relevant excerpt from The State of Browser Caching, Revisited article by Mark Nottingham:

Validation allows a cache to check with the server to see if a stale stored response can be reused.

All of the tested browsers support validation based upon ETag and Last-Modified. The tricky part is making sure that the 304 Not Modified response is correctly combined with the stored response; specifically, the headers in the 304 update the stored response headers.

All of the tested browsers do update stored headers upon a 304, both in the immediate response and subsequent ones served from cache.

This is good news; updating headers with a 304 is an important mechanism, and when they get out of sync it can cause problems.


For more information check HTTP Caching article by Ilya Grigorik.

Circuitry answered 19/4, 2018 at 9:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.