This page is about Caddy 2, which is currently in beta. Click here for the old Caddy 1 site. Thank you for your patience as we transition!
Caddy 2 is beta software. It is ready for production, but some things will change between releases; check the release notes before upgrading.

Caddyfile Concepts

This document will help you learn about the HTTP Caddyfile in detail.

  1. Structure
  2. Addresses
  3. Matchers
  4. Placeholders
  5. Snippets
  7. Environment variables
  8. Global options


The Caddyfile's structure can be described visually:

Caddyfile structure

Key points:

  • An optional global options block can be the very first thing in the file.
  • Otherwise, the first line of the Caddyfile is always the address(es) of the site to serve.
  • All directives and matchers must go in a site block. There is no global scope or inheritence across site blocks.
  • If there is only one site block, its curly braces { } are optional.

A Caddyfile consists of at least one or more site blocks, which always starts with one or more addresses for the site. Any directives appearing before the address will be confusing to the parser.


Opening and closing a block is done with curly braces:

... {
  • The open curly brace { must be at the end of its line.
  • The close curly brace } must be on its own line.

When there is only one site block, the curly braces (and indentation) are optional. This is for convenience to quickly define a single site, for example, this:


reverse_proxy /api/* localhost:9001

is equivalent to:

localhost {
	reverse_proxy /api/* localhost:9001

when you have only a single site block; it's a matter of preference.

To configure multiple sites with the same Caddyfile, you must use curly braces around each one to separate their configurations: {
	root * /www/
} {
	reverse_proxy localhost:9000


Directives are keywords which customize how the site is served. For example, a complete file server config might look like this:



Or a reverse proxy:


reverse_proxy localhost:9000

In these examples, file_server and reverse_proxy are directives. Directives are the first word on a line in a site block.

In the second example, localhost:9000 is an argument because it appears on the same line after the directive.

Subdirectives can appear in directive blocks:


reverse_proxy localhost:9000 localhost:9001 {
	lb_policy first

Here, lb_policy is a subdirective to reverse_proxy (it sets the load balancing policy to use between backends).


An address always appears at the top of the site block, and is usually the first thing in the Caddyfile.

These are examples of valid addresses:

  • localhost
  • :443
  • localhost:8080
  • [::1]:2015

From the address, Caddy can potentially infer the scheme, host, port, and path of your site. The default port is 2015 unless automatic HTTPS is activated, which changes it to the HTTPS port.

If you specify a hostname, only requests with a matching Host header will be honored. In other words, if the site address is localhost, then Caddy will not match requests to

If multiple sites share the same definition, you can list all of them together:




Notice how the commas indicate the continuation of addresses.

An address must be unique; you cannot specify the same address more than once.


By default, a directive that injects an HTTP handler applies to all requests (unless otherwise documented).

Request matchers can be used to classify requests by a given criteria. This concept originates in the underlying JSON structure, and it's important to know how to use them in the Caddyfile. With matchers, you can specify exactly which requests a directive applies to.

To limit a directive's scope, use a matcher token immediately after the directive. It can be one of these forms:

  1. * to match all requests (wildcard; default).
  2. /path start with a forward slash to match a request path.
  3. @name to specify a named matcher.

Matcher tokens are usually optional. If a matcher token is omitted, it is the same as a wildcard matcher (*).

Wildcard matcher

The wildcard matcher * matches all requests, and is only needed if a matcher token is required. For example, if the first argument you want to give a directive also happens to be a path, it would look exactly like a path matcher! So you can use a wildcard matcher to disambiguate, for example:

root * /home/www/mysite

Otherwise, this matcher is not often used. It is convenient to omit it when possible; just a matter of preference.

Path matcher

Because matching by path is so common, a single path matcher can be inlined, like so:

redir /old-article.html /new-article.html

Path matcher tokens must start with a forward slash /.

Path matching is an exact match by default; you must append a * for a fast prefix match. Note that /foo* will match /foo and /foo/ as well as /foobar; if this is unintended, you might actually want /foo/* instead.

Named matcher

Defining a matcher with a unique name gives you more flexibility, allowing you to combine any available matchers:

@name {

Then you can use the matcher like so: @name

For example:

@websockets {
	header_regexp Connection Upgrade
	header        Upgrade websocket
reverse_proxy @websockets localhost:6001

This proxies only the requests that have a header field named "Connection" containing the word "Upgrade", and another header named "Upgrade" with a value of "websocket". (TODO: actually you don't need regexp matching for this)

Like directives, named matcher definitions must go inside the site blocks that use them.

View full list of standard request matchers.

Matcher examples

This directive applies to all HTTP requests:

reverse_proxy localhost:9000

And this is the same:

reverse_proxy * localhost:9000

But this directive applies only to requests having a path starting with /api/:

reverse_proxy /api/* localhost:9000

To match on anything other than a path, define a named matcher and refer to it using @name:

@post {
	method POST
reverse_proxy @post localhost:9000


You can use any Caddy placeholders in the Caddyfile, but for convenience you can also use some equivalent shorthand ones:

Shorthand Replaces
{dir} {http.request.uri.path.dir}
{file} {http.request.uri.path.file}
{host} {}
{hostport} {http.request.hostport}
{method} {http.request.method}
{path} {http.request.uri.path}
{query} {http.request.uri.query}
{remote} {http.request.remote}
{remote_host} {}
{remote_port} {http.request.remote.port}
{scheme} {http.request.scheme}
{uri} {http.request.uri}


You can define special blocks called snippets by giving them a name surrounded in parentheses:

(redirect) {
	@http {
		scheme http
	redir @http https://{host}{uri}

And then you can reuse this anywhere you need:

import redirect

The import directive can also be used to include other files in its place. As a special case, it can appear almost anywhere within the Caddyfile.


Comments start with # and proceed until the end of the line:

# Comments can start a line
directive  # or go at the end

Environment variables

If your configuration relies on environment variables, you can use them in the Caddyfile:


Environment variables in this form are substituted before parsing begins, so they can expand to empty values, partial tokens, complete tokens, or even multiple tokens and lines.

If you want to defer the substitution of an environment variable until runtime, you can use the standard {env.*} placeholders.

Global options

A Caddyfile may optionally start with a special block that has no keys, called a global options block:


If present, it must be the very first block in the config.

It is used to set options that apply globally, or not to any one site in particular. Inside, only global options can be set; you cannot use regular site directives in them.

Learn more about the global options block.