Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do browsers inefficiently make 2 requests here?

I noticed something odd regarding ajax and image loading. Suppose you have an image on the page, and ajax requests the same image - one would guess that ajax requests would hit the browser cache, or it should at least only make one request, the resulting image going to the page and the script that wants to read/process the image.

Surprisingly, I found that even when the javascript waits for the entire page to load, the image request still makes a new request! Is this a known bug in Firefox and Chrome, or something bad jQuery ajax is doing?

Here you can see the problem, open Fiddler or Wireshark and set it to record before you click "run":

<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<div id="something" style="background-image:url(http://jsfiddle.net/img/logo-white.png);">Hello</div>
<script>

jQuery(function($) {
    $(window).load(function() {
        $.get('http://jsfiddle.net/img/logo-white.png');
    })
});

</script>

Note that in Firefox it makes two requests, both resulting in 200-OK, and sending the entire image back to the browser twice. In Chromium, it at least correctly gets a 304 on second request instead of downloading the entire contents twice.

Oddly enough, IE11 downloads the entire image twice, while it seems IE9 aggressively caches it and downloads it once.

Ideally I would hope the ajax wouldn't make a second request at all, since it is requesting exactly the same url. Is there a reason css and ajax in this case usually have different caches, as though the browser is using different cache storage for css vs ajax requests?

like image 537
NoBugs Avatar asked Sep 13 '14 04:09

NoBugs


5 Answers

The helpful folks at Mozilla gave some details as to why this happens. Apparently Firefox assumes an "anonymous" request could be different than normal, and for this reason it makes a second request and doesn't consider the cached value with different headers to be the same request.

https://bugzilla.mozilla.org/show_bug.cgi?id=1075297

like image 58
NoBugs Avatar answered Nov 15 '22 14:11

NoBugs


This "problem" could a be a CORS pre-flight test.

I had noticed this in my applications awhile back, that the call to retrieve information from a single page application made the call twice. This only happens when you're accessing URLs on a different domain. In my case we have APIs we've built and use on a different server (a different domain) than that of the applications we build. I noticed that when I use a GET or POST in my application to these RESTFUL APIs the call appears to be made twice.

What is happening is something called pre-flight (https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS), an initial request is made to the server to see if the ensuing call is allowed.

Excerpt from MDN:

Unlike simple requests, "preflighted" requests first send an HTTP request by the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if:

  1. It uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
  2. It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)
like image 26
m.e.conroy Avatar answered Nov 20 '22 17:11

m.e.conroy


I use the newest Google Chrome and it makes one request. But in your JSFIDDLE example you are loading jQuery twice. First with CSS over style attribute and second in your code over script tag. Improved: JSFIDDLE

<div id="something" style="background-image:url('http://jsfiddle.net/img/logo-white.png');">Hello</div>

<script>
    jQuery(window).load(function() {
        jQuery.get('http://jsfiddle.net/img/logo-white.png');
    });

    // or

    jQuery(function($) {
        jQuery.get('http://jsfiddle.net/img/logo-white.png');
    });
</script>

jQuery(function($) {...} is called when DOM is ready and jQuery(window).load(...); if DOM is ready and every image and other resources are loaded. To put both together nested makes no sense, see also here: window.onload vs $(document).ready()

Sure, the image is loaded two times in Network tab of the web inspector. First through your CSS and second through your JavaScript. The second request is probably cached.

UPDATE: But every request if cached or not is shown in this tab. See following example: http://jsfiddle.net/95mnf9rm/4/ There are 5 request with cached AJAX calls and 5 without caching. And 10 request are shown in 'Network' tab. When you use your image twice in CSS then it's only requested once. But if you explicitly make a AJAX call then the browser makes an AJAX call. As you want. And then maybe it's cached or not, but it's explicitly requested, isn't it?

like image 7
algorhythm Avatar answered Nov 20 '22 17:11

algorhythm


Your fiddle tries to load a resource from another domain via ajax: cross domain request

I think I created a better example. Here is the code:

<img src="smiley.png" alt="smiley" />
<div id="respText"></div>

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
  $(window).load(function(){        
    $.get("smiley.png", function(){
      $("#respText").text("ajax request succeeded");
    });
  });
</script>

You can test the page here.

According to Firebug and the chrome network panel the image is returned with the status code 200 and the image for the ajax request is coming from the cache:

Firefox: Firebug

Chrome: chrome network panel

So I cannot find any unexpected behavior.

like image 2
PSanetra Avatar answered Nov 20 '22 18:11

PSanetra


Cache control on Ajax requests have always been a blurred and buggy subject (example). The problem gets even worse with cross-domain references.

The fiddle link you provided is from jsfiddle.net which is an alias for fiddle.jshell.net. Every code runs inside the fiddle.jshell.net domain, but your code is referencing an image from the alias and browsers will consider it a cross-domain access.

To fix it, you could change both urls to http://fiddle.jshell.net/img/logo-white.png or just /img/logo-white.png.

like image 1
Sérgio Castelani Avatar answered Nov 20 '22 18:11

Sérgio Castelani