Skip to content

ServeStaticASGI API Reference

Tip

ServeStaticASGI inherits its interface and features from the WSGI variant.

Name Type Description Default
application Callable Original ASGI application N/A
root str Absolute path to a directory of static files to be served. None
prefix str If set, the URL prefix under which the files will be served. Trailing slashes are automatically added. None
**kwargs Sets configuration attributes for this instance N/A

Configuration attributes

These can be set by passing keyword arguments to the constructor, or by sub-classing ServeStatic and setting the attributes directly.


autorefresh

Default: False

Recheck the filesystem to see if any files have changed before responding. This is designed to be used in development where it can be convenient to pick up changes to static files without restarting the server. For both performance and security reasons, this setting should not be used in production.


max_age

Default: 60

Time (in seconds) for which browsers and proxies should cache files.

The default is chosen to be short enough not to cause problems with stale versions but long enough that, if you're running ServeStatic behind a CDN, the CDN will still take the majority of the strain during times of heavy load.

Set to None to disable setting any Cache-Control header on non-versioned files.


index_file

Default: False

If True enable index file serving. If set to a non-empty string, enable index files and use that string as the index file name.

When the index_file option is enabled:

  • Visiting /example/ will serve the file at /example/index.html
  • Visiting /example will redirect (302) to /example/
  • Visiting /example/index.html will redirect (302) to /example/

If you want to something other than index.html as the index file, then you can also set this option to an alternative filename.


mimetypes

Default: None

A dictionary mapping file extensions (lowercase) to the mimetype for that extension. For example:

{".foo": "application/x-foo"}

Note that ServeStatic ships with its own default set of mimetypes and does not use the system-supplied ones (e.g. /etc/mime.types). This ensures that it behaves consistently regardless of the environment in which it's run. View the defaults in the media_types.py file.

In addition to file extensions, mimetypes can be specified by supplying the entire filename, for example:

{ "some-special-file": "application/x-custom-type" }

charset

Default: utf-8

Charset to add as part of the Content-Type header for all files whose mimetype allows a charset.


allow_all_origins

Default: True

Toggles whether to send an Access-Control-Allow-Origin: * header for all static files.

This allows cross-origin requests for static files which means your static files will continue to work as expected even if they are served via a CDN and therefore on a different domain. Without this your static files will mostly work, but you may have problems with fonts loading in Firefox, or accessing images in canvas elements, or other mysterious things.

The W3C explicitly state that this behaviour is safe for publicly accessible files.


add_headers_function

Default: None

Reference to a function which is passed the headers object for each static file, allowing it to modify them.

For example...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def force_download_pdfs(headers, path, url):
    """
    Args:
        headers: A wsgiref.headers instance (which you can treat \
            just as a dict) containing the headers for the current \
            file
        path: The absolute path to the local file
        url: The host-relative URL of the file e.g. \
            `/static/styles/app.css`

    Returns:
        None. Changes should be made by modifying the headers \
        dictionary directly.
    """
    if path.endswith(".pdf"):
        headers["Content-Disposition"] = "attachment"


application = ServeStatic(
    application,
    add_headers_function=force_download_pdfs,
)

immutable_file_test

Default: return False

Reference to function, or string.

If a reference to a function, this is passed the path and URL for each static file and should return whether that file is immutable, i.e. guaranteed not to change, and so can be safely cached forever.

If a string, this is treated as a regular expression and each file's URL is matched against it.

For example...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def immutable_file_test(path, url):
    """
    Args:
        path: The absolute path to the local file.
        url: The host-relative URL of the file e.g. \
            `/static/styles/app.css`

    Returns:
        bool. Whether the file is immutable.

    """
    # Match filename with 12 hex digits before the extension
    # e.g. app.db8f2edc0c8a.js
    return re.match(r"^.+\.[0-9a-f]{12}\..+$", url)

Compression Support

When ServeStatic builds its list of available files it checks for corresponding files with a .gz and a .br suffix (e.g., scripts/app.js, scripts/app.js.gz and scripts/app.js.br). If it finds them, it will assume that they are (respectively) gzip and brotli compressed versions of the original file and it will serve them in preference to the uncompressed version where clients indicate that they that compression format (see note on Amazon S3 for why this behaviour is important).

ServeStatic comes with a command line utility which will generate compressed versions of your files for you. Note that in order for brotli compression to work the Brotli Python package must be installed.

Usage is simple:

$ python -m servestatic.compress --help
usage: compress.py [-h] [-q] [--no-gzip] [--no-brotli]
                   root [extensions [extensions ...]]

Search for all files inside <root> *not* matching <extensions> and produce
compressed versions with '.gz' and '.br' suffixes (as long as this results in
a smaller file)

positional arguments:
  root         Path root from which to search for files
  extensions   File extensions to exclude from compression (default: jpg,
               jpeg, png, gif, webp, zip, gz, tgz, bz2, tbz, xz, br, swf, flv,
               woff, woff2)

optional arguments:
  -h, --help   show this help message and exit
  -q, --quiet  Don't produce log output
  --no-gzip    Don't produce gzip '.gz' files
  --no-brotli  Don't produce brotli '.br' files

You can either run this during development and commit your compressed files to your repository, or you can run this as part of your build and deploy processes. (Note that this is handled automatically in Django if you're using the custom storage backend.)

Caching Headers

By default, ServeStatic sets a max-age header on all responses it sends. You can configure this by passing a max_age keyword argument.

ServeStatic sets both Last-Modified and ETag headers for all files and will return Not Modified responses where appropriate. The ETag header uses the same format as nginx which is based on the size and last-modified time of the file. If you want to use a different scheme for generating ETags you can set them via you own function by using the add_headers_function option.

Most modern static asset build systems create uniquely named versions of each file. This results in files which are immutable (i.e., they can never change their contents) and can therefore by cached indefinitely. In order to take advantage of this, ServeStatic needs to know which files are immutable. This can be done using the immutable_file_test option which accepts a reference to a function.

The exact details of how you implement this method will depend on your particular asset build system but see the documentation documentation for a simple example.

Once you have implemented this, any files which are flagged as immutable will have "cache forever" headers set.

Using a Content Distribution Network

The above steps will get you decent performance on moderate traffic sites, however for higher traffic sites, or sites where performance is a concern you should look at using a CDN.

Because ServeStatic sends appropriate cache headers with your static content, the CDN will be able to cache your files and serve them without needing to contact your application again.

Below are instruction for setting up ServeStatic with Amazon CloudFront, a popular choice of CDN. The process for other CDNs should look very similar though.

Configuring Amazon CloudFront

Go to CloudFront section of the AWS Web Console, and click "Create Distribution". Put your application's domain (without the http prefix) in the "Origin Domain Name" field and leave the rest of the settings as they are.

It might take a few minutes for your distribution to become active. Once it's ready, copy the distribution domain name into your settings.py file so it looks something like this:

STATIC_HOST = "https://d4663kmspf1sqa.cloudfront.net" if not DEBUG else ""
STATIC_URL = STATIC_HOST + "/static/"

Or, even better, you can avoid hard-coding your CDN into your settings by doing something like this:

STATIC_HOST = os.environ.get("DJANGO_STATIC_HOST", "")
STATIC_URL = STATIC_HOST + "/static/"

This way you can configure your CDN just by setting an environment variable. For apps on Heroku, you'd run this command

heroku config:set DJANGO_STATIC_HOST=https://d4663kmspf1sqa.cloudfront.net
CloudFront compression algorithms

By default, CloudFront will discard any Accept-Encoding header browsers include in requests, unless the value of the header is gzip. If it is gzip, CloudFront will fetch the uncompressed file from the origin, compress it, and return it to the requesting browser.

To get CloudFront to not do the compression itself as well as serve files compressed using other algorithms, such as Brotli, you must configure your distribution to cache based on the Accept-Encoding header. You can do this in the Behaviours tab of your distribution.

CloudFront SEO issues

The instructions for setting up CloudFront given above will result in the entire site being accessible via the CloudFront URL. It's possible that this can cause SEO problems if these URLs start showing up in search results. You can restrict CloudFront to only proxy your static files by following these directions:

  1. Go to your newly created distribution and click "Distribution Settings", then the "Behaviors" tab, then "Create Behavior". Put static/* into the path pattern and click "Create" to save.
  2. Now select the Default (*) behaviour and click "Edit". Set "Restrict Viewer Access" to "Yes" and then click "Yes, Edit" to save.
  3. Check that the static/* pattern is first on the list, and the default one is second. This will ensure that requests for static files are passed through but all others are blocked.