Optimizing external images

As of today, jampack only processes and optimizes local images available in the static source of files.

What about external images stored in a CDN or remote storage?

Step 1: Config

Because this may not be suitable for everybody we will use our brand new config to optionaly make it available.

image: {
    external: {
      process: 'off' | 'download',

Later, I would like to introduce other options like:

  • add-dimensions-only: Only add dimensions to the images when missing and no image is downloaded.
  • cdn-srcset-when-possible: using image CDN capabilities for resize and image format for srcset images.

Step 2: Download

External images will be download and stored in folder _jampack/ at the root of the static website and they will be processed and optimized as local images.

And with this, we have the demo working!

Step 3: Caching

We don’t want to download all images and reprocess them for every run of jampack.

If the image didn’t change we should not re-download.

Let’s use HTTP Caching!

Adding downloaded images to the cache

jampack already has a cache for processed images located in folder .jampack.

Let use it as well for downloaded images.

  • Processed images by sharp will go to subfolder img.
  • Downloaded images will go to subfolder img-ext.

Let’s ask if the image has changed

jampack will call all external images with If-Modified-Since HTTP header (when image is in cache). The server should respond status 200 OK if a new image exist or 304 Not modified if the image is the same. CDNs are good at that!

If the server respond with 304 Not modified we will then source the image directly from the cache.

# Performance results (Processing 10 external images)
- Without cache => ~8s
- With 304 => ~2.5s
(this will obviously vary with image size and network speed)


But this means we still have to make a HTTP request for each image. For websites with thousands of external images this could still be a performance issue.

If we know the image is still fresh, don’t even ask

In the HTTP response, the server tells us how long we can cache the image and don’t even ask for it.

This is done though headers properties Expires or Cache-Control: max-age.

jampack will calculate the expiration time of the image and take the image directly from the cache without any HTTP call. Exactly like a browser.


# Performance results (Processing 10 external images)
- Without cache => ~8s
- With 304 => ~2.5s
- Direct from cache => ~0.25s

We have now an efficient external image download 👍

External images are usually immutable when served from a CDN for example. So we can expect very high rate of cache use. Only new external images are likely to be downloaded.

Support for no-cache directive

Sometimes images should not be cache because they are generated on demand.

So jampack implements the following directives in HTTP Header:

  • Cache-Control: no-cache
  • Cache-Control: max-age=0, must-revalidate

As explained in HTTP Caching “Force Revalidation”.

What about the same image on multiple pages?

The same image should be downloaded only once. The cache will take care of this usecase 👍

It’s not perfect

There are a little bit more subtleties in proper HTTP cache management. But this first version should be good enough to cover the common ones.

We will improve the cache as we go and as we encounter performance issues or bugs.

The result

jampack has a now an configuration to download and process external images:

image: {
    external: {
      process: 'off' | 'download',

combined with a cache that tries to make it not too slow 💪


  • jampack 0.12.0+