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".

  • :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 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, arg2)

Callback implementation for Plug.call/2


Callback implementation for Plug.init/1


call(conn, arg2)

Callback implementation for Plug.call/2.


Callback implementation for Plug.init/1.

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