A plug for serving static assets.

It requires two options:

  • :at - the request path to reach for static assets. It must be a string.

  • :from - the file system path to read static assets from. It can be either: a string containing a file system path, an atom representing the application name (where assets will be served from priv/static), or a tuple containing the application name and the directory to serve assets from (besides priv/static).

The preferred form is to use :from with an atom or tuple, since it will make your application independent from the starting directory.

If a static asset cannot be found, Plug.Static simply forwards the connection to the rest of the pipeline.

Cache mechanisms

Plug.Static uses etags for HTTP caching. This means browsers/clients should cache assets on the first request and validate the cache on following requests, not downloading the static asset once again if it has not changed. The cache-control for etags is specified by the cache_control_for_etags option and defaults to "public".

However, Plug.Static also supports direct cache control by using versioned query strings. If the request query string starts with “?vsn=”, Plug.Static assumes the application is versioning assets and does not set the ETag header, meaning the cache behaviour will be specified solely by the cache_control_for_vsn_requests config, which defaults to "public, max-age=31536000".


  • :gzip - given a request for FILE, serves FILE.gz if it exists in the static directory and if the accept-encoding header is set to allow gzipped content (defaults to false).

  • :brotli - given a request for FILE, serves FILE.br if it exists in the static directory and if the accept-encoding header is set to allow brotli-compressed content (defaults to false). FILE.br is checked first and dominates FILE.gz due to the better compression ratio.

  • :cache_control_for_etags - sets the cache header for requests that use etags. Defaults to "public".

  • :etag_generation - specify a {module, function, args} to be used to generate an etag. The path of the resource will be passed to the function, as well as the args. If this option is not supplied, etags will be generated based off of file size and modification time.

  • :cache_control_for_vsn_requests - sets the cache header for requests starting with “?vsn=” in the query string. Defaults to "public, max-age=31536000".

  • :only - filters which requests to serve. This is useful to avoid file system traversals on every request when this plug is mounted at "/". For example, if only: ["images", "favicon.ico"] is specified, only files in the “images” directory and the exact “favicon.ico” file will be served by Plug.Static. Defaults to nil (no filtering).

  • :only_matching - a relaxed version of :only that will serve any request as long as one of the given values matches the given path. For example, only_matching: ["images", "favicon"] will match any request that starts at “images” or “favicon”, be it “/images/foo.png”, “/images-high/foo.png”, “/favicon.ico” or “/favicon-high.ico”. Such matches are useful when serving digested files at the root. Defaults to nil (no filtering).

  • :headers - other headers to be set when serving static assets.


This plug can be mounted in a Plug.Builder pipeline as follows:

defmodule MyPlug do
  use Plug.Builder

  plug Plug.Static,
    at: "/public",
    from: :my_app,
    only: ~w(images robots.txt)
  plug :not_found

  def not_found(conn, _) do
    send_resp(conn, 404, "not found")



call(conn, options)

Callback implementation for Plug.call/2


Callback implementation for Plug.init/1


call(conn, options)

Callback implementation for Plug.call/2.


Callback implementation for Plug.init/1.

© 2013 Plataformatec
Licensed under the Apache License, Version 2.0.