Better image caching by browsers

deanmoses Member
edited January 2018 in General support

I have a custom front end to Zenphoto and am trying to figure out the best way to get browsers and CDNs to permanently cache Zenphoto images.

This is important for me because I use a CDN to distribute the images to countries around the world. Right now the CDN can't cache images for very long at all, and going back to my Zenphoto instance significantly slows down the perceived load times for end users in distant locations. Also, CDNs use HTTP/2, which pipelines all those itty bitty thumbnails through a single HTTP connection, which makes it significantly faster. Most shared hosting services such as the type Zenphoto installations often run on do NOT support HTTP/2.

My Zenphoto web server can't serve the sized Zenphoto images with a far future expires header right now because any image could change at any moment (that's why I use Zenphoto, thanks!)

A common pattern to solve this is to request images using a URL that changes when the actual image changes. For example:

My <img src> URLs currently look like this:
mydomain.com/zenphoto/cache/my_album/my_photo_1024.jpg

I want them to look like this:
mydomain.com/zenphoto/cache/my_album/my_photo_1024.jpg?t=SOME_TIMESTAMP

... where SOME_TIMESTAMP is the timestamp of when the sized image or thumbnail was generated.

I don't see a way to get this through the API... am I missing something? I could go hunt for the file's last modified date on disk, but I'd much rather stay within the Zenphoto framework.

Thanks!

Comments

  • By the way, also consider this a feature request: automatically including a cachebuster in the URL and serving the images with a far future expires header would be a way to speed up all Zenphoto installations everywhere.

  • acrylian Administrator, Developer
    edited January 2018

    There is nothing for this. Cache image files are technically those generated (resized, cropped) from the originals as plugins or themes need them. These generated files are actually static and don't change by themselves unless you manually clear the image cache.

    So mydomain.com/zenphoto/cache/my_album/my_photo_1024.jpg points to an image with the largest side with 1024px. If you now request for example 500px a another new image will generated. The above will neither be removed nor changed. Because themes and plugins can request any size and it is not globally known which that might be. It is also not stored anywhere except on the file system itself. Zenphoto looks if the size requested is cached already and uses it. If not it generates it and uses it. It all goes through the image processor.

    Here is an info graphic how this image caching works:
    http://www.zenphoto.org/news/caching/

  • I understand that Zenphoto generates the resized images on demand.

    From a browser's standpoint, the resized images Zenphoto caches on disk must be considered dynamic, not static, because they change if you:
    - upload a new version of the underlying image
    - re-cut a thumbnail in Zenphoto's admin UI

    That's why I can't set a far future expires header when serving these images.

    I don't understand why you say "there's nothing for this". Zenphoto knows whether there's a resized version on disk when generating a HTML page. I can tell because the HTML contains either a URL to the resized cache image on disk or to the i.php URL.

    Since Zenphoto knows there's a resized version on disk at the point it generates HTML, it seems like it could also retrieve its last modified date.

  • acrylian Administrator, Developer
    edited January 2018

    considered dynamic, not static, because they change if you:

    • upload a new version of the underlying image

    No, actually if the file name is the same the cached image does not change unless you clear the cache manually.

    • re-cut a thumbnail in Zenphoto's admin UI

    No, as explained above you get a complete new cached image, existing cached images don't change.

    I don't understand why you say "there's nothing for this". Zenphoto knows whether there's a resized version on disk when generating a HTML page. I can tell because the HTML contains either a URL to the resized cache image on disk or to the i.php URL.

    It "knows" by the files within the cache folder. It simply looks if a matching file to the requested size exists and if not creates it from the original. It does not look at file time or anything. There is nothing stored in the database or else except what is on the file system.

    One specific size is indeed stored in the database and that is the default thumb cropping (images table). Themes and plugins can request any additional size which is not stored.

  • acrylian Administrator, Developer
    edited January 2018

    The function doing the check and creation if neede is within zp-core/functions-image.php called cacheImage().

  • deanmoses Member
    edited January 2018

    acrylian, I'm looking at the code that generates the URL to either i.php or the cache file. It's getImageURI() in functions-basic.php here in Github.

    The URL generation code does look at the last modified time of the cached image. If the cache image is older than the original, it returns i.php instead of the URL to the cache image.

    This is what I mean by dynamic: the contents of the cache file changes when the original changes, and therefore browsers can't cache it forever.

    A small change to that function would allow browsers and CDNs to cache the cache file forever. Here's the line that returns the URL to the cache image:

    return WEBPATH . '/' . CACHEFOLDER . imgSrcURI($cachefilename)
    

    It could add a cachebusting query string:

    return WEBPATH . '/' . CACHEFOLDER . imgSrcURI($cachefilename) . '?t=' . filemtime(SERVERCACHE . $cachefilename)
    

    If the cache image URLs looked like that, the web server could be configured to send a "cache forever" response header, allowing browsers and CDNs to cache that particular version of the image forever.

  • acrylian Administrator, Developer
    edited January 2018

    Indeed, you are right. Well, apparently I can't memorize every detail ;-) My bad, I did look only quickly. Thanks for pointing out. The one I named does the actual cached image if needed though.

    Okay, so it should not do any harm to add that to the url. I'll add that tomorrow (night over here already).

  • Thanks!

    LMK if you want me to file an enhancement request on github with the reason for the request.

  • acrylian Administrator, Developer

    Not necessary as we discussed this here and I already agreed to add it!

  • acrylian Administrator, Developer

    It's in the support build now as ?cached=<timestamp>.

  • MarkRH Member
    edited January 2018

    Curious, I'm hoping there's an option to not have this cache=timestamp thing in the settings for if and when I update my code?

    Saying that, I do use something similar with other images that change rapidly, such as my Winamp now playing images here: http://www.markheadrick.com/winamp-history.php where it can be a new image every few minutes.

  • acrylian Administrator, Developer

    Currently there is no option but we certainly can add one. In any case the absence of this parameter in older links will not break anything. It has no technical meaning in Zenphoto itself.

    I admit I didn't fully understand the winamp playing images part ;)

  • MarkRH Member
    edited January 2018

    Well, I added the timestamp /winamp/playing.gif?v=1517000011 so when I clicked around on my browser it, it would always load the newest; otherwise, I'd always have to force a browser refresh. Anyway, as I'm playing music in Winamp, it updates my website. (I am probably the only one that cares but there it is LOL).

  • acrylian Administrator, Developer

    That is indeed a quite individual usage ;-)

Sign In or Register to comment.