Cache control tutorial

  Last updated July 06, 2017

You are in full control of how Fastly caches your resources. The most preferred way of instructing Fastly is to use backend HTTP headers. The other way is to use the Varnish Configuration Language (VCL).

Backend HTTP headers

You can set four different types of HTTP headers which will have different effects on our caches and on web browsers. If you use more than one type, they are prioritized in the order listed below:



Surrogate-Control: max-age=(time in seconds)


Surrogate-Control: max-age=3600

will cache something on our caches for one hour. This header gets stripped and is only visible to Fastly caches.

Cache-Control: s-maxage


Cache-Control: s-maxage=(time in seconds)

This is the same as Surrogate-Control, except the header is not stripped and will be respected by Fastly caches and any caches between Fastly and the browser, but not the browser itself.

Cache-Control: max-age


Cache-Control: max-age=(time in seconds)

This header will be respected by Fastly caches, any caches between Fastly and the browser, and the browser itself.


This header caches content until it expires as specified. It will be respected by Fastly caches, any caches between Fastly and the browser and the browser itself. See section 14.21 of RFC2616 for an explanation of the format.

Do not cache

If you want to ensure that a resource is not cached by Fastly, send the following HTTP header with the origin response:

Cache-Control: private

If you just set max-age=0 or an Expires in the past, Fastly may still use a single response to satisfy multiple outstanding requests that arrive while waiting on the origin (see Request collapsing), or may cache the object in a stale form so that it can be used in case of errors or asyncronous revaldiation (see Serving stale content).

The private directive does not prevent content from being cached in the browser. If you need to prevent caching by both Fastly and web browsers, we recommend combining the private directive with max-age=0 or no-store. For example:

Cache-Control: private, no-store

Fastly does not currently respect no-store or no-cache directives. Including either or both of these in a Cache-Control header has no effect on Fastly's caching decision, unless you alter this behavior using custom VCL.

When Cache-Control and Surrogate-Control headers co-exist

Say that you want Fastly to cache your resources forever but send headers to browsers so that they don't cache it at all (so that every browser miss hits Fastly but isn't a cache miss from your service).

The best way to do this would be to send Fastly both the Cache-Control header as you want it to go to the browsers, and use Surrogate-Control to tell us how long to cache for. For example:

Cache-Control: no-cache, no-store, must-revalidate
Surrogate-Control: max-age=3600
Pragma: no-cache
Expires: 0

Except for when the Cache-Control header is set to private, the Surrogate-Control header takes priority over Cache-Control, but unlike Cache-Control it is stripped so the browsers don't see it.

Example backend configs

Apache Config

If you are using Apache, the easiest way to add headers is to use the mod_expires module. For example, to cache GIF images for 75 minutes after the image was last accessed by the cache server, you would add a directive like this under the VirtualHost (or globally). For example:

ExpiresActive On
ExpiresByType image/gif "access plus 1 hours 15 minutes"

You can also cache whole URL subtrees. For example:

<Location "/css">
  ExpiresActive On
  ExpiresDefault "access plus 1 year"

<Location "/static/">
  ExpiresActive On
  ExpiresDefault "access plus 1 day"

NGINX Configuration

To configure NGINX, add the expires directive. For example:

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
   expires 1h;

Alternatively, if you need more flexibility in modifying headers you can try the HttpHeadersMore Module.

Amazon S3 configuration

By default, S3 doesn't have a facility for setting Cache-Control headers across multiple objects, so you will have to do this file-by-file using the S3Explorer, or in an automated fashion by using a cloud library like boto. Remember that you can combine long cache time with instant purges to enhance your performance.

from boto.s3.connection import S3Connection

connection = S3Connection('aws access key', 'aws secret key')

buckets = connection.get_all_buckets()

for bucket in buckets:
    for key in bucket.list():
        print('%s' % key)

        if key.name.endswith('.jpg'):
            contentType = 'image/jpeg'
        elif key.name.endswith('.png'):
            contentType = 'image/png'

            'Content-Type': contentType,
            'Cache-Control': 'max-age=864000'

Custom Headers in Programming Languages and Frameworks


More information: http://php.net/manual/en/function.header.php

Example: add this to your PHP code before you send any output to cache certain HTML for an hour

header('Cache-Control: max-age=3600');


More information: https://docs.djangoproject.com/en/dev/ref/request-response/#setting-headers


response = HttpResponse()
response['Cache-Control'] = 'max-age=3600'


More information: http://sinatra.rubyforge.org/doc/


get '/' do
    headers['Cache-Control'] = 'max-age=3600'

Additional resources:

Back to Top