Webserver Caching Issues

76 Views Asked by At

Everything started when I was trying to improve the loading times of thumbnail pictures in a gallery of sort i have. the paths of all images come from a JSON file/string/response along with several details about them, in order to build the UI.

My first thought was to cache them with JavaScript itself:

for( let i = 0; i < data.length; i++ ) {

    let thumbnail = new Image();
    thumbnail.src = data[ i ].thumbnail;
}

ES2015 with BabelJS being used

I know it's not the best approach as I should load them on demand (i.e. when accessing their offsets in JSON) or even asynchronously with AJAX. In fact I did try both ways too, but I didn't manage to make the other data wait for the image to be fully loaded before fill the UI.

But this is a matter for later ^_^

So, I checked the Chrome Inspector and under Network tab all JPG were being loaded as expected but when actually using the images, modifying the src attribute of the container designed for the preview (see below), every image was being loaded again.

$( 'figure img' ).attr( 'src', data[ current ].thumbnail );

This is just a fragment. 'current' is the gallery counter to navigate through the JSON

After exhausting my testing skills I ended up believing the issue was my server.

I'm currently using PHP Internal Webserver for development because, at least until now, I didn't have the need of a complete server solution. It's bad to have several command-line windows opened, but anyway...

For the record, the webserver is started like this:

php.exe -c "path\to\php.ini" -S "0.0.0.0:8080" -t "." "routing.php"

Running under Windows environment >.<

My previous routing file was very blunt, just returning false if requesting an image, js, css, font... so, after reading several topics in here, I changed it trying to include some caching:

<?php

if( preg_match( '/\.(js|ico|gif|jpg|png|css|eot|svg|ttf|woff|php)$/', $_SERVER["REQUEST_URI"] ) ) {

    date_default_timezone_set( 'America/Sao_Paulo' );

    $file = sprintf( '.%s', $_SERVER['REQUEST_URI'] );

    if( ! file_exists( $file ) ) {
        header( 'HTTP/1.1 404 Not Found' );
    }

    $eTag = md5_file( $file );

    if( $eTag !== FALSE ) {
        header( 'Etag: ' . $eTag );
    }

    header( 'Cache-Control: public, max-age=2592000' );

    $mTime = filemtime( $file );

    if( $mTime !== FALSE ) {

        $GMD = gmdate('D, d M Y H:i:s', $mTime );

        header( sprintf( 'Last-Modified: %s GMT', $GMD ) );

        if( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {

            $mSince = strtotime( $_SERVER['HTTP_IF_MODIFIED_SINCE'] );

            if( $mSince !== FALSE && $mSince == $GMD ) {

                header( 'HTTP/1.1 304 Not Modified' ); return false;
            }
        }

        if( isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ) {

            $noneMatch = str_replace( '"', '', stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) );

            if( $noneMatch == md5( $file . $mTime ) ) {
                header( 'HTTP/1.1 304 Not Modified' ); return false;
            }

            if( $noneMatch == $eTag ) {
                header( 'HTTP/1.1 304 Not Modified' ); return false;
            }
        }
    }

    // Resource not cached yet

    header( 'Content-Type: '   . mime_content_type( $file ) );
    header( 'Content-Length: ' . filesize( $file ) );

    return false;
}

include __DIR__ . '/index.php';

The performance was considerably better but, testing with one single file, directly, the first load took ~300ms, the second ~5ms, the third ~300ms, the fourth ~5ms and so on. o.O

Then I tested it in the whole Application and the caching problem persisted, although, theoretically, the images were already loaded with JavaScript and stored in the Image Object, they were loaded once again when their respective offset in the JSON was accessed through the navigation.

Also, when refreshing the page multiple times I didn't have the same "jumping" response times as with direct access. everything not coming from CDNs (jQuery, Bootstrap...) were being loaded again, again and again. >:(

After more research, two of the culprits I've found were the Cache-control not being set and the overall status code.

I couldn't find a way to send the 304 Header using the PHP Internal WebServer, not because I don't know how, afterall the code is the same, but because all implementations I've seen depends on some $_SERVER entries that, as far as I know, are provided by Apache (and maybe other dedicated server applications).

Also, for some reason, in Chrome Inspector, opening the Headers section of a resource, I don't see in there the Cache-control directive under Response Headers even though I did set it, as you can see, with ~30 days of expiration.

More than how could I solve these issue I would like to understand why is this happening.

0

There are 0 best solutions below