Image Optimizer reference

The Fastly Image Optimizer (IO) manipulates and transforms images as they pass through the Fastly network, and caches optimized versions of them.

IO shielding illustration

IO interacts with the fetch node in the shield POP (see clustering and shielding). IO-enabled requests that ultimately load an image from origin will transit the shield fetch node twice. This results in both the original and the transformed image being cached at the shield, while just the transformed image will be cached at the edge.

IMPORTANT: If using custom VCL, be aware that at the shield POP, VCL subroutines that normally run on a fetch node (vcl_miss, vcl_pass, and vcl_fetch) will run upstream (i.e., on the origin side) of the image optimizer. As a result, these subroutines handle the request for the original image, while all other VCL subroutines on the shield POP (and all subroutines on the edge POP) are part of the request for the transformed image.

Enabling image optimization

Image optimization is an optional upgrade to Fastly service plans. If you have not yet purchased IO, contact sales.

If your Fastly account is entitled to use image optimization, it can be enabled on an individual service in the web interface or by enabling the image_optimizer product using the product enablement API.

Once enabled, you can trigger Fastly to pass a request through the image optimizer by adding the x-fastly-imageopto-api header in vcl_recv. This is usually done conditionally, so that it applies only to image requests:

sub vcl_recv { ... }
Fastly VCL
if (req.url.ext ~ "(?i)^(?:gif|png|jpe?g|webp)\z" || req.url.path ~ "^/images/") {
set req.http.x-fastly-imageopto-api = "fastly";

HINT: Typically requests are flagged for IO processing using a path match as above, but if you prefer you can also use the content-type of the response.

Services that use image optimization are required to enable shielding.

These steps can also be performed via the web interface.

Other options may also be specified in the x-fastly-imageopto-api header value. See caching below.

WARNING: IO will only process requests that are eligible for caching. See limitations.

Default behavior

Enabling IO will activate a set of standard transformations and filters intended to provide 'good defaults' that will not change the image's dimensions or visual fidelity. You can modify these in the web interface. Many of these can be further overridden or customized by providing explicit configuration directives as query parameters or headers:

  • jpg, pjpg, and webp images will optimized automatically with default quality of 85.
  • All metadata (for example, EXIF, XMP, or ICC) will be removed.
  • If the image contains an ICC profile, the data will be applied directly to the image to ensure color output is correct. If the image doesn't contain an ICC profile, a default profile is added. These behaviors do not apply to png or webp images.
  • If the source image contains orientation metadata, this orientation will be applied directly to the image data and metadata will be removed.
  • The original Vary response header from origins will be removed by IO. See limitations.


Often the default transformations will save a significant amount of data without any further effort, but you can customize the behavior with query parameters and headers. For example, the following query string will direct the IO processor to resize the image to 300 pixels wide and crop it to a 16:9 ratio, centered on the most important visual content, including the detection of faces:


Query parameters

We recognize the following parameters in the query string of the image request:

autoEnable optimization features automatically.
bg-colorSet the background color of an image.
blurSet the blurriness of the output image.
brightnessSet the brightness of the output image.
canvasIncrease the size of the canvas around an image.
contrastSet the contrast of the output image.
cropRemove pixels from an image.
disableDisable functionality that is enabled by default.
dprServe correctly sized images for devices that expose a device pixel ratio.
enableEnable functionality that is disabled by default.
fitSet how the image will fit within the size bounds provided.
formatSpecify the output format to convert the image to.
frameExtract the first frame from an animated image sequence.
heightResize the height of the image.
levelSpecify the level constraints when converting to video.
metadataControl which metadata fields are preserved during transformation.
optimizeAutomatically apply optimal quality compression.
orientChange the cardinal orientation of the image.
padAdd pixels to the edge of an image.
precropRemove pixels from an image before any other transformations occur.
profileSpecify the profile class of application when converting to video.
qualityOptimize the image to the given compression level for lossy file formatted images.
resize-filterSpecify the resize filter used when resizing images.
saturationSet the saturation of the output image.
sharpenSet the sharpness of the output image.
trimRemove pixels from the edge of an image.
widthResize the width of the image.

Header parameters

x-fastly-imageopto-montageCombine up to four images into a single displayed image.
x-fastly-imageopto-overlayOverlay one image on top of another image.

Processing order

Although the parameters can be specified in any order, we normalize the transformation sequence within our system to the following order:

  1. precrop
  2. trim
  3. crop
  4. orient
  5. width height dpr fit resize-filter disable enable
  6. pad canvas bg-color
  7. brightness contrast saturation
  8. sharpen
  9. blur
  10. x-fastly-imageopto-overlay
  11. format auto optimize quality profile level metadata

If an x-fastly-imageopto-montage header is specified, all other IO parameters and headers are ignored.


The original image is only requested and cached once, even if distinct requests might demand versions of the image that are transformed differently.

For example, a user on a desktop device triggers a request for image.png?width=1600, while a different user on a mobile device requests image.png?width=400. These requests are considered to be different for the purposes of caching transformed images and will result in different cache objects, but the request to origin is image.png (with no query parameters).

IMPORTANT: Image optimization will only be applied to cacheable responses, and for services created on or after May 2, 2023, a minimum cache TTL of 60 seconds is enforced on transformed images (the original image is also cached and has no minimum TTL).

A request with image optimization enabled cannot make use of cached content created by a request that did not have image optimization enabled. Activating a configuration that enables IO on a large number of images may therefore result in a temporary, sharp decrease in cache hit ratio and corresponding increase in traffic to origin. See enabling features gradually if you need to avoid this.

Query string removal

By default, all query parameters are stripped, even those that are not recognized by the image optimizer. To allow passthrough of unknown query parameters, opt-in by changing the value of the X-Fastly-Imageopto-Api header:

set req.http.X-Fastly-Imageopto-Api = "fastly; qp=*";

With this opt-in enabled, a request for image.png?width=300&something=foo would trigger the original image to be loaded from your origin server with a request URI of image.png?something=foo. The known parameter has been removed, but the unknown parameter was passed through.

WARNING: Enabling the qp=* override may reduce the image cache hit ratio and increase traffic to your origin servers.

Purging images from the Fastly cache

The query string normalization described above happens prior to the vcl_hash subroutine and the IO query parameters are saved in a secondary hash to allow separation of the variants. This allows all variants of the same image to be purged in a single request.

To purge the original image and all variants, make a purge request without IO parameters:

$ curl -X PURGE

It is not possible (and likely never desirable) to purge a single, transformed variant (because a subsequent request for that variant would produce the same result). It is also not possible to purge an original image without purging the transformed images, because subsequent requests would potentially result in caching an inconsistent set of images.

Testing and debugging

When images pass through the IO processor, it will add a header called fastly-io-info with the following form:

fastly-io-info: ifsz=108501 idim=827x788 ifmt=jpeg ofsz=13066 odim=300x286 ofmt=jpeg

This header provides detail on the input format (ifmt), dimensions (idim), and size in bytes (ifsz), and also the output format (ofmt), dimensions (odim), and size in bytes (ofsz).

Click 'RUN' on the interactive demo below to create an ephemeral Fastly service and make a request through it for an image of former US president Barack Obama.

In this demo, we also show how to calculate the size ratio between the original image and the transformed image by post processing the fastly-io-info header, dividing ofsz by ifsz, and multiplying by 100 to yield a percentage.

Errors and warnings

If an error occurs during image processing, a fastly-io-warning or fastly-io-error header is added to the response. The following error scenarios will prevent the image from being processed:

  • Image exceeds maximum dimensions
  • Image could not be parsed
  • Not a supported image format
  • Unsupported Content-Encoding
  • Gzipped body exceeds maximum length
  • Gzipped body could not be decoded
  • Invalid status
  • Response is pass
  • Response is not cacheable

Authenticating IO requests

You may prefer not to expose image optimization parameters publicly. For example, allowing raw image transform parameters to be accepted on inbound client requests may allow an end users to gain access to a higher resolution of your source images than you had intended. One solution is to implement transformation classes. In this example, we map some precomposed IO queries to general labels like "large", "medium", and "small", so that the image request from the client is ?class=large rather than ?crop=16:9,smart&width=640.

While not essential, the demo above also includes logic to remove any query parameters that are not the custom class param, preventing end-user customization of the IO behavior.

Using optimized images and videos in HTML

The Fastly Image Optimizer allows you to generate images to suit the way you want to lay them out on the client device, but it's up to you to prompt the client device to request the most appropriate image variant. If your client device is a web browser and you are serving HTML, then you can make use of responsive images web technologies.

Pixel density

Using the srcset attribute, it's possible to define images to be used at different display pixel densities, which works well with the width and dpr parameters.

<img srcset="/io-demo/image.jpg?width=320&dpr=1.5 1.5x,
/io-demo/image.jpg?width=320&dpr=2 2x"

Art direction

Use the HTML5 <picture> element to deliver different image crops at different browser viewport sizes, a technique called art direction:

<source srcset="/io-demo/image.jpg?width=600&crop=16:9" media="(min-width: 600px)"/>
<img src="/io-demo/image.jpg?width=320&crop=1:1"/>

Image type

The <picture> element can also be used to provide different formats, allowing the browser to choose a format that best suits its needs and is compatible with the formats that it supports:

<source type="image/webp" srcset="/io-demo/image.webp"/>
<source type="image/png" srcset="/io-demo/image.png"/>
<img src="/io-demo/image.jpg"/>

Learn more about which image formats are supported in which browsers.

Replacing GIFs with inline video

The animated GIF format benefits from being universally compatible and plays inline on all major web browsers. However, with the introduction of the playsinline attribute, the <video> element is now usable in most of the same use cases as GIF. Video formats typically offer far better compression than GIF, and the Image Optimizer can be used to convert existing GIFs into MPEG 4 video:

<video width="300" height="224" playsinline autoplay muted loop>
<source src="/io-demo/image.gif?format=mp4" type="video/mp4" />

Origin failover

Sometimes you may want to have more than one origin, often for redundancy purposes (e.g., primary: Amazon S3, secondary: Google Cloud Storage). If a resource does not exist in the primary origin, or the primary is unavailable, then we can perform a restart and try the secondary origin. We have a code example showing how to do this for normal resources, but there are some caveats to implementing this solution with IO:

  • With IO enabled, query parameters will be stripped from the request URL after vcl_hash, so won't be present on the restarted request. This can be solved by storing the original request URL in a temporary request header in vcl_recv and reinstating it upon restart.
  • To ensure that IO draws from the same pre-transform image cache, it's important that shielding is enabled on the primary backend, and that the restart happens on the shield.

Here is an example of how to do this:

Limitations and constraints

  • IO will only process requests that are eligible for caching. If the request passes through vcl_pass before reaching origin, the response image will not be transformed.
  • IO is incompatible with the Segmented Caching feature. If IO is enabled, Segmented Caching is disabled automatically.
  • The maximum input image file size is 50 Megabytes.
  • The maximum input image dimensions are 12,000 x 12,000 pixels.
  • The maximum output image dimensions are 8,192 x 8,192 pixels (8K Ultra HD).
  • The maximum output image area for AVIF images is 4,096 x 4,096 pixels. AVIF is a premium feature of Fastly's Image Optimizer and choosing it as an encoding format will increase your bill. Specific charges will appear on your service order.
  • The maximum number of frames an animated GIF can contain is 1,000.
  • We support input images in GIF, JPEG, PNG, or WEBP format.
  • We can produce output images in AVIF, GIF, JPEG, MP4, PNG or WEBP format.
  • The Vary response header is removed by the IO processor automatically after vcl_fetch execution at shield POPs, so the original Vary response header from origins is not passed to downstream clients including edge POPs. This is to avoid multiple variations of the same original of transformed image being cached individually. We recommend also removing the Vary header in your service's vcl_fetch to ensure this behavior is also applied for requests received directly by shield POPs (the relevant VCL is included in our standard VCL boilerplate).

HINT: If, in order to satisfy CORS, you need to apply an Origin response header that matches the inbound Origin request header, we recommend adding that header in vcl_deliver at edge POPs. This allows one, single copy of the response to be stored in cache and then "customized" as needed for requests that come from different client origins. This advice is equally applicable to all requests, but in the case of IO, it is essential since it is not possible to cache variants for responses that pass through IO.

sub vcl_deliver { ... }
Fastly VCL
if (fastly.ff.visits_this_service == 0 && resp.http.Access-Control-Allow-Origin) {
set resp.http.Vary:Origin = "";