Search code examples
htmlbrowsersafarixmlhttprequestmobile-safari

Overcoming connection limitation with mobile safari


I recently discovered that mobile safari (on iOS 9.1 - though unsure about older safari versions) has an unfortunate problem with multiple connections and images. If you have six images when you load a page, it will delay XHR requests that loads after those six images by a huge lag time (order of 30s).

For example, I loaded the following using a standard NodeJS/Express server and saw giant delays before the alert showed up - despite the fact that all there images are just a couple of KB and I can visibly see that they are all loaded. The dev console also shows the images loaded but not the XHR request. The duration it took to download the file was very small but the latency was huge.

This is not a problem on any other browsers (mobile chrome, regular safari, etc).

Sample problem html:

<!DOCTYPE html>
<html>
<head>

</head>
<body>

<img src="/static/images/home/payment.png">
<img src="/static/images/home/couple-present-mobile.jpg">
<img src="/static/images/general/welcome.jpg">
<img src="/static/images/general/arrow-down.png">
<img src="/static/images/general/arrow-right.png">
<img src="/static/images/general/check.png">
<script>
    
            var url = '/static/test.html'
            var xhr = new XMLHttpRequest();
            xhr.open('GET', encodeURI(url));

            xhr.onload = function() {
                  alert(url) //This takes forever
            };
            xhr.send();

</script>
</body>
</html>

An odd thing is if you ran the XHR request BEFORE there were 6 images, it would work perfectly. In fact, if you even did something like this one below, it was fine. I think this works because the background CSS image must retrieve the URL after the XHR is initiated.

Replace one of the img tags with a background css image and it works:

<!DOCTYPE html>
<html>
<head>

    <style>
        .test {
            background-image: url("/static/images/home/check.png");
            height: 400px;
        }
    </style>

</head>
<body>

<img src="/static/images/home/payment.png">
<img src="/static/images/home/couple-present-mobile.jpg">
<img src="/static/images/general/welcome.jpg">
<img src="/static/images/general/arrow-down.png">
<img src="/static/images/general/arrow-right.png">
<!--<img src="/static/images/general/check.png">   REMOVE THIS LINE and add the background image instead--> 
  <div class="test"></div>
<script>
            var url = '/static/test.html;

            var xhr = new XMLHttpRequest();
            xhr.open('GET', encodeURI(url));

            xhr.onload = function() {
                console.log(url) //NO DELAYS!
            };
            xhr.send();
</script>
</body>
</html>

Also I found out that just running 7 simultaneous XHR requests also does not cause this problem (such as the below):

<!DOCTYPE html>
<html>
<head>


</head>
<body>


<script>
    var urls = ['/static/images/home/payment.png',
        '/static/images/home/couple-present-mobile.jpg',
        '/static/images/general/arrow-right.png',
        '/static/images/general/arrow-down.png',
        '/static/images/general/welcome.jpg',
        '/static/images/general/check.png',
        '/static/test.html'];

    for(var i = 0, ii = urls.length; i < ii; i++){
        (function(){
            var url = urls[i];

            var xhr = new XMLHttpRequest();
            xhr.open('GET', encodeURI(url));

            xhr.onload = function() {
                console.log(url)
            };
            xhr.send();
        })()
    }




</script>
</body>
</html>

Has anyone come across this problem and figured out a way to deal with it without reducing the number of images or putting them in a sprite or something?


Solution

  • The hack I ultimately used to get around this was to load the images from a different host. For simplicity, if the browser detected that the host was www.domain.com, I would load images it from domain.com and vice versa. You can also just have all your images come from a certain host like images.domain.com and keep your api's and other stuff on another host.

    Not the most ideal or elegant solution, but its super simple to implement and solves the problem.