Support for Imgix in Imager for Craft

No more execution timeouts? No more memory issues? Low TTFB no matter how many transforms you create? WebP delivered automatically? Jpeg-2000 and Jpeg-XR?
It might be just a config setting away!

As I touched upon in my last article, the biggest issue people face with Imager in Craft is long loading times (TTFB) when image transforms need to be created for the first time, causing occasional execution timeouts and out of memory issues. 

What if there were a simple switch that you could flick and all of your problems would just go away? Sound to good to be true? Well, it's not, and the switch is named Imgix.

What is Imgix and why do you need it?

The truth is, your web server is good at a lot of things, but resizing and manipulating images is not one of them. There is a reason why your computer has both a CPU (central processing unit) and a GPU (graphics processing unit). Your webserver doesn't; it only has a CPU (or four).

Imgix is a service (or, Software as a Service, SaaS, according to popular jargon) that lets you outsource the job of transforming images from your server to their massive, specialised platform (seriously, you need to read this article!). Think of it as a GPU in the cloud.

Imgix is accessed through what is best described as a URL API. The following image illustrates the different components of an Imgix URL:

The Imgix domain is the domain for the source you create in their service, the path is the path to your image in the location you have specified that your image is publicly available at, and the query string specifies what transformations you want to perform on the image. Super simple, right? Imgix's URL API has a ton of functionality and is well documented.

As if that wasn't enough, Imgix uses Fastly for edge caching, which will act as a CDN for your transformed images. 

Imgix + Imager = ?

When adding Imgix support for Imager, the main goal was to make it super simple to: 1) retro-fit it to existing projects, and 2) disable it if you for some reason want to stop using it in the future. If Imgix suddenly goes bust, or your client no longer wants to pay for hosting, it would be nice to not have to rebuild all of your templates, right? 

The result is that all you need to do if you're already using Imager in your project, is: 

  1. Sign up to Imgix and tell Imgix where your images are. 
  2. Update Imager to version 1.6 and configure it for Imgix support.
  3. There's no third step. You're done.

Admittedly, there might be a bit more to it depending on what functionality in Imager you use, but for all basic transforms it'll just work after enabling Imgix support. 

Interested? Let me guide you through the steps above in more detail.

Signing up to Imgix and setting up a source

The first step is easy enough, head over to, click "Sign up", fill in your details, and you're ready to go.

After signing up, you'll get the choice to create a new source straight away. Go ahead, click "Add a source" to get going.

There are three different types of sources to choose from:

  • Amazon S3, which connects to an S3 bucket.
  • Web folder, which connects to any (one) publicly accessible URL (ie, your server).
  • Web proxy, which lets you use Imgix with any images on the internet that are publicly accessible.

Which one you should choose depends on your project. 

  • If you have your images on an S3 asset source in Craft and you don't need to transform images from any external sources it's a no-brainer. Create an Amazon S3 source.
  • If you need to transform images from different sources, it's also a no-brainer. Create a Web proxy source.
  • If you have the images on your own server, you could choose Web folder, or you could also go for Web Proxy.

In fact, Web proxy would work for all these scenarios, so if you're in doubt, choose that. 

One thing to consider when setting everything up is how you want things to work during development. If you have the assets locally on your computer during development (ie, not publicly accessible), you either have to disable Imgix in your dev environment or set up some kind of dynamic DNS that let's Imgix access the files on your computer. In general, everything is a lot easier if the assets are on some kind of cloud service, so I recommend using one.

After creating your source, you'll find it under "Sources" in the main menu. You can edit the source to add more Imgix-domains or your own custom domains, add default settings, default image, error image, cache headers, and more.

Configuring Imager for Imgix support

In Imager's configuration file, you'll find some configuration settings related to Imgix (prefixed "imgix"). Copy these to your own imager.php config file in the config folder. Remember that you can use all of Craft's multi-environment functionality to set up different settings for different environments.

Here's an example of my config setting for the (web proxy) source above:

// Enables Imgix integration
'imgixEnabled' => true,

// An array of Imgix domains connected to your bucket
'imgixDomains' => [''],

// Enables the use of https for image urls
'imgixUseHttps' => true,

// Sign key (also called token). This is needed for all web proxy sources. And a good idea in general.
// You find this on the details page for your source, click the "Show token" button.
'imgixSignKey' => 'A8UdjgdrGRcejXTU',

// Imager needs to know whether the source is a web proxy or not. 
'imgixSourceIsWebProxy' => false,

// When using multiple domains for one source, this decides what sharding strategy Imgix will use.
'imgixShardStrategy' => 'cycle',

// When transforming external images, Imager might have to download the external file to be
// able to figure out what the size of the transformed file is. Disable for even better performance.
'imgixGetExternalImageDimensions' => true,

And that's about it!

Using Imager with Imgix

If your source is set up correctly and the settings are correct, you should now be able to use Imager exactly as you would without Imgix. Imager accomplishes this by translating the normal transform parameters to the Imgix URL API equivalents. Here are some more examples:

{% set transformWidth = craft.imager.transformImage(image, { width: 600 }) %}
{{ transformWidth.url }}
{# Outputs: #}

{% set transformRatio = craft.imager.transformImage(image, { width: 600, ratio: 16/9, position: '30% 60%' }) %}
{{ transformRatio.url }}
{# Outputs: #} 

{% set transformFormat = craft.imager.transformImage(image, { width: 600, format: 'png', pngQuality: 20 }) %}
{{ transformFormat.url }}
{# Outputs: #}

You'll probably also see some more parameters added to the query string, like ixlib showing the version of the imgix library, a hashed key if you're using secure URLs, etc. But you get the idea. 

You'll notice that your TTFB doesn't even move, no matter how many transforms you add to a page. Just for fun, try this:

{% set transformedImages = craft.imager.transformImage(image, 
    [{ width: 100 }, { width: 2000 }], 
    { ratio: (16/9) }, 
    { fillTransforms: true, fillInterval: 50 }) 

{% for transformedImage in transformedImages %}
    <img src="{{ transformedImage.url }}"><br>
{% endfor %}

If you aren't using fillTransforms and fillInterval already, you should have a look at the docs.

That code should output 40 (!) images ranging in width from 100px to 2000px. You might notice that the first time Imgix encounters a new source image, the loading time for the actual image might go up a little bit (like 1-2 seconds), but the TTFB is still as low as it was before you started adding image transforms to your template. 

All the basic transform functionality in Imager, like sizes, crop modes, formats, quality, etc, will automatically be translated into something that Imgix understands. In addition you can use all of the Imgix API parameters, either by adding them directly to your transform object, or to the imgixParams transform parameter. So even though Imager doesn't support cropping with face detection, you can do it by setting the fit parameter which will override Imager's mode like this:

{% set transformedImage = craft.imager.transformImage(image, 
   { width: 300, height: 300, fit: 'facearea'  }) 

If the image contains one or more faces, Imgix will do its best to crop to that area (works some of the time, but not always, so not sure if I'll ever use it).

You could even use only imgix style parameters, if you're really, really sure that you'll never, ever need to disable it:

{% set transformedImage = craft.imager.transformImage(image, 
   { w: 300, h: 300, fit: 'crop', crop: 'edges'  }) 

Resizes to 300x300, and uses edge detection for cropping.

If you're retrofitting Imgix to an existing project you might want to introduce it step by step into your templates to make sure that everything works as it should. You can do this by disabling Imgix by default in your config with 'imgixEnabled' => false, and then enabling it on the transforms where you want to use it:

{% set transformedImages = craft.imager.transformImage(image, 
    [{ width: 600 }, { width: 800 }, { width: 1000 }], 
    { ratio: (16/9) }, 
    { imgixEnabled: true }) 

My new BFF, auto!

Apart from the benefits to server performance, the biggest benefit (IMHO) of using Imgix is the auto parameter. By setting auto: 'compress,format' on your transforms, Imgix will:

  • Remove meta data and compress the images.
  • Deliver the images in WebP format to Chrome.
  • GIF and PNG images without transparency will automatically be converted to JPEG.
  • Tramsparent images will be converted to PNG8.

This is nothing short of a gamechanger. Imgix will automatically deliver WebP images to Chrome, and you'll never have to worry about clients uploading images in PNG format that doesn't need to be – and there's nothing more you need to do to make it work! Wave goodbye to flaky WebP server support and post-optimization tools. With auto format enabled, Google PageSpeed will be sooo happy.

{% set transformedImages = craft.imager.transformImage(image, 
    [{ width: 600 }, { width: 2000 }], 
    { ratio: (16/9), auto: 'format,compress' }, 
    { fillTransforms: true }) 

What Imager doesn't do

There are a few things that Imager doesn't automatically do for you:

  • Effects are not automatically converted to the Imgix equivalents. 
  • Watermarks are not automatically converted to the Imgix equivalents.
  • The Imager_Imgix model which Imager returns to the template lacks some of the functionality that Imager_Image has. Most notably, you won't be able to get a base64 version of the image (yet).
  • A few things, like the croponly value of the mode transform parameter doesn't have an equivalent in Imgix as far as I can tell.
  • Imgix has some nifty ways of getting metadata about an image through the API, I haven't looked into that yet.

Instead of trying to bridge the gap between how the different effects work in GD and Imagick (which already is a mess), and trying to find the equivalent values for the effects in Imgix, I've left that up to you. You can easily add the parameters needed as shown above. And you can also easily create a base64 version of an image by disabling Imgix support on a given transform. More Examples:

{# Defining the sharpen effect both the Imager way (with sharpen) and for Imgix (with sharp) #}
{% set transformedImages = craft.imager.transformImage(image, 
    [{ width: 600 }, { width: 800 }, { width: 1000 }], 
    { ratio: (16/9), effects: { sharpen: false }, imgixParams: { sharp: 45 }}) 

Imgix uses a value between -100 and 100 to define sharpen amount, whereas GD uses true/false, and Imagick an integer. 

{# The example shows disabling Imgix support when creating a 6x6px gif for inlining, 
while using Imgix for the main transforms. #}

{% set transformedImageInlined = craft.imager.transformImage(image, 
    { width: 6, height: 6 }, { format: 'gif' }, 
    { imgixEnabled: false }) 
{% set transformedImages = craft.imager.transformImage(image, 
    [{ width: 600 }, { width: 1800 }], 
    { ratio: 16/9, format: 'jpg', auto: 'compress,format' }, 
    { fillTransforms: true }) 

<div style="background-image: url({{ transformedImageInlined.getDataUri() }});">
    <img class="product-info__image-img lazyload" src="{{ craft.imager.base64Pixel() }}"
        data-srcset="{{ craft.imager.srcset(transformedImages) }}">

Just your average lazysizes setup with a wicked, blurred background image.


I knew about Imgix for years, but I didn't look too deeply into it for two reasons: I felt uncomfortable about going all-in and making myself dependant on it, and I was unsure about how expensive it would turn out to be over time.

The first problem is all gone, now that you can turn Imgix support on and off in Imager with little or no extra work. 

After talking to someone on the Craft Slack who is using Imgix for a big site, it seems like the pricing is actually quite reasonable. There are three components to Imgix's pricing model: how many master images you have, the bandwidth used, and a minimum charge of $10 per month. 

You pay $3 per 1000 master images you have and, as far as I can tell, the count is reset every month so you don't necessarily pay for old, stale images that are not used anymore. We have a few sites with tens of thousands of images, but for most normal sites we're usually within the 1000 image limit. Bandwidth is charged at $0.08 per GB, which may sound like a lot. But remember, this will eliminate the need for a CDN, so if you're already using something like CloudFront, which charges $0.085 per GB (first 10TB), you're actually just moving the cost (and maybe even save a few cents). And, there aren't really any limits or tiered pricing for the account, so you can host as many sources as you'd like from one. So, if you're doing hosting for your clients, you could gather all the clients in one account and eliminate the minimum charge issue.

All in all, when seeing the benefits to performance and workflow that Imgix provides, I think that's a steal! 

Final words

This turned out to be a monster of a blog post, so I can't imagine that anyone who gets to this point has any more questions. But, if you do, don't hesitate to post a comment below or contact me on the Craft Slack (@aelvan). 

Get Imager and go craft!

Support open source. Support Imager. Buy beer.
Imager is licensed under a MIT license, which means that it's completely free, open source software, and you can use it for whatever you wish without paying a dime. But, if you use and like Imager, and want to support the development, you now have the chance. Just head over to Beerpay and donate a beer or two! Code flows so much better with a dark and creamy porter on the desk. 

Posted in ⟶

  • Craft