LOG IN SIGN UP
Documentation

Image optimization VCL boilerplate

  Last updated October 23, 2018

If you use the Fastly Image Optimizer (IO) with custom VCL, you should consider using the IO VCL boilerplate. This boilerplate is specially designed to work with IO. It also fixes several potential issues that can arise when using IO with our default VCL boilerplate.

IO VCL boilerplate

Before using the IO VCL boilerplate, review the Image Optimizer documentation and the instructions in our custom VCL guide.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
sub vcl_recv {
#FASTLY recv

  if (req.method != "HEAD" && req.method != "GET" && req.method != "FASTLYPURGE") {
    return(pass);
  }

  # Enable IO for image file-types
  if (req.url.ext ~ "(?i)^(?:gif|png|jpe?g|webp)$")  {
    set req.http.X-Fastly-Imageopto-Api = "fastly";
  }

  return(lookup);
}

sub vcl_fetch {
#FASTLY fetch

  # Unset headers which reduce cacheability for images
  if (req.http.X-Fastly-Imageopto-Api) {
    unset beresp.http.Set-Cookie;
    unset beresp.http.Vary;
  }

  # Check origin caching headers and override / apply defaults
  if (beresp.http.Expires || beresp.http.Surrogate-Control ~ "max-age" || beresp.http.Cache-Control ~ "(s-maxage|max-age)") {
    # Keep origin TTL
    
  } else {
    # Apply a default where origin does not provide TTL
    if (beresp.status == 200) {
      set beresp.ttl = 604800s; # 7 days
      set beresp.http.Cache-Control = "max-age=604800, public";

      # Apply a longer default TTL for images
      if (req.http.X-Fastly-Imageopto-Api) {
        set beresp.ttl = 2592000s; # 30 days
        set beresp.http.Cache-Control = "max-age=2592000, public";
      }

    } else {
      # Apply short TTL for non-200 responses
      set beresp.ttl = 60s;
    }
  }

  return(deliver);
}

sub vcl_hit {
#FASTLY hit

  if (!obj.cacheable) {
    return(pass);
  }
  return(deliver);
}

sub vcl_miss {
#FASTLY miss
  return(fetch);
}

sub vcl_deliver {
#FASTLY deliver
  return(deliver);
}

sub vcl_error {
#FASTLY error
}

sub vcl_pass {
#FASTLY pass
}

sub vcl_log {
#FASTLY log
}

Customizing the IO VCL boilerplate

Read the information in this section before modifying the IO VCL boilerplate.

Shielding

You must use shielding with IO. When a request is received at the edge for a particular variation of an image that isn't cached, the shield passes the request along to the image processors, which pass the request to your origin for the original image. Shielding is important because original images and image variations are cached at the shield. Without shielding enabled, more requests are passed directly to your origin.

Limiting IO passthrough to images

The simplest way to prevent non-image files passing through to IO is to limit application of the header by image file extension.

1
2
3
4
5
6
sub vcl_recv {
  if (req.url.ext ~ "(?i)^(?:gif|png|jpe?g|webp)$" {
    set req.http.X-Fastly-Imageopto-Api = "fastly";
  }
  return(lookup);
}

If the origin doesn't have a file extension or doesn't have valid file extensions, you'll need to determine validity using another method. One common approach is identifying images by path:

1
2
3
4
5
6
sub vcl_recv {
  if (req.url.path ~ "/images/" {
    set req.http.X-Fastly-Imageopto-Api = "fastly";
  }
  return(lookup);
}

Another approach is dedicating the entire service to image assets.

X-Fastly-Imageopto-Api header

The X-Fastly-Imageopto-Api header must be applied unconditionally for IO requests at both edge and shield. We unset this by default to prevent the header from being spoofed. Applying this header at the edge only (wrapping with if (!req.http.Fastly-FF)) can result in unexpected behavior. The cache key is constructed differently based on whether IO is enabled or not, so only applying the header at the edge will create a scenario where the same assets reside under different cache keys at the shield and the edge.

Query string passthrough

By default, any query string parameters which don't exist as part of our IO API are stripped in master vcl_recv to protect your origin. Because additional query string parameters form part of the cache key, for each query string variation, there is an additional branch of image variations.

With query string passthrough disabled (default), the following will occur:

1
2
3
Fastly: ?width=100&something=else
Fastly IO: ?width=100
Origin:  [none]

With query string passthrough enabled, the following will occur:

1
2
3
Fastly: ?width=100&something=else
Fastly IO: ?width=100&something=else
Origin:  ?something=else

Enabling query string passthrough presents an attack vector for your origin. For this reason, query string passthrough is only allowed via explicit opt-in, using the following header:

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

Default TTL

The standard Fastly VCL boilerplate applies a default TTL of 3600s. Ideally, image content should have a greater longevity. When using IO, there's a more severe consequence for a low TTL. It doesn't just mean a cache invalidation and pull from origin. It also invalidates all image variations which results in reprocessing and therefore increased miss latency.


Additional resources:


Back to Top