From bb909b74adb442f2c5c922513391d4db8201e3f6 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Fri, 13 Sep 2024 09:36:36 +0200 Subject: [PATCH] update caddy --- technology/applications/web/Caddy.md | 3442 +++++++++++++++++++++++++- 1 file changed, 3440 insertions(+), 2 deletions(-) diff --git a/technology/applications/web/Caddy.md b/technology/applications/web/Caddy.md index 945f92a..0706b3a 100644 --- a/technology/applications/web/Caddy.md +++ b/technology/applications/web/Caddy.md @@ -2,17 +2,3455 @@ obj: application website: https://caddyserver.com repo: https://github.com/caddyserver/caddy +rev: 2024-09-13 --- # Caddy -#wip +Caddy is an open-source web server with automatic HTTPS support. It makes it simple to serve static files and applications over secure HTTP connections. +## CLI +### caddy file-server +A simple but production-ready file server. Useful for quick deployments, demos, and development. +Usage: `caddy file-server [OPTIONS]` + +#### Options + +| Option | Description | +| ----------------------------- | --------------------------------------------------------------------------------------- | +| `-a, --access-log` | Enable the access log | +| `-b, --browse` | Enable directory browsing | +| `-v, --debug` | Enable verbose debug logs | +| `-d, --domain string` | Domain name at which to serve the files | +| `-l, --listen string` | The address to which to bind the listener | +| `--no-compress` | Disable Zstandard and Gzip compression | +| `-p, --precompressed strings` | Specify precompression file extensions. Compression preference implied from flag order. | +| `--reveal-symlinks` | Show symlink paths when browse is enabled. | +| `-r, --root string` | The path to the root of the site | +| `-t, --templates` | Enable template rendering | + +### caddy fmt +Formats the Caddyfile by adding proper indentation and spaces to improve human readability. It prints the result to stdout. + +Usage: `caddy fmt [--overwrite] [--diff] []` + +### caddy hash-password +Convenient way to hash a plaintext password. The resulting hash is written to stdout as a base64 string. + +Usage: `caddy hash-password [--plaintext ] [--algorithm ]` + +### caddy reverse-proxy +A simple but production-ready reverse proxy. Useful for quick deployments, +demos, and development. + +Usage: `caddy reverse-proxy [OPTIONS]` + +#### Options + +| Option | Description | +| --------------------------- | ---------------------------------------------------------------------------------------- | +| `--access-log` | Enable the access log | +| `-c, --change-host-header` | Set upstream Host header to address of upstream | +| `-v, --debug` | Enable verbose debug logs | +| `-r, --disable-redirects` | Disable HTTP->HTTPS redirects | +| `-f, --from string` | Address on which to receive traffic (default "localhost") | +| `-d, --header-down strings` | Set a response header to send back to the client (format: "Field: value") | +| `-H, --header-up strings` | Set a request header to send to the upstream (format: "Field: value") | +| `--insecure` | Disable TLS verification (WARNING: DISABLES SECURITY BY NOT VERIFYING TLS CERTIFICATES!) | +| `-i, --internal-certs` | Use internal CA for issuing certs | +| `-t, --to strings` | Upstream address(es) to which traffic should be sent | + +### caddy run +Starts the Caddy process, optionally bootstrapped with an initial config file, and blocks indefinitely until the server is stopped; i.e. runs Caddy in "daemon" mode (foreground). + +Usage: `caddy run [--config [--adapter ]] [--envfile ] [--environ] [--resume] [--watch] [--pidfile ]` ## Configuration ### Caddyfile -#todo +The Caddyfile is a convenient Caddy configuration format for humans. It is most people's favorite way to use Caddy because it is easy to write, easy to understand, and expressive enough for most use cases. + +It looks like this: +```caddyfile +example.com { + root * /var/www/wordpress + encode gzip + php_fastcgi unix//run/php/php-version-fpm.sock + file_server +} +``` + +#### Structure +```caddyfile +{ + email you@yours.com + ... +} + +(snippet) { + # this is a reusable snippet +} + +example.com { + @post { + method post + } + reverse_proxy @post localhost:9001 localhost:9002 { + lb_policy first + } + file_server /static + import snippet +} + +www.example.com { + redir https://example.com{uri} + import snippet +} +``` + +Key points: +- An optional global options block can be the very first thing in the file. +- Snippets or named routes may optionally appear next. +- 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 inheritance 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. + +##### Blocks +Opening and closing a block is done with curly braces: +```caddyfile +... { + ... +} +``` + +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: +```caddyfile +localhost + +reverse_proxy /api/* localhost:9001 +file_server +``` + +is equivalent to: +```caddyfile +localhost { + reverse_proxy /api/* localhost:9001 + file_server +} +``` + +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: +```caddyfile +example1.com { + root * /www/example.com + file_server +} + +example2.com { + reverse_proxy localhost:9000 +} +``` + +If a request matches multiple site blocks, the site block with the most specific matching address is chosen. Requests don't cascade into to other site blocks. + +##### Directives +Directives are functional keywords which customize how the site is served. They must appear within site blocks. For example, a complete file server config might look like this: +```caddyfile +localhost { + file_server +} +``` + +Or a reverse proxy: +```caddyfile +localhost { + 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. +Sometimes directives can open their own blocks. Subdirectives appear on the beginning of each line within directive blocks: +```caddyfile +localhost { + 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). + +Unless otherwise documented, directives cannot be used within other directive blocks. For example, `basic_auth` cannot be used within `file_server` because the file server does not know how to do authentication; but you can use directives within `route`, `handle`, and `handle_path` blocks because they are specifically designed to group directives together. + +Note that when the HTTP Caddyfile is adapted, HTTP handler directives are sorted according to a specific default directive order unless in a route block, so the order of appearance of the directives does not matter except in route blocks. + +##### Tokens and quotes +The Caddyfile is lexed into tokens before being parsed. Whitespace is significant in the Caddyfile, because tokens are separated by whitespace. + +Often, directives expect a certain number of arguments; if a single argument has a value with whitespace, it would be lexed as two separate tokens: +```caddyfile +directive abc def +``` + +This could be problematic and return errors or unexpected behavior. +If abc def is supposed to be the value of a single argument, it needs to be quoted: +```caddyfile +directive "abc def" +``` + +Quotes can be escaped if you need to use quotes in quoted tokens, too: +```caddyfile +directive "\"abc def\"" +``` + +To avoid escaping quotes, you can instead use backticks \` \` to enclose tokens; for example: +```caddyfile +directive `{"foo": "bar"}` +``` + +Inside quoted tokens, all other characters are treated literally, including spaces, tabs, and newlines. Multi-line tokens are thus possible: +```caddyfile +directive "first line + second line" +``` + +Heredocs are also supported: +```caddyfile +example.com { + respond < + Foo + Foo + + HTML 200 +} +``` + +The opening heredoc marker must start with `<<`, followed by any text (uppercase letters recommended). The closing heredoc marker must be the same text (in the above example, `HTML`). The opening marker can be escaped with `\<<` to prevent heredoc parsing, if needed. + +The closing marker can be indented, which causes every line of text to have that much indentation stripped (inspired by PHP) which is nice for readability inside blocks while giving great control of the whitespace in the token text. The trailing newline is also stripped, but can be retained by adding an extra blank line before the closing marker. + +Additional tokens may follow the closing marker as arguments to the directive (such as in the example above, the status code 200). + +##### Global options +A Caddyfile may optionally start with a special block that has no keys, called a global options block: +```caddyfile +{ + ... +} +``` + +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. + +For example, to enable the `debug` global option, which is commonly used to produce verbose logs for troubleshooting: +```caddyfile +{ + debug +} +``` + +##### Addresses +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: +| Address | Effect | +| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `example.com` | HTTPS with managed [publicly-trusted certificate](https://caddyserver.com/docs/automatic-https#hostname-requirements) | +| `*.example.com` | HTTPS with managed [wildcard publicly-trusted certificate](https://caddyserver.com/docs/caddyfile/patterns#wildcard-certificates) | +| `localhost` | HTTPS with managed [locally-trusted certificate](https://caddyserver.com/docs/automatic-https#local-https) | +| `http://` | HTTP catch-all, affected by [`http_port`](https://caddyserver.com/docs/caddyfile/options#http-port) | +| `https://` | HTTPS catch-all, affected by [`https_port`](https://caddyserver.com/docs/caddyfile/options#http-port) | +| `http://example.com` | HTTP explicitly, with a `Host` matcher | +| `example.com:443` | HTTPS due to matching the [`https_port`](https://caddyserver.com/docs/caddyfile/options#http-port) default | +| `:443` | HTTPS catch-all due to matching the [`https_port`](https://caddyserver.com/docs/caddyfile/options#http-port) default | +| `:8080` | HTTP on non-standard port, no `Host` matcher | +| `localhost:8080` | HTTPS on non-standard port, due to having a valid domain | +| `https://example.com:443` | HTTPS, but both `https://` and `:443` are redundant | +| `127.0.0.1` | HTTPS, with a locally-trusted IP certificate | +| `http://127.0.0.1` | HTTP, with an IP address `Host` matcher (rejects `localhost`) | + +Automatic HTTPS is enabled if your site's address contains a hostname or IP address. This behavior is purely implicit, however, so it never overrides any explicit configuration. + +For example, if the site's address is `http://example.com`, auto-HTTPS will not activate because the scheme is explicitly `http://`. + +From the address, Caddy can potentially infer the scheme, host and port of your site. If the address is without a port, the Caddyfile will choose the port matching the scheme if specified, or the default port of 443 will be assumed. + +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 127.0.0.1. + +Wildcards (`*`) may be used, but only to represent precisely one label of the hostname. For example, `*.example.com` matches `foo.example.com` but not `foo.bar.example.com`, and `*` matches `localhost` but not `example.com`. See the wildcard certificates pattern for a practical example. + +To catch all hosts, omit the host portion of the address, for example, simply `https://`. This is useful when using On-Demand TLS, when you don't know the domains ahead of time. + +If multiple sites share the same definition, you can list all of them together, either with spaces or commas. The following three examples are equivalent: +```caddyfile +# Comma separated site addresses +localhost:8080, example.com, www.example.com { + ... +} +``` + +or + +```caddyfile +# Space separated site addresses +localhost:8080 example.com www.example.com { + ... +} +``` + +or + +```caddyfile +# Comma and new-line separated site addresses +localhost:8080, +example.com, +www.example.com { + ... +} +``` + +An address must be unique; you cannot specify the same address more than once. + +Placeholders cannot be used in addresses, but you may use Caddyfile-style environment variables in them: +```caddyfile +{$DOMAIN:localhost} { + ... +} +``` + +By default, sites bind on all network interfaces. If you wish to override this, use the `bind` directive or the `default_bind` global option to do so. + +##### Matchers +HTTP handler directives apply to all requests by default (unless otherwise documented). + +Request matchers can be used to classify requests by a given criteria. With matchers, you can specify exactly which requests a certain directive applies to. + +For directives that support matchers, the first argument after the directive is the matcher token. Here are some examples: +```caddyfile +root * /var/www # matcher token: * +root /index.html /var/www # matcher token: /index.html +root @post /var/www # matcher token: @post +``` + +Matcher tokens can be omitted entirely to match all requests; for example, `*` does not need to be given if the next argument does not look like a path matcher. + +##### Placeholders +You can use any Caddy placeholders in the Caddyfile, but for convenience you can also use some equivalent shorthand ones: + +| Shorthand | Replaces | +| ------------------------------------- | -------------------------------------------------- | +| `{cookie.*}` | `{http.request.cookie.*}` | +| `{client_ip}` | `{http.vars.client_ip}` | +| `{dir}` | `{http.request.uri.path.dir}` | +| `{err.*}` | `{http.error.*}` | +| `{file_match.*}` | `{http.matchers.file.*}` | +| `{file.base}` | `{http.request.uri.path.file.base}` | +| `{file.ext}` | `{http.request.uri.path.file.ext}` | +| `{file}` | `{http.request.uri.path.file}` | +| `{header.*}` | `{http.request.header.*}` | +| `{host}` | `{http.request.host}` | +| `{hostport}` | `{http.request.hostport}` | +| `{labels.*}` | `{http.request.host.labels.*}` | +| `{method}` | `{http.request.method}` | +| `{path.*}` | `{http.request.uri.path.*}` | +| `{path}` | `{http.request.uri.path}` | +| `{port}` | `{http.request.port}` | +| `{query.*}` | `{http.request.uri.query.*}` | +| `{query}` | `{http.request.uri.query}` | +| `{re.*}` | `{http.regexp.*}` | +| `{remote_host}` | `{http.request.remote.host}` | +| `{remote_port}` | `{http.request.remote.port}` | +| `{remote}` | `{http.request.remote}` | +| `{rp.*}` | `{http.reverse_proxy.*}` | +| `{scheme}` | `{http.request.scheme}` | +| `{tls_cipher}` | `{http.request.tls.cipher_suite}` | +| `{tls_client_certificate_der_base64}` | `{http.request.tls.client.certificate_der_base64}` | +| `{tls_client_certificate_pem}` | `{http.request.tls.client.certificate_pem}` | +| `{tls_client_fingerprint}` | `{http.request.tls.client.fingerprint}` | +| `{tls_client_issuer}` | `{http.request.tls.client.issuer}` | +| `{tls_client_serial}` | `{http.request.tls.client.serial}` | +| `{tls_client_subject}` | `{http.request.tls.client.subject}` | +| `{tls_version}` | `{http.request.tls.version}` | +| `{upstream_hostport}` | `{http.reverse_proxy.upstream.hostport}` | +| `{uri}` | `{http.request.uri}` | +| `{vars.*}` | `{http.vars.*}` | + +##### Snippets +You can define special blocks called snippets by giving them a name surrounded in parentheses: +```caddyfile +(logging) { + log { + output file /var/log/caddy.log + format json + } +} +``` + +And then you can reuse this anywhere you need, using the special import directive: +```caddyfile +example.com { + import logging +} + +www.example.com { + import logging +} +``` + +The import directive can also be used to include other files in its place. If the argument does not match a defined snippet, it will be tried as a file. It also supports globs to import multiple files. As a special case, it can appear anywhere within the Caddyfile (except as an argument to another directive), including outside of site blocks: +```caddyfile +{ + email admin@example.com +} + +import sites/* +``` + +You can pass arguments to an imported configuration (snippets or files) and use them like so: +```caddyfile +(snippet) { + respond "Yahaha! You found {args[0]}!" +} + +a.example.com { + import snippet "Example A" +} + +b.example.com { + import snippet "Example B" +} +``` + +##### Comments +Comments start with `#` and proceed until the end of the line: +```caddyfile +# Comments can start a line +directive # or go at the end +``` + +The hash character `#` for a comment cannot appear in the middle of a token (i.e. it must be preceded by a space or appear at the beginning of a line). This allows the use of hashes within URIs or other values without requiring quoting. + +##### Environment variables +If your configuration relies on environment variables, you can use them in the Caddyfile: +```caddyfile +{$ENV} +``` + +Environment variables in this form are substituted before Caddyfile parsing begins, so they can expand to empty values (i.e. ""), partial tokens, complete tokens, or even multiple tokens and lines. + +For example, a environement variable `UPSTREAMS="app1:8080 app2:8080 app3:8080"` would expand to multiple tokens: +```caddyfile +example.com { + reverse_proxy {$UPSTREAMS} +} +```caddyfile + +A default value can be specified for when the environment variable is not found, by using `:` as the delimiter between the variable name and the default value: +```caddyfile +{$DOMAIN:localhost} { + +} +``` + +If you want to defer the substitution of an environment variable until runtime, you can use the standard `{env.*}` placeholders. Note that not all config parameters support these placeholders though, since module developers need to add a line of code to perform the replacement. If it doesn't seem to work, please file an issue to request support for it. + +For example, if you have the `caddy-dns/cloudflare` plugin installed and wish to configure the DNS challenge, you can pass your `CLOUDFLARE_API_TOKEN` environment variable to the plugin like this: +```caddyfile +{ + acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN} +} +``` + +If you're running Caddy as a [systemd](../../linux/systemd/Systemd.md) service, see these instructions for setting service overrides to define your environment variables. + +#### Global Options +The Caddyfile has a way for you to specify options that apply globally. Some options act as default values; others customize HTTP servers and don't apply to just one particular site. + +The very top of your Caddyfile can be a **global options block**. This is a block that has no keys: + +``` +{ + ... +} +``` + +There can only be one at most, and it must be the first block of the Caddyfile. + +Possible options are: + +``` +{ + # General Options + debug + http_port + https_port + default_bind + order first|last|[before|after ] + storage { + + } + storage_clean_interval + admin off| { + origins + enforce_origin + } + persist_config off + log [name] { + output ... + format ... + level + include + exclude + } + grace_period + shutdown_delay + + # TLS Options + auto_https off|disable_redirects|ignore_loaded_certs|disable_certs + email + default_sni + fallback_sni + local_certs + skip_install_trust + acme_ca + acme_ca_root + acme_eab { + key_id + mac_key + } + acme_dns ... + on_demand_tls { + ask + interval + burst + } + key_type ed25519|p256|p384|rsa2048|rsa4096 + cert_issuer ... + renew_interval + ocsp_interval + ocsp_stapling off + preferred_chains [smallest] { + root_common_name + any_common_name + } + + # Server Options + servers [] { + name + listener_wrappers { + + } + timeouts { + read_body + read_header + write + idle + } + keepalive_interval + trusted_proxies ... + client_ip_headers + metrics + max_header_size + enable_full_duplex + log_credentials + protocols [h1|h2|h2c|h3] + strict_sni_host [on|insecure_off] + } + + # File Systems + filesystem { + + } + + # PKI Options + pki { + ca [] { + name + root_cn + intermediate_cn + intermediate_lifetime + root { + format + cert + key + } + intermediate { + format + cert + key + } + } + } + + # Event options + events { + on + } +} +``` + +For more information see [docs](https://caddyserver.com/docs/caddyfile/options). + +#### Matcher +**Request matchers** can be used to filter (or classify) requests by various criteria. + +In the Caddyfile, a **matcher token** immediately following the directive can limit that directive's scope. The matcher token 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_. + +If a directive supports matchers, it will appear as `[]` in its syntax documentation. Matcher tokens are usually optional, denoted by `[ ]`. If the matcher token is omitted, it is the same as a wildcard matcher (`*`). + +##### Wildcard matchers +The wildcard (or "catch-all") 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. We generally recommend omitting it if syntax doesn't require it. + +##### Path matchers +Matching by URI path is the most common way to match requests, so the matcher can be inlined, like this: + +``` +redir /old.html /new.html +``` + +Path matcher tokens must start with a forward slash `/`. + +**Path matching is an exact match by default, not a prefix match.** You must append a `*` for a fast prefix match. Note that `/foo*` will match `/foo` and `/foo/` as well as `/foobar`; you might actually want `/foo/*` instead. + +##### Named matchers +All matchers that are not path or wildcard matchers must be named matchers. This is a matcher that is defined outside of any particular directive, and can be reused. + +Defining a matcher with a unique name gives you more flexibility, allowing you to combine any available matchers into a set: + +``` +@name { + ... +} +``` + +or, if there is only one matcher in the set, you can put it on the same line: + +``` +@name ... +``` + +Then you can use the matcher like so, by specifying it as the first argument to a directive: + +``` +directive @name +``` + +For example, this proxies websocket requests to `localhost:6001`, and other requests to `localhost:8080`. It matches requests that have a header field named `Connection` _containing_ `Upgrade`, **and** another field named `Upgrade` with exactly `websocket`: + +``` +example.com { + @websockets { + header Connection *Upgrade* + header Upgrade websocket + } + reverse_proxy @websockets localhost:6001 + + reverse_proxy localhost:8080 +} +``` + +If the matcher set consists of only one matcher, a one-liner syntax also works: + +``` +@post method POST +reverse_proxy @post localhost:6001 +``` + +As a special case, the `expression` matcher may be used without specifying its name as long as one quoted argument (the CEL expression itself) follows the matcher name: + +``` +@not-found `{err.status_code} == 404` +``` + +Like directives, named matcher definitions must go inside the site blocks that use them. + +A named matcher definition constitutes a _matcher set_. Matchers in a set are AND'ed together; i.e. all must match. For example, if you have both a `header` and `path` matcher in the set, both must match. + +Multiple matchers of the same type may be merged (e.g. multiple `path` matchers in the same set) using boolean algebra (AND/OR), as described in their respective sections below. + +For more complex boolean matching logic, it's recommended to the `expression` matcher to write a CEL expression, which supports **and** `&&`, **or** `||`, and **parentheses** `( )`. + +##### Standard Matchers + +| Matcher | Description | +| --------------- | ------------------------------------------------------------------------------------------------------ | +| `client_ip` | Matches requests based on client IP address | +| `expression` | Evaluates a CEL expression to determine if a request matches | +| `file` | Matches requests based on file extensions | +| `header` | Matches requests based on custom headers | +| `header_regexp` | Matches requests based on regular expressions in custom headers | +| `host` | Matches requests based on the host domain name | +| `method` | Matches requests based on HTTP method (e.g., GET, POST) | +| `not` | Negates the match for a given matcher | +| `path` | Matches requests based on request paths | +| `path_regexp` | Matches requests based on regular expressions in request paths | +| `protocol` | Matches requests based on used protocol (e.g., HTTP, HTTPS) | +| `query` | Matches requests based on query parameters | +| `remote_ip` | Matches requests based on client's remote IP address | +| `vars` | Uses Caddy placeholders to match values against request or response variables | +| `vars_regexp` | Uses regular expressions with Caddy placeholders to match values against request or response variables | + +#### Directives +The syntax of each directive will look something like this: + +```caddyfile +directive [] { + subdirective [] +} +``` + +##### abort +Prevents any response to the client by immediately aborting the HTTP handler chain and closing the connection. Any concurrent, active HTTP streams on the same connection are interrupted. +Syntax + +```caddyfile +abort [] +``` + +Examples: Forcefully close a connection received for unknown domains when using a wildcard certificate: +```caddyfile +*.example.com { + @foo host foo.example.com + handle @foo { + respond "This is foo!" 200 + } + + handle { + # Unhandled domains fall through to here, + # but we don't want to accept their requests + abort + } +} +``` + +##### acme_server +An embedded [ACME protocol](https://tools.ietf.org/html/rfc8555) server handler. This allows a Caddy instance to issue certificates for any other ACME-compatible software (including other Caddy instances). + +When enabled, requests matching the path `/acme/*` will be handled by the ACME server. + +Using ACME server defaults, ACME clients should simply be configured to use `https://localhost/acme/local/directory` as their ACME endpoint. (`local` is the ID of Caddy's default CA.) + +``` +acme_server [] { + ca + lifetime + resolvers + challenges + allow_wildcard_names + allow { + domains + ip_ranges + } + deny { + domains + ip_ranges + } +} +``` + +- **ca** specifies the ID of the certificate authority with which to sign certificates. The default is `local`, which is Caddy's default CA, intended for locally-used, self-signed certificates, which is most common in dev environments. For broader use, it is recommended to specify a different CA to avoid confusion. If the CA with the given ID does not already exist, it will be created. See the [PKI app global options](https://caddyserver.com/docs/caddyfile/options#pki-options) to configure alternate CAs. + +- **lifetime** (Default: `12h`) is a duration which specifies the validity period for issued certificates. This value must be less than the lifetime of the [intermediate certificate](https://caddyserver.com/docs/caddyfile/options#intermediate-lifetime) used for signing. It is not recommended to change this unless absolutely necessary. + +- **resolvers** are the addresses of DNS resolvers to use when looking up the TXT records for solving ACME DNS challenges. Accepts [network addresses](https://caddyserver.com/docs/conventions#network-addresses) defaulting to UDP and port 53 unless specified. If the host is an IP address, it will be dialed directly to resolve the upstream server. If the hot is not an IP address, the addresses are resolved using the [name resolution convention](https://golang.org/pkg/net/#hdr-Name_Resolution) of the Go standard library. If multiple resolvers are specified, then one is chosen at random. + +- **challenges** sets the enabled challenge types. If not set or the directive is used without values, then all challenge types are enabled. Accepted values are: http-01, tls-alpn-01, dns-01. + +- **allow_wildcard_names** enables issuing of certificates with wildcard SAN (Subject Alternative Name) + +- **allow**, **deny** configure the operational policy of the `acme_server`. The policy evaluation follows the criteria described by Step-CA [here](https://smallstep.com/docs/step-ca/policies/#policy-evaluation). + + - **domains** sets the subject domain names to be allowed or denied per the policy evaluation criteria. + + - **ip_ranges** sets the subject IP ranges to be allowed or denied per the policy evaluation criteria. + + +Examples: + +To serve an ACME server with ID `home` on the domain `acme.example.com`, with the CA customized via the [`pki` global option](https://caddyserver.com/docs/caddyfile/options#pki-options), and issuing its own certificate using the `internal` issuer: + +``` +{ + pki { + ca home { + name "My Home CA" + } + } +} + +acme.example.com { + tls { + issuer internal { + ca home + } + } + acme_server { + ca home + } +} +``` + +If you have another Caddy server, it can use the above ACME server to issue its own certificates: + +``` +{ + acme_ca https://acme.example.com/acme/home/directory + acme_ca_root /path/to/home_ca_root.crt +} + +example.com { + respond "Hello, world!" +} +``` + +##### basic_auth +Enables HTTP Basic Authentication, which can be used to protect directories and files with a username and hashed password. + +**Note that basic auth is not secure over plain HTTP.** Use discretion when deciding what to protect with HTTP Basic Authentication. + +When a user requests a resource that is protected, the browser will prompt the user for a username and password if they have not already supplied one. If the proper credentials are present in the `Authorization` header, the server will grant access to the resource. If the header is missing or the credentials are incorrect, the server will respond with `HTTP 401 Unauthorized`. + +Caddy configuration does not accept plaintext passwords; you MUST hash them before putting them into the configuration. The `caddy hash-password` command can help with this. + +After a successful authentication, the `{http.auth.user.id}` placeholder will be available, which contains the authenticated username. + +``` +basic_auth [] [ []] { + + ... +} +``` + +- **** is the name of the password hashing algorithm (or KDF) used for the hashes in this configuration. Default: `bcrypt` + +- **** is a custom realm name. + +- **** is a username or user ID. + +- **** is the password hash. + +Examples: + +Require authentication for all requests to `example.com`: + +``` +example.com { + basic_auth { + # Username "Bob", password "hiccup" + Bob $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG + } + respond "Welcome, {http.auth.user.id}" 200 +} +``` + +Protect files in `/secret/` so only `Bob` can access them (and anyone can see other paths): + +``` +example.com { + root * /srv + + basic_auth /secret/* { + # Username "Bob", password "hiccup" + Bob $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG + } + + file_server +} +``` + +##### bind +Overrides the interface to which the server's socket should bind. + +Normally, the listener binds to the empty (wildcard) interface. However, you may force the listener to bind to another hostname or IP instead. This directive accepts only a host, not a port. The port is determined by the site address (defaulting to `443`). + +Note that binding sites inconsistently may result in unintended consequences. For example, if two sites on the same port resolve to `127.0.0.1` and only one of those sites is configured with `bind 127.0.0.1`, then only one site will be accessible since the other will bind to the port without a specific host; the OS will choose the more specific matching socket. (Virtual hosts are not shared across different listeners.) + +`bind` accepts network addresses, but may not include a port. + +``` +bind +``` + +- **** is the list of host interfaces to bind which to bind the listener. + +Examples: + +To make a socket accessible only on the current machine, bind to the loopback interface (localhost): + +``` +example.com { + bind 127.0.0.1 +} +``` + +To include IPv6: + +``` +example.com { + bind 127.0.0.1 [::1] +} +``` + +To bind to `10.0.0.1:8080`: + +``` +example.com:8080 { + bind 10.0.0.1 +} +``` + +To bind to a Unix domain socket at `/run/caddy`: + +``` +example.com { + bind unix//run/caddy +} +``` + +To change the file permission to be writable by all users (defaults to `0200`, which is only writable by the owner): + +``` +example.com { + bind unix//run/caddy|0222 +} +``` + +To bind one domain to two different interfaces, with different responses: + +``` +example.com { + bind 10.0.0.1 + respond "One" +} + +example.com { + bind 10.0.0.2 + respond "Two" +} +``` + +##### encode +Encodes responses using the configured encoding(s). A typical use for encoding is compression. + +``` +encode [] { + # encoding formats + gzip [] + zstd + + minimum_length + + # response matcher single line syntax + match [header []] | [status ] + # or response matcher block for multiple conditions + match { + status + header [] + } +} +``` + +- **** is the list of encoding formats to enable. If multiple encodings are enabled, the encoding is chosen based the request's Accept-Encoding header; if the client has no strong preference (q-factor), then the first supported encoding is used. + +- **gzip** enables Gzip compression, optionally at the specified level. + +- **zstd** enables Zstandard compression. + +- **minimum_length** the minimum number of bytes a response should have to be encoded (default: 512). + +- **match** is a response matcher. Only matching responses are encoded. The default looks like this: + + ``` + match { + header Content-Type application/atom+xml* + header Content-Type application/eot* + header Content-Type application/font* + header Content-Type application/geo+json* + header Content-Type application/graphql+json* + header Content-Type application/javascript* + header Content-Type application/json* + header Content-Type application/ld+json* + header Content-Type application/manifest+json* + header Content-Type application/opentype* + header Content-Type application/otf* + header Content-Type application/rss+xml* + header Content-Type application/truetype* + header Content-Type application/ttf* + header Content-Type application/vnd.api+json* + header Content-Type application/vnd.ms-fontobject* + header Content-Type application/wasm* + header Content-Type application/x-httpd-cgi* + header Content-Type application/x-javascript* + header Content-Type application/x-opentype* + header Content-Type application/x-otf* + header Content-Type application/x-perl* + header Content-Type application/x-protobuf* + header Content-Type application/x-ttf* + header Content-Type application/xhtml+xml* + header Content-Type application/xml* + header Content-Type font/* + header Content-Type image/svg+xml* + header Content-Type image/vnd.microsoft.icon* + header Content-Type image/x-icon* + header Content-Type multipart/bag* + header Content-Type multipart/mixed* + header Content-Type text/* + } + ``` + +**Response matchers** can be used to filter (or classify) responses by specific criteria. + +``` +status +``` + +By HTTP status code. + +- **** is a list of HTTP status codes. Special cases are `2xx`, `3xx`, ... which match against all status codes in the range of 200-299, 300-399, ... respectively + +Examples: + +Enable Gzip compression: + +``` +encode gzip +``` + +Enable Zstandard and Gzip compression (with Zstandard implicitly preferred, since it is first): + +``` +encode zstd gzip +``` + +And in a full site, compressing static files served by [`file_server`](https://caddyserver.com/docs/caddyfile/directives/file_server): + +``` +example.com { + root * /srv + encode zstd gzip + file_server +} +``` + +##### error +Triggers an error in the HTTP handler chain, with an optional message and recommended HTTP status code. + +This handler does not write a response. Instead, it's meant to be paired with the `handle_errors` directive to invoke your custom error handling logic. + +``` +error [] | [] { + message +} +``` + +- **** is the HTTP status code to write. Default is `500`. +- **** is the error message. Default is no error message. +- **message** is an alternate way to provide an error message; convenient if it is multiple lines. + +To clarify, the first non-matcher argument can be either a 3-digit status code, or an error message string. If it is an error message, the next argument can be the status code. + +Examples: + +Trigger an error on certain request paths, and use `handle_errors` to write a response: + +``` +example.com { + root * /srv + + # Trigger errors for certain paths + error /private* "Unauthorized" 403 + error /hidden* "Not found" 404 + + # Handle the error by serving an HTML page + handle_errors { + rewrite * /{err.status_code}.html + file_server + } + + file_server +} +``` + +##### file_server +A static file server that supports real and virtual file systems. It forms file paths by appending the request's URI path to the site's root path. + +By default, it enforces canonical URIs; meaning HTTP redirects will be issued for requests to directories that do not end with a trailing slash (to add it), or requests to files that have a trailing slash (to remove it). However, redirects are not issued if an internal rewrite modifies the last element of the path (the filename). + +Most often, the `file_server` directive is paired with the `root` directive to set the file root for the whole site. This directive also has a `root` subdirective (see below) to set the root only for this handler (not recommended). Note that a site root does not carry sandbox guarantees: the file server does prevent directory traversal from path components, but symbolic links within the root can still allow accesses outside of the root. + +When errors occur (e.g. file not found `404`, permission denied `403`), the error routes will be invoked. Use the `handle_errors` directive to define error routes, and display custom error pages. + +``` +file_server [] [browse] { + fs + root + hide + index + browse [] { + reveal_symlinks + } + precompressed + status + disable_canonical_uris + pass_thru +} +``` + +- **fs** specifies an alternate (perhaps virtual) file system to use. Any Caddy module in the `caddy.fs` namespace can be used here. Any root path/prefix will still apply to alternate file system modules. By default, the local disk is used. + + [`xcaddy`](https://caddyserver.com/docs/build#xcaddy) v0.4.0 introduces the [`--embed` flag](https://github.com/caddyserver/xcaddy#custom-builds) to embed a filesystem tree into the custom Caddy build, and registers an `fs` module named `embedded` which allows your static site to be distributed as a Caddy executable. + +- **root** sets the path to the site root. It's similar to the `root` directive except it applies to this file server instance only and overrides any other site root that may have been defined. Default: `{http.vars.root}` or the current working directory. Note: This subdirective only changes the root for this handler. For other directives (like `try_files` or `templates`) to know the same site root, use the `root` directive instead. + +- **hide** is a list of files or folders to hide; if requested, the file server will pretend they do not exist. Accepts placeholders and glob patterns. Note that these are _file system_ paths, NOT request paths. In other words, relative paths use the current working directory as a base, NOT the site root; and all paths are transformed to their absolute form before comparisons (if possible). Specifying a file name or pattern without a path separator will hide all files with a matching name regardless of its location; otherwise, a path prefix match will be attempted, and then a globular match. Since this is a Caddyfile config, the active configuration file(s) will be added by default. + +- **index** is a list of filenames to look for as index files. Default: `index.html index.txt` + +- **browse** enables file listings for requests to directories that do not have an index file. + + - **** is an optional custom template file to use for directory listings. Defaults to the template that can be extracted using the command `caddy file-server export-template`, which will print the defaut template to stdout. The embedded template can also be found [here in the source code ![external link](https://caddyserver.com/old/resources/images/external-link.svg)](https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/fileserver/browse.html) . Browse templates can use actions from [the standard templates module](https://caddyserver.com/docs/modules/http.handlers.templates#docs) as well. + + - **reveal_symlinks** enables revealing the targets of symbolic links in directory listings. By default, the symlink targets are hidden, and only the link file itself is shown. + +- **precompressed** is the list of encoding formats to search for precompressed sidecar files. Arguments are an ordered list of encoding formats to search for precompressed sidecar files. Supported formats are `gzip` (`.gz`), `zstd` (`.zst`) and `br` (`.br`). + + All file lookups will look for the existence of the uncompressed file first. Once found Caddy will look for sidecar files with the file extension of each enabled format. If a precompressed sidecar file is found, Caddy will respond with the precompressed file, with the `Content-Encoding` response header set appropriately. Otherwise, Caddy will respond with the uncompressed file as normal. If the `encode` directive is enabled, then it may compress the response on-the-fly if not precompressed. + +- **status** is an optional status code override to be used when writing the response. Particularly useful when responding to a request with a custom error page. Can be a 3-digit status code, For example: `404`. Placeholders are supported. By default, the written status code will typically be `200`, or `206` for partial content. + +- **disable_canonical_uris** disables the default behaviour of redirecting (to add a trailing slash if the request path is a directory, or remove the trailing slash if the request path is a file). Note that by default, canonicalization will not happen if the last element of the request's path (the filename) underwent an internal rewrite, to avoid clobbering an explicit rewrite with implicit behaviour. + +- **pass_thru** enables pass-thru mode, which continues to the next HTTP handler in the route if the requested file is not found, instead of triggering a `404` error (invoking `handle_errors` routes). Practically, this is only useful inside of a `route` block with other handler directives following `file_server`, because this directive is effectively [ordered last](https://caddyserver.com/docs/caddyfile/directives#directive-order). + + +Examples: + +A static file server out of the current directory: + +``` +file_server +``` + +With file listings enabled: + +``` +file_server browse +``` + +Only serve static files within the `/static` folder: + +``` +file_server /static/* +``` + +The `file_server` directive is usually paired with the `root` directive to set the root path from which to serve files: + +``` +example.com { + root * /srv + file_server +} +``` + +If you're running Caddy as a systemd service, reading files from `/home` will not work, because the `caddy` user does not have "executable" permission on the `/home` directory (necessary for traversal). It's recommended that you place your files in `/srv` or `/var/www/html` instead. + +Hide all `.git` folders and their contents: + +``` +file_server { + hide .git +} +``` + +If supported by the client (`Accept-Encoding` header) checks the existence of precompressed files along side the requested file. So if `/path/to/file` is requested, it checks for `/path/to/file.zst`, `/path/to/file.br` and `/path/to/file.gz` in that order and serves the first available file with corresponding Content-Encoding: + +``` +file_server { + precompressed zstd br gzip +} +``` + +##### forward_auth +Caddy's `reverse_proxy` is capable of performing "pre-check requests" to an external service, but this directive is tailored specifically for the authentication usecase. This directive is actually just a convenient way to use a longer, more common configuration (below). + +This directive makes a `GET` request to the configured upstream with the `uri` rewritten: + +- If the upstream responds with a `2xx` status code, then access is granted and the header fields in `copy_headers` are copied to the original request, and handling continues. +- Otherwise, if the upstream responds with any other status code, then the upstream's response is copied back to the client. This response should typically involve a redirect to login page of the authentication gateway. + +If this behaviour is not exactly what you want, you may take the [expanded form](https://caddyserver.com/docs/caddyfile/directives/forward_auth#expanded-form) below as a basis and customize it to your needs. + +All the subdirectives of `reverse_proxy` are supported, and passed through to the underlying `reverse_proxy` handler. + +``` +forward_auth [] [] { + uri + copy_headers { + + } +} +``` + +- **** is a list of upstreams (backends) to which to send auth requests. + +- **uri** is the URI (path and query) to set on the request sent to the upstream. This will usually be the verification endpoint of the authentication gateway. + +- **copy_headers** is a list of HTTP header fields to copy from the response to the original request, when the request has a success status code. + + The field can be renamed by using `>` followed by the new name, for example `Before>After`. + + A block may be used to list all the fields, one per line, if you prefer for readability. + +The `forward_auth` directive is the same as the following configuration. Auth gateways like [Authelia](./Authelia.md) work well with this preset. If yours does not, feel free to borrow from this and customize it as needed instead of using the `forward_auth` shortcut. + +``` +reverse_proxy { + # Always GET, so that the incoming + # request's body is not consumed + method GET + + # Change the URI to the auth gateway's + # verification endpoint + rewrite + + # Forward the original method and URI, + # since they get rewritten above; this + # is in addition to other X-Forwarded-* + # headers already set by reverse_proxy + header_up X-Forwarded-Method {method} + header_up X-Forwarded-Uri {uri} + + # On a successful response, copy response headers + @good status 2xx + handle_response @good { + request_header { + # for example, for each copy_headers field... + Remote-User {rp.header.Remote-User} + Remote-Email {rp.header.Remote-Email} + } + } +} +``` + +Examples: + +Delegating authentication to [Authelia](https://www.authelia.com/), before serving your app via a reverse proxy: + +``` +# Serve the authentication gateway itself +auth.example.com { + reverse_proxy authelia:9091 +} + +# Serve your app +app1.example.com { + forward_auth authelia:9091 { + uri /api/verify?rd=https://auth.example.com + copy_headers Remote-User Remote-Groups Remote-Name Remote-Email + } + + reverse_proxy app1:8080 +} +``` + +##### fs +Sets which file system should be used for performing file I/O. + +This could let you connect to a remote filesystem running in the cloud, or a database with a file-like interface, or even to read from files embedded within the Caddy binary. + +First, you must declare a file system name using the `filesystem` global option, then you can use this directive to specify which file system to use. + +This directive is often used in conjunction with the `file_server` directive to serve static files, or the `try_files` directive to perform rewrites based on the existence of files. Typically also used with `root` directive to set the root path within the file system. + +``` +fs [] +``` + +Examples: + +Using an file system named `foo`, using an imaginary module named `custom` which might require authentication: + +``` +{ + filesystem foo custom { + api_key abc123 + } +} + +example.com { + fs foo + root /srv + file_server +} +``` + +To only serve images from the `foo` file system, and the rest from the default file system: + +``` +example.com { + fs /images* foo + root /srv + file_server +} +``` + +##### handle +Evaluates a group of directives mutually exclusively from other `handle` blocks at the same level of nesting. + +In other words, when multiple `handle` directives appear in sequence, only the first _matching_ `handle` block will be evaluated. A handle with no matcher acts like a _fallback_ route. + +The `handle` directives are sorted according to the [directive sorting algorithm](https://caddyserver.com/docs/caddyfile/directives#sorting-algorithm) by their matchers. The `handle_path` directive is a special case which sorts at the same priority as a `handle` with a path matcher. + +Handle blocks can be nested if needed. Only HTTP handler directives can be used inside handle blocks. + +``` +handle [] { + +} +``` + +- **** is a list of HTTP handler directives or directive blocks, one per line, just like would be used outside of a handle block. + +Examples: + +Handle requests in `/foo/` with the static file server, and other requests with the reverse proxy: + +``` +example.com { + handle /foo/* { + file_server + } + + handle { + reverse_proxy 127.0.0.1:8080 + } +} +``` + +You can mix `handle` and `handle_path` in the same site, and they will still be mutually exclusive from each other: + +``` +example.com { + handle_path /foo/* { + # The path has the "/foo" prefix stripped + } + + handle /bar/* { + # The path still retains "/bar" + } +} +``` + +You can nest `handle` blocks to create more complex routing logic: + +``` +example.com { + handle /foo* { + handle /foo/bar* { + # This block only matches paths under /foo/bar + } + + handle { + # This block matches everything else under /foo/ + } + } + + handle { + # This block matches everything else (acts as a fallback) + } +} +``` + +##### handle_errors +Sets up error handlers. + +When the normal HTTP request handlers return an error, normal processing stops and the error handlers are invoked. Error handlers form a route which is just like normal routes, and they can do anything that normal routes can do. This enables great control and flexibility when handling errors during HTTP requests. For example, you can serve static error pages, templated error pages, or reverse proxy to another backend to handle errors. + +The directive may be repeated with different status codes to handle different errors differently. If no status codes are specified, then it will match any error, acting as a fallback if any other error handlers does not match. + +A request's context is carried into error routes, so any values set on the request context such as site root or vars will be preserved in error handlers, too. Additionally, [new placeholders](https://caddyserver.com/docs/caddyfile/directives/handle_errors#placeholders) are available when handling errors. + +Note that certain directives, for example `reverse_proxy` which may write a response with an HTTP status which is classified as an error, will _not_ trigger the error routes. + +You may use the `error` directive to explicitly trigger an error based on your own routing decisions. + +``` +handle_errors [] { + +} +``` + +- **** is one or more HTTP status codes to match against the error being handled. The status codes may be 3-digit numbers, or a special case of `4xx` or `5xx` which match against all status codes in the range of 400-499 or 500-599, respectively. If no status codes are specified, then it will match any error, acting as a fallback if any other error handlers does not match. + +- **** is a list of HTTP handler [directives](https://caddyserver.com/docs/caddyfile/directives) and [matchers](https://caddyserver.com/docs/caddyfile/matchers), one per line. + + +The following placeholders are available while handling errors. They are Caddyfile shorthands for the full placeholders which can be found in [the JSON docs for an HTTP server's error routes](https://caddyserver.com/docs/json/apps/http/servers/errors/#routes). + +| Placeholder | Description | +| ------------------- | ----------------------------------------------------------- | +| `{err.status_code}` | The recommended HTTP status code | +| `{err.status_text}` | The status text associated with the recommended status code | +| `{err.message}` | The error message | +| `{err.trace}` | The origin of the error | +| `{err.id}` | An identifier for this occurrence of the error | + +Examples: + +Custom error pages based on the status code (i.e. a page called `404.html` for `404` errors). Note that `file_server` preserves the error's HTTP status code when run in `handle_errors` (assumes you set a site root in your site beforehand): + +``` +handle_errors { + rewrite * /{err.status_code}.html + file_server +} +``` + +A single error page that uses `templates` to write a custom error message: + +``` +handle_errors { + rewrite * /error.html + templates + file_server +} +``` + +If you want to provide custom error pages only for some error codes, you can check the existence of the custom error files beforehand with a `file` matcher: + +``` +handle_errors { + @custom_err file /err-{err.status_code}.html /err.html + handle @custom_err { + rewrite * {file_match.relative} + file_server + } + respond "{err.status_code} {err.status_text}" +} +``` + +Reverse proxy to a professional server that is highly qualified for handling HTTP errors and improving your day 😸: + +``` +handle_errors { + rewrite * /{err.status_code} + reverse_proxy https://http.cat { + header_up Host {upstream_hostport} + replace_status {err.status_code} + } +} +``` + +Simply use `respond` to return the error code and name + +``` +handle_errors { + respond "{err.status_code} {err.status_text}" +} +``` + +To handle specific error codes differently: + +``` +handle_errors 404 410 { + respond "It's a 404 or 410 error!" +} + +handle_errors 5xx { + respond "It's a 5xx error." +} + +handle_errors { + respond "It's another error" +} +``` + +The above behaves the same as the below, which uses an `expression` matcher against the status codes, and using `handle` for mutual exclusivity: + +``` +handle_errors { + @404-410 `{err.status_code} in [404, 410]` + handle @404-410 { + respond "It's a 404 or 410 error!" + } + + @5xx `{err.status_code} >= 500 && {err.status_code} < 600` + handle @5xx { + respond "It's a 5xx error." + } + + handle { + respond "It's another error" + } +} +``` + +##### handle_path +Works the same as the `handle` directive, but implicitly uses `uri strip_prefix` to strip the matched path prefix. + +Handling a request matching a certain path (while stripping that path from the request URI) is a common enough use case that it has its own directive for convenience. + +``` +handle_path { + +} +``` + +- **** is a list of HTTP handler directives or directive blocks, one per line, just like would be used outside of a `handle_path` block. + +Only a single path matcher is accepted, and is required; you cannot use named matchers with `handle_path`. + +Examples: + +This configuration: + +``` +handle_path /prefix/* { + ... +} +``` + +👆 is effectively the same as this 👇, but the `handle_path` form 👆 is slightly more succinct + +``` +handle /prefix/* { + uri strip_prefix /prefix + ... +} +``` + +A full Caddyfile example, where `handle_path` and `handle` are mutually exclusive. + +``` +example.com { + # Serve your API, stripping the /api prefix + handle_path /api/* { + reverse_proxy localhost:9000 + } + + # Serve your static site + handle { + root * /srv + file_server + } +} +``` + +##### header +Manipulates HTTP response header fields. It can set, add, and delete header values, or perform replacements using regular expressions. + +By default, header operations are performed immediately unless any of the headers are being deleted (`-` prefix) or setting a default value (`?` prefix). In those cases, the header operations are automatically deferred until the time they are being written to the client. + +To manipulate HTTP request headers, you may use the `request_header` directive. + +``` +header [] [[+|-|?|>] [|] []] { + # Add + + + + # Set + + + # Set with defer + > + + # Delete + - + + # Replace + + + # Replace with defer + > + + # Default + ? + + [defer] +} +``` + +- **** is the name of the header field. + + With no prefix, the field is set (overwritten). + + Prefix with `+` to add the field instead of overwriting (setting) the field if it already exists; header fields can appear more than once in a request. + + Prefix with `-` to delete the field. The field may use prefix or suffix `*` wildcards to delete all matching fields. + + Prefix with `?` to set a default value for the field. The field is only written if it doesn't yet exist. + + Prefix with `>` to set the field, and enable `defer`, as a shortcut. + +- **** is the header field value, when adding or setting a field. + +- **** is the substring or regular expression to search for. + +- **** is the replacement value; required if performing a search-and-replace. + +- **defer** will force the header operations to be deferred until the response is being written out to the client. This is automatically enabled if any of the header fields are being deleted with `-`, when setting a default value with `?`, or when having used the `>` prefix. + + +For multiple header manipulations, you can open a block and specify one manipulation per line in the same way. + +When using the `?` prefix to set a default header value, it is automatically separated into its own `header` handler, if it was in a `header` block with multiple header operations. [Under the hood](https://caddyserver.com/docs/modules/http.handlers.headers#response/require), using `?` configures a response matcher which applies to the directive's entire handler, which only applies the header operations (like `defer`), but only if the field is not yet set. + +Examples: + +Set a custom header field on all requests: + +``` +header Custom-Header "My value" +``` + +Strip the "Hidden" header field: + +``` +header -Hidden +``` + +Replace `http://` with `https://` in any Location header: + +``` +header Location http:// https:// +``` + +Set security and privacy headers on all pages: (**WARNING:** only use if you understand the implications!) + +``` +header { + # disable FLoC tracking + Permissions-Policy interest-cohort=() + + # enable HSTS + Strict-Transport-Security max-age=31536000; + + # disable clients from sniffing the media type + X-Content-Type-Options nosniff + + # clickjacking protection + X-Frame-Options DENY +} +``` + +Multiple header directives that are intended to be mutually-exclusive: + +``` +route { + header Cache-Control max-age=3600 + header /static/* Cache-Control max-age=31536000 +} +``` + +Set a default cache expiration if upstream doesn't define one: + +``` +header ?Cache-Control "max-age=3600" +reverse_proxy upstream:443 +``` + +To override the cache expiration that a proxy upstream had set for paths starting with `/no-cache`; enabling `defer` is necessary to ensure the header is set _after_ the proxy writes its headers: + +``` +header /no-cache* >Cache-Control nocache +reverse_proxy upstream:443 +``` + +To perform a deferred update of a `Set-Cookie` header to add `SameSite=None`; a regexp capture is used to grab the existing value, and `$1` re-inserts it at the start with the additional option appended: + +``` +header >Set-Cookie (.*) "$1; SameSite=None;" +``` + +##### import +Includes a snippet or file, replacing this directive with the contents of the snippet or file. + +This directive is a special case: it is evaluated before the structure is parsed, and it can appear anywhere in the Caddyfile. + +``` +import [] +``` + +- **** is the filename, glob pattern, or name of to include. Its contents will replace this line as if that file's contents appeared here to begin with. + + It is an error if a specific file cannot be found, but an empty glob pattern is not an error. + + If importing a specific file, a warning will be emitted if the file is empty. + + If the pattern is a filename or glob, it is always relative to the file the `import` appears in. + + If using a glob pattern `*` as the final path segment, hidden files (i.e. files starting with a `.`) are ignored. To import hidden files, use `.*` as the final segment. + +- **** is an optional list of arguments to pass to the imported tokens. This placeholder is a special case and is evaluated at Caddyfile-parse-time, not at run-time. They can be used in various forms, similarly to Go's slice syntax: + + - `{args[n]}` where `n` is the 0-based positional index of the parameter + - `{args[:]}` where all the arguments are inserted + - `{args[:m]}` where the arguments before `m` are inserted + - `{args[n:]}` where the arguments beginning with `n` are inserted + - `{args[n:m]}` where the arguments in the range between `n` and `m` are inserted + + For the forms that insert many tokens, the placeholder **must** be a token on its own, it cannot be part of another token. In other words, it must have spaces around it, and cannot be in quotes. + + Note that prior to v2.7.0, the syntax was `{args.N}` but this form was deprecated in favor of the more flexible syntax above. + + +Examples: + +Import all files in an adjacent sites-enabled folder (except hidden files): + +``` +import sites-enabled/* +``` + +Import a snippet that sets CORS headers using an import argument: + +``` +(cors) { + @origin header Origin {args[0]} + header @origin Access-Control-Allow-Origin "{args[0]}" + header @origin Access-Control-Allow-Methods "OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" +} + +example.com { + import cors example.com +} +``` + +Import a snippet which takes a list of proxy upstreams as arguments: + +``` +(https-proxy) { + reverse_proxy {args[:]} { + transport http { + tls + } + } +} + +example.com { + import https-proxy 10.0.0.1 10.0.0.2 10.0.0.3 +} +``` + +Import a snippet which creates a proxy with a prefix rewrite rule as the first argument: + +``` +(proxy-rewrite) { + rewrite * {args[0]}{uri} + reverse_proxy {args[1:]} +} + +example.com { + import proxy-rewrite /api 10.0.0.1 10.0.0.2 10.0.0.3 +} +``` + +##### log +Enables and configures HTTP request logging (also known as access logs). + +To configure Caddy's runtime logs, see the `log` global option instead. + +The `log` directive applies to the hostnames of the site block it appears in, unless overridden with the `hostnames` subdirective. + +When configured, by default all requests to the site will be logged. To conditionally skip some requests from logging, use the `log_skip` directive. + +To add custom fields to the log entries, use the `log_append` directive. + +Since Caddy v2.5, by default, headers with potentially sensitive information (`Cookie`, `Set-Cookie`, `Authorization` and `Proxy-Authorization`) will be logged with empty values. This behaviour can be disabled with the `log_credentials` global server option. + +``` +log [] { + hostnames + output ... + format ... + level +} +``` + +- **logger_name** is an optional override of the logger name for this site. + + By default, a logger name is generated automatically, e.g. `log0`, `log1`, and so on depending on the order of the sites in the Caddyfile. This is only useful if you wish to reliably refer to the output of this logger from another logger defined in global options. + +- **hostnames** is an optional override of the hostnames that this logger applies to. + + By default, the logger applies to the hostnames of the site block it appears in, i.e. the site addresses. This is useful if you wish to define different loggers per subdomain in a wildcard site block. + +- **output** configures where to write the logs.. + + Default: `stderr`. + +- **format** describes how to encode, or format, the logs.. + + Default: `console` if `stderr` is detected to be a terminal, `json` otherwise. + +- **level** is the minimum entry level to log. Default: `INFO`. + + Note that access logs currently only emit `INFO` and `ERROR` level logs. + +Examples: + +Enable access logging to the default logger. + +In other words, by default this logs to `stderr`, but this can be changed by reconfiguring the `default` logger with the `log` global option: + +``` +example.com { + log +} +``` + +Write logs to a file (with log rolling, which is enabled by default): + +``` +example.com { + log { + output file /var/log/access.log + } +} +``` + +Customize log rolling: + +``` +example.com { + log { + output file /var/log/access.log { + roll_size 1gb + roll_keep 5 + roll_keep_for 720h + } + } +} +``` + +Delete the `User-Agent` request header from the logs: + +``` +example.com { + log { + format filter { + request>headers>User-Agent delete + } + } +} +``` + +Redact multiple sensitive cookies. (Note that some sensitive headers are logged with empty values by default; see the `log_credentials` global option to enable logging `Cookie` header values): + +``` +example.com { + log { + format filter { + request>headers>Cookie cookie { + replace session REDACTED + delete secret + } + } + } +} +``` + +Mask the remote address from the request, keeping the first 16 bits (i.e. 255.255.0.0) for IPv4 addresses, and the first 32 bits from IPv6 addresses. + +Note that as of Caddy v2.7, both `remote_ip` and `client_ip` are logged, where `client_ip` is the "real IP" when `trusted_proxies` is configured: + +``` +example.com { + log { + format filter { + request>remote_ip ip_mask 16 32 + request>client_ip ip_mask 16 32 + } + } +} +``` + +To append a server ID from an environment variable to all log entries, and chain it with a `filter` to delete a header: + +``` +example.com { + log { + format append { + server_id {env.SERVER_ID} + wrap filter { + request>headers>Cookie delete + } + } + } +} +``` + +To write separate log files for each subdomain in a wildcard site block. by overriding `hostnames` for each logger. This uses a snippet to avoid repetition: + +``` +(subdomain-log) { + log { + hostnames {args[0]} + output file /var/log/{args[0]}.log + } +} + +*.example.com { + import subdomain-log foo.example.com + @foo host foo.example.com + handle @foo { + respond "foo" + } + + import subdomain-log bar.example.com + @bar host bar.example.com + handle @bar { + respond "bar" + } +} +``` + +##### log_append +Appends a field to the access log for the current request. + +This should be used alongside the `log` directive which is required to enable access logging in the first place. + +The value may be a static string, or a placeholder which will be replaced with the value of the placeholder at the time of the request. + +``` +log_append [] +``` + +Examples: + +Display in the logs the area of the site that the request is being served from, either `static` or `dynamic`: + +``` +example.com { + log + + handle /static* { + log_append area "static" + respond "Static response!" + } + + handle { + log_append area "dynamic" + reverse_proxy localhost:9000 + } +} +``` + +##### log_skip +Skips access logging for matched requests. + +This should be used alongside the `log` directive to skip logging requests that are not relevant for your needs. + +Prior to v2.8.0, this directive was named `skip_log`, but was renamed for consistency with other directives. + +``` +log_skip [] +``` + +Examples: + +Skip access logging for static files stored in a subpath: + +``` +example.com { + root * /srv + + log + log_skip /static* + + file_server +} +``` + +Skip access logging for requests matching a pattern; in this case, for files with particular extensions: + +``` +@skip path_regexp \.(js|css|png|jpe?g|gif|ico|woff|otf|ttf|eot|svg|txt|pdf|docx?|xlsx?)$ +log_skip @skip +``` + +The matcher is not needed if it's found within a route which is already within a matcher. For example with a handle for a file server for a particular subpath: + +``` +handle_path /static* { + root * /srv/static + log_skip + file_server +} +``` + +##### method +Changes the HTTP method on the request. + +``` +method [] +``` + +- **** is the HTTP method to change the request to. + +Examples: + +Change the method for all requests under `/api` to `POST`: + +``` +method /api* POST +``` + +##### redir +Issues an HTTP redirect to the client. + +This directive implies that a matched request is to be rejected as-is, and the client should try again at a different URL. For that reason, its directive order is very early. + +``` +redir [] [] +``` + +- **** is the target location. Becomes the response's `Location` header. + +- **** is the HTTP status code to use for the redirect. Can be: + + - A positive integer in the `3xx` range, or `401` + + - `temporary` for a temporary redirect (`302`, this is the default) + + - `permanent` for a permanent redirect (`301`) + + - `html` to use an HTML document to perform the redirect (useful for redirecting browsers but not API clients) + + - A placeholder with a status code value + + +Examples: + +Redirect all requests to `https://example.com`: + +``` +www.example.com { + redir https://example.com +} +``` + +Same, but preserve the existing URI by appending the `{uri}` placeholder: + +``` +www.example.com { + redir https://example.com{uri} +} +``` + +Same, but permanent: + +``` +www.example.com { + redir https://example.com{uri} permanent +} +``` + +Redirect your old `/about-us` page to your new `/about` page: + +``` +example.com { + redir /about-us /about + reverse_proxy localhost:9000 +} +``` + +##### request_body +Manipulates or sets restrictions on the bodies of incoming requests. + +``` +request_body [] { + max_size +} +``` + +- **max_size** is the maximum size in bytes allowed for the request body. It accepts all size values supported by [∂go-humanize](https://pkg.go.dev/github.com/dustin/go-humanize#pkg-constants). Reads of more bytes will return an error with HTTP status `413`. + +Examples: + +Limit request body sizes to 10 megabytes: + +``` +example.com { + request_body { + max_size 10MB + } + reverse_proxy localhost:8080 +} +``` + +##### request_header +Manipulates HTTP header fields on the request. It can set, add, and delete header values, or perform replacements using regular expressions. + +If you intend to manipulate headers for proxying, use the `header_up` subdirective of `reverse_proxy` instead, as those manipulations are proxy-aware. + +To manipulate HTTP response headers, you may use the `header` directive. + +``` +request_header [] [[+|-] [|] []] +``` + +- **** is the name of the header field. + + With no prefix, the field is set (overwritten). + + Prefix with `+` to add the field instead of overwriting (setting) the field if it already exists; header fields can appear more than once in a request. + + Prefix with `-` to delete the field. The field may use prefix or suffix `*` wildcards to delete all matching fields. + +- **** is the header field value, if adding or setting a field. + +- **** is the substring or regular expression to search for. + +- **** is the replacement value; required if performing a search-and-replace. + + +Examples: + +Remove the Referer header from the request: + +``` +request_header -Referer +``` + +Delete all headers containing an underscore from the request: + +``` +request_header -*_* +``` + +##### respond +Writes a hard-coded/static response to the client. + +If the body is non-empty, this directive sets the `Content-Type` header if it is not already set. The default value is `text/plain; utf-8` unless the body is a valid JSON object or array, in which case it is set to `application/json`. For all other types of content, set the proper Content-Type explicitly using the `header` directive. + +``` +respond [] | [] { + body + close +} +``` + +- **** is the HTTP status code to write. + + If `103` (Early Hints), the response will be written without a body and the handler chain will continue. (HTTP `1xx` responses are informational, not final.) + + Default: `200` + +- **** is the response body to write. + +- **body** is an alternate way to provide a body; convenient if it is multiple lines. + +- **close** will close the client's connection to the server after writing the response. + + +To clarify, the first non-matcher argument can be either a 3-digit status code or a response body string. If it is a body, the next argument can be the status code. + +Responding with an error status code is different than returning an error in the handler chain, which invokes error handlers internally. + +Examples: + +Write an empty 200 status with an empty body to all health checks, and a simple response body to all other requests: + +``` +example.com { + respond /health-check 200 + respond "Hello, world!" +} +``` + +Write an error response and close the connection: + +You might prefer to use the `error` directive instead, which triggers an error that can be handled with the `handle_errors` directive. + +``` +example.com { + respond /secret/* "Access denied" 403 { + close + } +} +``` + +Write an HTML response, using heredoc syntax to control whitespace, and also setting the `Content-Type` header to match the response body: + +``` +example.com { + header Content-Type text/html + respond < + Foo + Foo + + HTML 200 +} +``` + +##### reverse_proxy +Proxies requests to one or more backends with configurable transport, load balancing, health checking, request manipulation, and buffering options. + +``` +reverse_proxy [] [] { + # backends + to + dynamic ... + + # load balancing + lb_policy [] + lb_retries + lb_try_duration + lb_try_interval + lb_retry_match + + # active health checking + health_uri + health_port + health_interval + health_timeout + health_status + health_body + health_follow_redirects + health_headers { + [] + } + + # passive health checking + fail_duration + max_fails + unhealthy_status + unhealthy_latency + unhealthy_request_count + + # streaming + flush_interval + request_buffers + response_buffers + stream_timeout + stream_close_delay + + # request/header manipulation + trusted_proxies [private_ranges] + header_up [+|-] [ []] + header_down [+|-] [ []] + method + rewrite + + # round trip + transport { + ... + } + + # optionally intercept responses from upstream + @name { + status + header [] + } + replace_status [] + handle_response [] + + + # special directives only available in handle_response + copy_response [] [] { + status + } + copy_response_headers [] { + include + exclude + } + } +} +``` + +**Upstreams**: + +- **** is a list of upstreams (backends) to which to proxy. +- **to** is an alternate way to specify the list of upstreams, one (or more) per line. +- **dynamic** configures a _dynamic upstreams_ module. This allows getting the list of upstreams dynamically for every request. See dynamic upstreams below for a description of standard dynamic upstream modules. Dynamic upstreams are retrieved at every proxy loop iteration (i.e. potentially multiple times per request if load balancing retries are enabled) and will be preferred over static upstreams. If an error occurs, the proxy will fall back to using any statically-configured upstreams. + +**Upstream addresses**: + +Static upstream addresses can take the form of a URL that contains only scheme and host/port, or a conventional Caddy network address. Valid examples: + +- `localhost:4000` +- `127.0.0.1:4000` +- `[::1]:4000` +- `http://localhost:4000` +- `https://example.com` +- `h2c://127.0.0.1` +- `example.com` +- `unix//var/php.sock` +- `unix+h2c//var/grpc.sock` +- `localhost:8001-8006` +- `[fe80::ea9f:80ff:fe46:cbfd%eth0]:443` + +By default, connections are made to the upstream over plaintext HTTP. When using the URL form, a scheme can be used to set some `transport` defaults as a shorthand. + +- Using `https://` as the scheme will use the `http` transport with `tls` enabled. + + Additionally, you may need to override the `Host` header such that it matches the TLS SNI value, which is used by servers for routing and certificate selection. See the HTTPS section below for more details. + +- Using `h2c://` as the scheme will use the `http` transport with HTTP versions set to allow cleartext HTTP/2 connections. + +- Using `http://` as the scheme is identical to having omitted the scheme, since HTTP is already the default. This syntax is included for symmetry with the other scheme shortcuts. + + +Schemes cannot be mixed, since they modify the common transport configuration (a TLS-enabled transport cannot carry both HTTPS and plaintext HTTP). Any explicit transport configuration will not be overwritten, and omitting schemes or using other ports will not assume a particular transport. + +When using IPv6 with a zone (e.g. link-local addresses with a specific network interface), a scheme **cannot** be used as a shortcut because the `%` will result in a URL-parse error; configure the transport explicitly instead. + +When using the network address form, the network type is specified as a prefix to the upstream address. This cannot be combined with a URL scheme. As a special case, `unix+h2c/` is supported as a shortcut for the `unix/` network plus the same effects as the `h2c://` scheme. Port ranges are supported as a shortcut, which expands to multiple upstreams with the same host. + +Upstream addresses **cannot** contain paths or query strings, as that would imply simultaneous rewriting the request while proxying, which behavior is not defined or supported. You may use the `rewrite` directive should you need this. + +If the address is not a URL (i.e. does not have a scheme), then placeholders can be used, but this makes the upstream _dynamically static_, meaning that potentially many different backends act as a single, static upstream in terms of health checks and load balancing. We recommend using a dynamic upstreams module instead, if possible. When using placeholders, a port **must** be included (either by the placeholder replacement, or as a static suffix to the address). + +**Dynamic upstreams**: + +Caddy's reverse proxy comes standard with some dynamic upstream modules. Note that using dynamic upstreams has implications for load balancing and health checks, depending on specific policy configuration: active health checks do not run for dynamic upstreams; and load balancing and passive health checks are best served if the list of upstreams is relatively stable and consistent (especially with round-robin). Ideally, dynamic upstream modules only return healthy, usable backends. + +**Load balancing**: + +Load balancing is typically used to split traffic between multiple upstreams. By enabling retries, it can also be used with one or more upstreams, to hold requests until a healthy upstream can be selected (e.g. to wait and mitigate errors while rebooting or redeploying an upstream). + +This is enabled by default, with the `random` policy. Retries are disabled by default. + +- **lb_policy** is the name of the load balancing policy, along with any options. Default: `random`. + + For policies that involve hashing, the [highest-random-weight (HRW)](https://en.wikipedia.org/wiki/Rendezvous_hashing) algorithm is used to ensure that a client or request with the same hash key is mapped to the same upstream, even if the list of upstreams change. + + Some policies support fallback as an option, if noted, in which case they take a block with `fallback ` which takes another load balancing policy. For those policies, the default fallback is `random`. Configuring a fallback allows using a secondary policy if the primary does not select one, allowing for powerful combinations. Fallbacks can be nested multiple times if desired. + + For example, `header` can be used as primary to allow for developers to choose a specific upstream, with a fallback of `first` for all other connections to implement primary/secondary failover. + + ``` + lb_policy header X-Upstream { + fallback first + } + ``` + + - `random` randomly chooses an upstream + + - `random_choose ` selects two or more upstreams randomly, then chooses one with least load (`n` is usually 2) + + - `first` chooses the first available upstream, from the order they are defined in the config, allowing for primary/secondary failover; remember to enable health checks along with this, otherwise failover will not occur + + - `round_robin` iterates each upstream in turn + + - `weighted_round_robin ` iterates each upstream in turn, respecting the weights provided. The amount of weight arguments should match the amount of upstreams configured. Weights should be non-zero positive integers. For example with two upstreams and weights `5 1`, the first upstream would be selected 5 times in a row before the second upstream is selected once, then the cycle repeats. + + - `least_conn` choose upstream with fewest number of current requests; if more than one host has the least number of requests, then one of those hosts is chosen at random + + - `ip_hash` maps the remote IP (the immediate peer) to a sticky upstream + + - `client_ip_hash` maps the client IP to a sticky upstream; this is best paired with the `servers > trusted_proxies` global option which enables real client IP parsing, otherwise it behaves the same as `ip_hash` + + - `uri_hash` maps the request URI (path and query) to a sticky upstream + + - `query [key]` maps a request query to a sticky upstream, by hashing the query value; if the specified key is not present, the fallback policy will be used to select an upstream (`random` by default) + + - `header [field]` maps a request header to a sticky upstream, by hashing the header value; if the specified header field is not present, the fallback policy will be used to select an upstream (`random` by default) + + - `cookie [ []]` on the first request from a client (when there's no cookie), the fallback policy will be used to select an upstream (`random` by default), and a `Set-Cookie` header is added to the response (default cookie name is `lb` if not specified). The cookie value is the upstream dial address of the chosen upstream, hashed with HMAC-SHA256 (using `` as the shared secret, empty string if not specified). + + On subsequent requests where the cookie is present, the cookie value will be mapped to the same upstream if it's available; if not available or not found, a new upstream is selected with the fallback policy, and the cookie is added to the response. + + If you wish to use a particular upstream for debugging purposes, you may hash the upstream address with the secret, and set the cookie in your HTTP client (browser or otherwise). For example, with PHP, you could run the following to compute the cookie value, where `10.1.0.10:8080` is the address of one of your upstreams, and `secret` is your configured secret. + + ``` + echo hash_hmac('sha256', '10.1.0.10:8080', 'secret'); + // cdd96966817dd14a99f47ee17451464f29998da170814a16b483e4c1ff4c48cf + ``` + + You can set the cookie in your browser via the Javascript console, for example to set the cookie named `lb`: + + ``` + document.cookie = "lb=cdd96966817dd14a99f47ee17451464f29998da170814a16b483e4c1ff4c48cf"; + ``` + +- **lb_retries** is how many times to retry selecting available backends for each request if the next available host is down. By default, retries are disabled (zero). + + If `lb_try_duration` is also configured, then retries may stop early if the duration is reached. In other words, the retry duration takes precedence over the retry count. + +- **lb_try_duration** is a duration value that defines how long to try selecting available backends for each request if the next available host is down. By default, retries are disabled (zero duration). + + Clients will wait for up to this long while the load balancer tries to find an available upstream host. A reasonable starting point might be `5s` since the HTTP transport's default dial timeout is `3s`, so that should allow for at least one retry if the first selected upstream cannot be reached; but feel free to experiment to find the right balance for your usecase. + +- **lb_try_interval** is a duration value that defines how long to wait between selecting the next host from the pool. Default is `250ms`. Only relevant when a request to an upstream host fails. Be aware that setting this to `0` with a non-zero `lb_try_duration` can cause the CPU to spin if all backends are down and latency is very low. + +- **lb_retry_match** restricts with which requests retries are allowed. A request must match this condition in order to be retried if the connection to the upstream succeded but the subsequent round-trip failed. If the connection to the upstream failed, a retry is always allowed. By default, only `GET` requests are retried. + + The syntax for this option is the same as for named request matchers, but without the `@name`. If you only need a single matcher, you may configure it on the same line. For multiple matchers, a block is necessary. + + +**Active health checks**: + +Active health checks perform health checking in the background on a timer. To enable this, `health_uri` or `health_port` are required. + +- **health_uri** is the URI path (and optional query) for active health checks. + +- **health_port** is the port to use for active health checks, if different from the upstream's port. + +- **health_interval** is a duration value that defines how often to perform active health checks. Default: `30s`. + +- **health_timeout** is a duration value that defines how long to wait for a reply before marking the backend as down. Default: `5s`. + +- **health_status** is the HTTP status code to expect from a healthy backend. Can be a 3-digit status code, or a status code class ending in `xx`. For example: `200` (which is the default), or `2xx`. + +- **health_body** is a substring or regular expression to match on the response body of an active health check. If the backend does not return a matching body, it will be marked as down. + +- **health_follow_redirects** will cause the health check to follow redirects provided by upstream. Without this setting, a redirect will cause the check to fail. + +- **health_headers** allows specifying headers to set on the active health check requests. This is useful if you need to change the `Host` header, or if you need to provide some authentication to your backend as part of your health checks. + + +**Passive health checks**: + +Passive health checks happen inline with actual proxied requests. To enable this, `fail_duration` is required. + +- **fail_duration** is a duration value that defines how long to remember a failed request. A duration > `0` enables passive health checking; the default is `0` (off). A reasonable starting point might be `30s` to balance error rates with responsiveness when bringing an unhealthy upstream back online; but feel free to experiment to find the right balance for your usecase. + +- **max_fails** is the maximum number of failed requests within `fail_duration` that are needed before considering a backend to be down; must be >= `1`; default is `1`. + +- **unhealthy_status** counts a request as failed if the response comes back with one of these status codes. Can be a 3-digit status code or a status code class ending in `xx`, for example: `404` or `5xx`. + +- **unhealthy_latency** is a duration value that counts a request as failed if it takes this long to get a response. + +- **unhealthy_request_count** is the permissible number of simultaneous requests to a backend before marking it as down. In other words, if a particular backend is currently handling this many requests, then it's considered "overloaded" and other backends will be preferred instead. + + This should be a reasonably large number; configuring this means that the proxy will have a limit of `unhealthy_request_count × upstreams_count` total simultaneous requests, and any requests after that point will result in an error due to no upstreams being available. + +**Streaming**: + +By default, the proxy partially buffers the response for wire efficiency. + +The proxy also supports WebSocket connections, performing the HTTP upgrade request then transitioning the connection to a bidirectional tunnel. + +By default, WebSocket connections are forcibly closed (with a Close control message sent to both the client and upstream) when the config is reloaded. Each request holds a reference to the config, so closing old connections is necessary to keep memory usage in check. This closing behaviour can be customized with the `stream_timeout` and `stream_close_delay` options. + +- **flush_interval** is a duration value that adjusts how often Caddy should flush the response buffer to the client. By default, no periodic flushing is done. A negative value (typically -1) suggests "low-latency mode" which disables response buffering completely and flushes immediately after each write to the client, and does not cancel the request to the backend even if the client disconnects early. This option is ignored and responses are flushed immediately to the client if one of the following applies from the response: + + - `Content-Type: text/event-stream` + - `Content-Length` is unknown + - HTTP/2 on both sides of the proxy, `Content-Length` is unknown, and `Accept-Encoding` is either not set or is "identity" +- **request_buffers** will cause the proxy to read up to `` amount of bytes from the request body into a buffer before sending it upstream. This is very inefficient and should only be done if the upstream requires reading request bodies without delay (which is something the upstream application should fix). This accepts all size formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). + +- **response_buffers** will cause the proxy to read up to `` amount of bytes from the response body to be read into a buffer before being returned to the client. This should be avoided if at all possible for performance reasons, but could be useful if the backend has tighter memory constraints. This accepts all size formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). + +- **stream_timeout** is a duration value after which streaming requests such as WebSockets will be forcibly closed at the end of the timeout. This essentially cancels connections if they stay open too long. A reasonable starting point might be `24h` to cull connections older than a day. Default: no timeout. + +- **stream_close_delay** is a duration value which delays streaming requests such as WebSockets from being forcibly closed when the config is unloaded; instead, the stream will remain open until the delay is complete. In other words, enabling this prevents streams from immediately closing when Caddy's config is reloaded. Enabling this may be a good idea to avoid a thundering herd of reconnecting clients which had their connections closed by the previous config closing. A reasonable starting point might be something like `5m` to allow users 5 minutes to leave the page naturally after a config reload. Default: no delay. + +**Headers**: + +The proxy can **manipulate headers** between itself and the backend: + +- **header_up** sets, adds (with the `+` prefix), deletes (with the `-` prefix), or performs a replacement (by using two arguments, a search and replacement) in a request header going upstream to the backend. + +- **header_down** sets, adds (with the `+` prefix), deletes (with the `-` prefix), or performs a replacement (by using two arguments, a search and replacement) in a response header coming downstream from the backend. + + +For example, to set a request header, overwriting any existing values: + +``` +header_up Some-Header "the value" +``` + +To add a response header; note that there can be multiple values for a header field: + +``` +header_down +Some-Header "first value" +header_down +Some-Header "second value" +``` + +To delete a request header, preventing it from reaching the backend: + +``` +header_up -Some-Header +``` + +To delete all matching request headers, using a suffix match: + +``` +header_up -Some-* +``` + +To delete _all_ request headers, to be able to individually add the ones you want (not recommended): + +``` +header_up -* +``` + +To perform a regular expression replacement on a request header: + +``` +header_up Some-Header "^prefix-([A-Za-z0-9]*)$" "replaced-$1-suffix" +``` + +The regular expression language used is RE2, included in Go. See the [RE2 syntax reference](https://github.com/google/re2/wiki/Syntax) and the [Go regexp syntax overview](https://pkg.go.dev/regexp/syntax). The replacement string is [expanded](https://pkg.go.dev/regexp#Regexp.Expand), allowing use of captured values, for example `$1` being the first capture group. + +**Defaults**: + +By default, Caddy passes thru incoming headers—including `Host`—to the backend without modifications, with three exceptions: + +- It sets or augments the [`X-Forwarded-For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) header field. +- It sets the [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) header field. +- It sets the [`X-Forwarded-Host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host) header field. + +For these `X-Forwarded-*` headers, by default, the proxy will ignore their values from incoming requests, to prevent spoofing. + +If Caddy is not the first server being connected to by your clients (for example when a CDN is in front of Caddy), you may configure `trusted_proxies` with a list of IP ranges (CIDRs) from which incoming requests are trusted to have sent good values for these headers. + +It is strongly recommended that you configure this via the `servers > trusted_proxies` global option instead of in the proxy, so that this applies to all proxy handlers in your server, and this has the benefit of enabling client IP parsing. + +Additionally, when using the `http` transport, the `Accept-Encoding: gzip` header will be set, if it is missing in the request from the client. This allows the upstream to serve compressed content if it can. This behavior can be disabled with `compression off` on the transport. + +Since (most) headers retain their original value when being proxied, it is often necessary to override the `Host` header with the configured upstream address when proxying to HTTPS, such that the `Host` header matches the TLS ServerName value: + +``` +reverse_proxy https://example.com { + header_up Host {upstream_hostport} +} +``` + +The `X-Forwarded-Host` header is still passed by default, so the upstream may still use that if it needs to know the original `Host` header value. + +**Rewrites**: + +By default, Caddy performs the upstream request with the same HTTP method and URI as the incoming request, unless a rewrite was performed in the middleware chain before it reaches `reverse_proxy`. + +Before proxying it, the request is cloned; this ensures that any modifications done to the request during the handler do not leak to other handlers. This is useful in situations where the handling needs to continue after the proxy. + +In addition to header manipulations, the request's method and URI may be changed before it is sent to the upstream: + +- **method** changes the HTTP method of the cloned request. If the method is changed to `GET` or `HEAD`, then the incoming request's body will _not_ be sent upstream by this handler. This is useful if you wish to allow a different handler to consume the request body. +- **rewrite** changes the URI (path and query) of the cloned request. This is similar to the `rewrite` directive, except that it doesn't persist the rewrite past the scope of this handler. + +These rewrites are often useful for a pattern like "pre-check requests", where a request is sent to another server to help make a decision on how to continue handling the current request. + +For example, the request could be sent to an authentication gateway to decide whether the request was from an authenticated user (e.g. the request has a session cookie) and should continue, or should instead be redirected to a login page. For this pattern, Caddy provides a shortcut directive `forward_auth` to skip most of the config boilerplate. + +**Transports**: + +Caddy's proxy **transport** is pluggable: + +- **transport** defines how to communicate with the backend. Default is `http`. + +The `http` transport: + +``` +transport http { + read_buffer + write_buffer + max_response_header + proxy_protocol v1|v2 + dial_timeout + dial_fallback_delay + response_header_timeout + expect_continue_timeout + resolvers + tls + tls_client_auth | + tls_insecure_skip_verify + tls_curves + tls_timeout + tls_trust_pool + tls_server_name + tls_renegotiation + tls_except_ports + keepalive [off|] + keepalive_interval + keepalive_idle_conns + keepalive_idle_conns_per_host + versions + compression off + max_conns_per_host +} +``` + +- **read_buffer** is the size of the read buffer in bytes. It accepts all formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). Default: `4KiB`. + +- **write_buffer** is the size of the write buffer in bytes. It accepts all formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). Default: `4KiB`. + +- **max_response_header** is the maximum amount of bytes to read from response headers. It accepts all formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). Default: `10MiB`. + +- **proxy_protocol** enables [PROXY protocol](https://github.com/haproxy/haproxy/blob/master/doc/proxy-protocol.txt) (popularized by HAProxy) on the connection to the upstream, prepending the real client IP data. This is best paired with the `servers > trusted_proxies` global option if Caddy is behind another proxy. Versions `v1` and `v2` are supported. This should only be used if you know the upstream server is able to parse PROXY protocol. By default, this is disabled. + +- **dial_timeout** is the maximum duration to wait when connecting to the upstream socket. Default: `3s`. + +- **dial_fallback_delay** is the maximum duration to wait before spawning an RFC 6555 Fast Fallback connection. A negative value disables this. Default: `300ms`. + +- **response_header_timeout** is the maximum duration to wait for reading response headers from the upstream. Default: No timeout. + +- **expect_continue_timeout** is the maximum duration to wait for the upstreams's first response headers after fully writing the request headers if the request has the header `Expect: 100-continue`. Default: No timeout. + +- **read_timeout** is the maximum duration to wait for the next read from the backend. Default: No timeout. + +- **write_timeout** is the maximum duration to wait for the next writes to the backend. Default: No timeout. + +- **resolvers** is a list of DNS resolvers to override system resolvers. + +- **tls** uses HTTPS with the backend. This will be enabled automatically if you specify backends using the `https://` scheme or port `:443`, or if any of the below `tls_*` options are configured. + +- **tls_client_auth** enables TLS client authentication one of two ways: (1) by specifying a domain name for which Caddy should obtain a certificate and keep it renewed, or (2) by specifying a certificate and key file to present for TLS client authentication with the backend. + +- **tls_insecure_skip_verify** turns off TLS handshake verification, making the connection insecure and vulnerable to man-in-the-middle attacks. _Do not use in production._ + +- **tls_curves** is a list of elliptic curves to support for the upstream connection. Caddy's defaults are modern and secure, so you should only need to configure this if you have specific requirements. + +- **tls_timeout** is the maximum duration to wait for the TLS handshake to complete. Default: No timeout. + +- **tls_trust_pool** configures the source of trusted certificate authorities similar to the `trust_pool` sub-directive described on the `tls` directive documentation. The list of trust pool sources available in standard Caddy installation is available [here](https://caddyserver.com/docs/caddyfile/directives/tls#trust-pool-providers). + +- **tls_server_name** sets the server name used when verifying the certificate received in the TLS handshake. By default, this will use the upstream address' host part. + + You only need to override this if your upstream address does not match the certificate the upstream is likely to use. For example if the upstream address is an IP address, then you would need to configure this to the hostname being served by the upstream server. + + A request placeholder may be used, in which case a clone of the HTTP transport config will be used on every request, which may incur a performance penalty. + +- **tls_renegotiation** sets the TLS renegotiation level. TLS renegotiation is the act of performing subsequent handshakes after the first. The level may be one of: + + - `never` (the default) disables renegotiation. + - `once` allows a remote server to request renegotiation once per connection. + - `freely` allows a remote server to repeatedly request renegotiation. +- **tls_except_ports** when TLS is enabled, if the upstream target uses one of the given ports, TLS will be disabled for those connections. This may be useful when configuring dynamic upstreams, where some upstreams expect HTTP and others expect HTTPS requests. + +- **keepalive** is either `off` or a duration value that specifies how long to keep connections open (timeout). Default: `2m`. + +- **keepalive_interval** is the duration between liveness probes. Default: `30s`. + +- **keepalive_idle_conns** defines the maximum number of connections to keep alive. Default: No limit. + +- **keepalive_idle_conns_per_host** if non-zero, controls the maximum idle (keep-alive) connections to keep per-host. Default: `32`. + +- **versions** allows customizing which versions of HTTP to support. As a special case, "h2c" is a valid value which will enable cleartext HTTP/2 connections to the upstream (however, this is a non-standard feature that does not use Go's default HTTP transport, so it is exclusive of other features; subject to change or removal). Default: `1.1 2`, or if scheme is `h2c://`, `h2c 2` + +- **compression** can be used to disable compression to the backend by setting it to `off`. + +- **max_conns_per_host** optionally limits the total number of connections per host, including connections in the dialing, active, and idle states. Default: No limit. + +**Intercepting responses**: + +The reverse proxy can be configured to intercept responses from the backend. To facilitate this, response matchers can be defined (similar to the syntax for request matchers) and the first matching `handle_response` route will be invoked. + +When a response handler is invoked, the response from the backend is not written to the client, and the configured `handle_response` route will be executed instead, and it is up to that route to write a response. If the route does _not_ write a response, then request handling will continue with any handlers that are ordered after this `reverse_proxy`. + +- **@name** is the name of a response matcher. As long as each response matcher has a unique name, multiple matchers can be defined. A response can be matched on the status code and presence or value of a response header. +- **replace_status** simply changes the status code of response when matched by the given matcher. +- **handle_response** defines the route to execute when matched by the given matcher (or, if a matcher is omitted, all responses). The first matching block will be applied. Inside a `handle_response` block, any other directives can be used. + +Additionally, inside `handle_response`, two special handler directives may be used: + +- **copy_response** copies the response body received from the backend back to the client. Optionally allows changing the status code of the response while doing so. This directive is ordered before `respond`. +- **copy_response_headers** copies the response headers from the backend to the client, optionally including _OR_ excluding a list of headers fields (cannot specify both `include` and `exclude`). This directive is ordered after `header`. + +Three placeholders will be made available within the `handle_response` routes: + +- `{rp.status_code}` The status code from the backend's response. +- `{rp.status_text}` The status text from the backend's response. +- `{rp.header.*}` The headers from the backend's response. + +Examples: + +Reverse proxy all requests to a local backend: + +``` +example.com { + reverse_proxy localhost:9005 +} +``` + +Load-balance all requests between 3 backends: + +``` +example.com { + reverse_proxy node1:80 node2:80 node3:80 +} +``` + +Same, but only requests within `/api`, and sticky by using the `cookie` policy: + +``` +example.com { + reverse_proxy /api/* node1:80 node2:80 node3:80 { + lb_policy cookie api_sticky + } +} +``` + +Using active health checks to determine which backends are healthy, and enabling retries on failed connections, holding the request until a healthy backend is found: + +``` +example.com { + reverse_proxy node1:80 node2:80 node3:80 { + health_uri /healthz + lb_try_duration 5s + } +} +``` + +Configure some transport options: + +``` +example.com { + reverse_proxy localhost:8080 { + transport http { + dial_timeout 2s + response_header_timeout 30s + } + } +} +``` + +Reverse proxy to an HTTPS upstream: + +``` +example.com { + reverse_proxy https://example.com { + header_up Host {upstream_hostport} + } +} +``` + +Reverse proxy to an HTTPS upstream, but ⚠️ disable TLS verification. This is NOT RECOMMENDED, since it disables all security checks that HTTPS offers; proxying over HTTP in private networks is preferred if possible, because it avoids the false sense of security: + +``` +example.com { + reverse_proxy 10.0.0.1:443 { + transport http { + tls_insecure_skip_verify + } + } +} +``` + +Instead you may establish trust with the upstream by explicitly trusting the upstream's certificate, and (optionally) setting TLS-SNI to match the hostname in the upstream's certificate: + +``` +example.com { + reverse_proxy 10.0.0.1:443 { + transport http { + tls_trusted_ca_certs /path/to/cert.pem + tls_server_name app.example.com + } + } +} +``` + +Strip a path prefix before proxying: + +``` +example.com { + handle_path /prefix/* { + reverse_proxy localhost:9000 + } +} +``` + +Replace a path prefix before proxying, using a `rewrite`: + +``` +example.com { + handle_path /old-prefix/* { + rewrite * /new-prefix{path} + reverse_proxy localhost:9000 + } +} +``` + +`X-Accel-Redirect` support, i.e. serving static files as requested, by intercepting the response: + +``` +example.com { + reverse_proxy localhost:8080 { + @accel header X-Accel-Redirect * + handle_response @accel { + root * /path/to/private/files + rewrite * {rp.header.X-Accel-Redirect} + method * GET + file_server + } + } +} +``` + +Custom error page for errors from upstream, by intercepting error responses by status code: + +``` +example.com { + reverse_proxy localhost:8080 { + @error status 500 503 + handle_response @error { + root * /path/to/error/pages + rewrite * /{rp.status_code}.html + file_server + } + } +} +``` + +Get backends dynamically from `A`/`AAAA` record DNS queries: + +``` +example.com { + reverse_proxy { + dynamic a example.com 9000 + } +} +``` + +Get backends dynamically from `SRV` record DNS queries: + +``` +example.com { + reverse_proxy { + dynamic srv _api._tcp.example.com + } +} +``` + +##### rewrite +Rewrites the request URI internally. + +A rewrite changes some or all of the request URI. Note that the URI does not include scheme or authority (host & port), and clients typically do not send fragments. Thus, this directive is mostly used for **path** and **query** string manipulation. + +The `rewrite` directive implies the intent to accept the request, but with modifications. + +It is mutually exclusive to other `rewrite` directives in the same block, so it is safe to define rewrites that would otherwise cascade into each other as only the first matching rewrite will be executed. + +A request matcher that matches a request before the `rewrite` might not match the same request after the `rewrite`. If you want your `rewrite` to share a route with other handlers, use the `route` or `handle` directives. + +``` +rewrite [] +``` + +- **** is the URI to rewrite the request to. Only the components of the URI (path or query string) that are specified in the rewrite will be operated on. The URI path is any substring that comes before `?`. If `?` is omitted, then the whole token is considered to be the path. + +Examples: + +Rewrite all requests to `index.html`, leaving any query string unchanged: + +``` +example.com { + rewrite * /index.html +} +``` + +Note that prior to v2.8.0, a wildcard matcher was required here because the first argument is ambiguous with a path matcher, i.e. `rewrite * /foo`, but it can now be simplified to `rewrite /foo`. + +Prefixing all requests with `/api`, preserving the rest of the URI, then reverse proxying to an app: + +``` +api.example.com { + rewrite * /api{uri} + reverse_proxy localhost:8080 +} +``` + +Replace the query string on API requests with `a=b`, leaving the path unchanged: + +``` +example.com { + rewrite * ?a=b +} +``` + +For only requests to `/api/`, preserve the existing query string and add a key-value pair: + +``` +example.com { + rewrite /api/* ?{query}&a=b +} +``` + +Change both the path and query string, preserving the original query string while adding the original path as the `p` parameter: + +``` +example.com { + rewrite * /index.php?{query}&p={path} +} +``` + +##### root +Sets the root path of the site, used by various matchers and directives that access the file system. If unset, the default site root is the current working directory. + +Specifically, this directive sets the `{http.vars.root}` placeholder. It is mutually exclusive to other `root` directives in the same block, so it is safe to define multiple roots with matchers that intersect: they will not cascade and overwrite each other. + +This directive does not automatically enable serving static files, so it is often used in conjunction with the `file_server` directive or the `php_fastcgi` directive. + +``` +root [] +``` + +- **** is the path to use for the site root. + +Prior to v2.8.0, the `` argument could be confused by the parser for a matcher token if it began with `/`, so it was necessary to specify a wildcard matcher token (`*`). + +Set the site root to `/home/bob/public_html` (assumes Caddy is running as the user `bob`): + +If you're running Caddy as a systemd service, reading files from `/home` will not work, because the `caddy` user does not have "executable" permission on the `/home` directory (necessary for traversal). It's recommended that you place your files in `/srv` or `/var/www/html` instead. + +``` +root * /home/bob/public_html +``` + +Note that prior to v2.8.0, a wildcard matcher was required here because the first argument is ambiguous with a path matcher, i.e. `root * /srv`, but it can now be simplified to `root /srv`. + +Set the site root to `public_html` (relative to current working directory) for all requests: + +``` +root public_html +``` + +Change the site root only for requests in `/foo/*`: + +``` +root /foo/* /home/user/public_html/foo +``` + +The `root` directive is commonly paired with `file_server` to serve static files and/or with `php_fastcgi` to serve a PHP site: + +``` +example.com { + root * /srv + file_server +} +``` + +##### route +Evaluates a group of directives literally and as a single unit. + +Directives contained in a route block will not be reordered internally. Only HTTP handler directives (directives which add handlers or middleware to the chain) can be used in a route block. + +This directive is a special case in that its subdirectives are also regular directives. + +``` +route [] { + +} +``` + +- **** is a list of directives or directive blocks, one per line, just like outside of a route block; except these directives will not be reordered. Only HTTP handler directives can be used. + +The `route` directive is helpful in certain advanced use cases or edge cases to take absolute control over parts of the HTTP handler chain. + +Because the order of HTTP middleware evaluation is significant, the Caddyfile will normally reorder directives after parsing to make the Caddyfile easier to use; you don't have to worry about what order you type things. + +While the built-in order is compatible with most sites, sometimes you need to take manual control over the order, either for the whole site or just a part of it. That's what the `route` directive is for. + +To illustrate, consider the case of two terminating handlers: `redir` and `file_server`. Both write the response to the client and do not call the next handler in the chain, so only one of these will be executed for a certain request. So which comes first? Normally, `redir` is executed before `file_server` because usually you would want to issue a redirect only in specific cases and serve files in the general case. + +However, there may be occasions where the second directive (`redir`) has a more specific matcher than the second (`file_server`). In other words, you want to redirect in the general case, and serve only a specific file. + +So you might try a Caddyfile like this (but this will not work as expected!): + +``` +example.com { + file_server /specific.html + redir https://anothersite.com{uri} +} +``` + +The problem is that after the directives are sorted, `redir` comes before `file_server`. + +But in this case the matcher for `redir` (an implicit `*`) is a superset of the matcher for `file_server` (`*` is a superset of `/specific.html`). + +Fortunately, the solution is easy: just wrap those two directives in a `route` block, to ensure that `file_server` is executed before `redir`: + +``` +example.com { + route { + file_server /specific.html + redir https://anothersite.com{uri} + } +} +``` + +Another way to do this is to make the two matchers mutually exclusive, but this can quickly become complex if there are more than one or two conditions. With the `route` directive, the mutual exclusivity of the two handlers is implicit because they are both terminal handlers. + +And now `file_server` will be chained in before `redir` because the order is taken literally. + +Examples: + +Proxy requests to `/api` as-is, and rewrite all other requests based on whether they match a file on disk, otherwise `/index.html`. Then that file is served. + +Since `try_files` has a higher directive order than `reverse_proxy`, then it would normally get sorted higher and run first; this would cause the API requests to all get rewritten to `/index.html` and fail to match `/api*`, so none of them would get proxied and instead would result in a `404` from `file_server`. Wrapping it all in a `route` ensures that `reverse_proxy` always runs first, before the request is rewritten. + +``` +example.com { + root * /srv + route { + reverse_proxy /api* localhost:9000 + + try_files {path} /index.html + file_server + } +} +``` + +##### templates +Executes the response body as a template document. Templates provide functional primitives for making simple dynamic pages. Features include HTTP subrequests, HTML file includes, Markdown rendering, JSON parsing, basic data structures, randomness, time, and more. + +``` +templates [] { + mime + between + root +} +``` + +- **mime** are the MIME types the templates middleware will act on; any responses that do not have a qualifying `Content-Type` will not be evaluated as templates. + + Default: `text/html text/plain`. + +- **between** are the opening and closing delimiters for template actions. You can change them if they interfere with the rest of your document. + + Default: `{{ }}`. + +- **root** is the site root, when using functions that access the file system. + + Defaults to the site root set by the [`root`](https://caddyserver.com/docs/caddyfile/directives/root) directive, or the current working directory if not set. + + +Documentation for the built-in template functions can be found in [templates module](https://caddyserver.com/docs/modules/http.handlers.templates#docs). + +Examples: + +For a complete example of a site using templates to serve markdown, take a look at the source for [this very website](https://github.com/caddyserver/website)! Specifically, take a look at the [`Caddyfile`](https://github.com/caddyserver/website/blob/master/Caddyfile) and [`src/docs/index.html`](https://github.com/caddyserver/website/blob/master/src/docs/index.html). + +Enable templates for a static site: + +``` +example.com { + root * /srv + templates + file_server +} +``` + +To serve a simple static response using a template, make sure to set `Content-Type`: + +``` +example.com { + header Content-Type text/plain + templates + respond "Current year is: {{now | date "2006"}}" +} +``` + +##### tls +Configures TLS for the site. + +**Caddy's default TLS settings are secure. Only change these settings if you have a good reason and understand the implications.** The most common use of this directive will be to specify an ACME account email address, change the ACME CA endpoint, or to provide your own certificates. + +Compatibility note: Due to its sensitive nature as a security protocol, deliberate adjustments to TLS defaults may be made in new minor or patch releases. Old or broken TLS versions, ciphers, features, etc. may be removed at any time. If your deployment is extremely sensitive to changes, you should explicitly specify those values which must remain constant, and be vigilant about upgrades. In almost every case, we recommend using the default settings. + +``` +tls [internal|] | [ ] { + protocols [] + ciphers + curves + alpn + load + ca + ca_root + key_type ed25519|p256|p384|rsa2048|rsa4096 + dns [] + propagation_timeout + propagation_delay + dns_ttl + dns_challenge_override_domain + resolvers + eab + on_demand + reuse_private_keys + client_auth { + mode [request|require|verify_if_given|require_and_verify] + trust_pool + trusted_leaf_cert + trusted_leaf_cert_file + verifier + } + issuer [] + get_certificate [] + insecure_secrets_log +} +``` + +- **internal** means to use Caddy's internal, locally-trusted CA to produce certificates for this site. To further configure the `internal` issuer, use the `issuer` subdirective. + +- **** is the email address to use for the ACME account managing the site's certificates. You may prefer to use the `email` global option instead, to configure this for all your sites at once. + +Keep in mind that Let's Encrypt may send you emails about your certificate nearing expiry, but this may be misleading because Caddy may have chosen to use a different issuer (e.g. ZeroSSL) when renewing. Check your logs and/or the certificate itself (in your browser for example) to see which issuer was used, and that its expiry is still valid; if so, you may safely ignore the email from Let's Encrypt. + +- **** and **** are the paths to the certificate and private key PEM files. Specifying just one is invalid. + +- **protocols** specifies the minimum and maximum protocol versions. DO NOT change these unless you know what you're doing. Configuring this is rarely necessary, because Caddy will always use modern defaults. + + Default min: `tls1.2`, Default max: `tls1.3` + +- **ciphers** specifies the list of cipher suite names in descending preference order. DO NOT change these unless you know what you're doing. Note that cipher suites are not customizable for TLS 1.3; and not all TLS 1.2 ciphers are enabled by default. The supported names are (in order of preference by the Go stdlib): + + - `TLS_AES_128_GCM_SHA256` + - `TLS_CHACHA20_POLY1305_SHA256` + - `TLS_AES_256_GCM_SHA384` + - `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` + - `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` + - `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` + - `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384` + - `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` + - `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` + - `TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA` + - `TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA` + - `TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA` + - `TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA` + - `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA` +- **curves** specifies the list of EC curves to support. It is recommended to not change these. Supported values are: + + - `x25519` + - `secp256r1` + - `secp384r1` + - `secp521r1` + +- **alpn** is the list of values to advertise in the ALPN extension of the TLS handshake. +- **load** specifies a list of folders from which to load PEM files that are certificate+key bundles. + +- **ca** changes the ACME CA endpoint. This is most often used to set Let's Encrypt's staging endpoint when testing, or an internal ACME server. (To change this value for the whole Caddyfile, use the `acme_ca` global option instead.) + +- **ca_root** specifies a PEM file that contains a trusted root certificate for the ACME CA endpoint, if not in the system trust store. + +- **key_type** is the type of key to use when generating CSRs. Only set this if you have a specific requirement. + +- **dns** enables the DNS challenge using the specified provider plugin, which must be plugged in from one of the [`caddy-dns`](https://github.com/caddy-dns) repositories. Each provider plugin may have their own syntax following their name; refer to their docs for details. Maintaining support for each DNS provider is a community effort. + +- **propagation_timeout** is a duration value that sets the maximum time to wait for the DNS TXT records to appear when using the DNS challenge. Set to `-1` to disable propagation checks. Default 2 minutes. + +- **propagation_delay** is a duration value that sets how long to wait before starting DNS TXT records propagation checks when using the DNS challenge. Default `0` (no wait). + +- **dns_ttl** is a duration value that sets the TTL of the `TXT` record used for the DNS challenge. Rarely needed. + +- **dns_challenge_override_domain** overrides the domain to use for the DNS challenge. This is to delegate the challenge to a different domain. + + You may want to use this if your primary domain's DNS provider does not have a [DNS plugin](https://github.com/caddy-dns) available. You can instead add a `CNAME` record with subdomain `_acme-challenge` to your primary domain, pointing to a secondary domain for which you _do_ have a plugin. This option _does not_ require special support from the plugin. + + When ACME issuers try to solve the DNS challenge for your primary domain, they will then follow the `CNAME` to your secondary domain to find the `TXT` record. + + **Note:** Use full canonical name from the CNAME record as value here - `_acme-challenge` subdomain won't be prepended automatically. + +- **resolvers** customizes the DNS resolvers used when performing the DNS challenge; these take precedence over system resolvers or any default ones. If set here, the resolvers will propagate to all configured certificate issuers. + + This is typically a list of IP addresses. For example, to use Google Public DNS: + + ``` + resolvers 8.8.8.8 8.8.4.4 + ``` + +- **eab** configures ACME external account binding (EAB) for this site, using the key ID and MAC key provided by your CA. + +- **on_demand** enables [On-Demand TLS](https://caddyserver.com/docs/automatic-https#on-demand-tls) for the hostnames given in the site block's address(es). **Security warning:** Doing so in production is insecure unless you also configure the `on_demand_tls` global option to mitigate abuse. + +- **reuse_private_keys** enables reuse of private keys when renewing certificates. By default, a new key is created for every new certificate to mitigate pinning and reduce the scope of key compromise. Key pinning is against industry best practices. This option is not recommended unless you have a specific reason to use it; this may be subject to removal in a future version. + +- **client_auth** enables and configures TLS client authentication: + + - **mode** is the mode for authenticating the client. Allowed values are: + + | Mode | Description | + | ------------------ | --------------------------------------------------------------------------------------- | + | request | Ask clients for a certificate, but allow even if there isn't one; do not verify it | + | require | Require clients to present a certificate, but do not verify it | + | verify_if_given | Ask clients for a certificate; allow even if there isn't one, but verify it if there is | + | require_and_verify | Require clients to present a valid certificate that is verified | + + Default: `require_and_verify` if any `trusted_ca_cert` or `trusted_leaf_cert` are provided; otherwise, `require`. + + - **trust_pool** configures the source of certificate authorities (CA) providing certificates against which to validate client certificates. + + The certificate authority used providing the pool of trusted certificates and the configuration within the segment depends on the configured source of trust pool module. The standard modules available in Caddy are [listed here](https://caddyserver.com/docs/caddyfile/directives/tls#trust-pool-providers). The full list of modules, including 3rd-party, is listed in the `trust_pool` JSON documentation. + + - **trusted_leaf_cert** is a base64 DER-encoded client leaf certificate to accept. + + - **trusted_leaf_cert_file** is a path to a PEM CA certificate file against which to validate client certificates. + + Multiple `trusted_*` directives may be used to specify multiple CA or leaf certificates. Client certificates which are not listed as one of the leaf certificates or signed by any of the specified CAs will be rejected according to the **mode**. + + - **verifier** enables the use of a custom client certificate verifier module. These can perform custom client authentication checks, such as ensuring the certificate is not revoked. + +- **issuer** configures a custom certificate issuer, or a source from which to obtain certificates. + + Which issuer is used and the options that follow in this segment depend on the [issuer modules](https://caddyserver.com/docs/caddyfile/directives/tls#issuers) that are available. Some of the other subdirectives such as `ca` and `dns` are actually shortcuts for configuring the `acme` issuer (and this subdirective was added later), so specifying this directive and some of the others is confusing and thus prohibited. + + This subdirective can be specified multiple times to configure multiple, redundant issuers; if one fails to issue a cert, the next one will be tried. + +- **get_certificate** enables getting certificates from a [manager module](https://caddyserver.com/docs/caddyfile/directives/tls#certificate-managers) at handshake-time. + +- **insecure_secrets_log** enables logging of TLS secrets to a file. This is also known as `SSLKEYLOGFILE`. Uses NSS key log format, which can then be parsed by Wireshark or other tools. ⚠️ **Security Warning:** This is insecure as it allows other programs or tools to decrypt TLS connections, and therefore completely compromises security. However, this capability can be useful for debugging and troubleshooting. + +Examples: + +Use a custom certificate and key. The certificate should have [SANs](https://en.wikipedia.org/wiki/Subject_Alternative_Name) that match the site address: + +``` +example.com { + tls cert.pem key.pem +} +``` + +Use locally-trusted certificates for all hosts on the current site block, rather than public certificates via ACME / Let's Encrypt (useful in dev environments): + +``` +example.com { + tls internal +} +``` + +Use locally-trusted certificates, but managed [On-Demand](https://caddyserver.com/docs/automatic-https#on-demand-tls) instead of in the background. This allows you to point any domain at your Caddy instance and have it automatically provision a certificate for you. This SHOULD NOT be used if your Caddy instance is publicly accessible, since an attacker could use it to exhaust your server's resources: + +``` +https:// { + tls internal { + on_demand + } +} +``` + +Use custom options for the internal CA (cannot use the `tls internal` shortcut): + +``` +example.com { + tls { + issuer internal { + ca foo + } + } +} +``` + +Specify an email address for your ACME account (but if only one email is used for all sites, we recommend the `email` global option instead): + +``` +example.com { + tls your@email.com +} +``` + +Enable the DNS challenge for a domain managed on Cloudflare with account credentials in an environment variable. This unlocks wildcard certificate support, which requires DNS validation: + +``` +*.example.com { + tls { + dns cloudflare {env.CLOUDFLARE_API_TOKEN} + } +} +``` + +Get the certificate chain via HTTP, instead of having Caddy manage it. Note that `get_certificate` implies `on_demand` is enabled, fetching certificates using a module instead of triggering ACME issuance: + +``` +https:// { + tls { + get_certificate http http://localhost:9007/certs + } +} +``` + +Enable TLS Client Authentication and require clients to present a valid certificate that is verified against all the provided CA's via `trusted_ca_cert_file` + +``` +example.com { + tls { + client_auth { + mode require_and_verify + trusted_ca_cert_file ../caddy.ca.cer + trusted_ca_cert_file ../root.ca.cer + } + } +} +``` + +##### try_files +Rewrites the request URI path to the first of the listed files which exists in the site root. If no files match, no rewrite is performed. + +``` +try_files { + policy first_exist|smallest_size|largest_size|most_recently_modified +} +``` + +- **** is the list of files to try. The URI path will be rewritten to the first one that exists. + + To match directories, append a trailing forward slash `/` to the path. All file paths are relative to the site [root](https://caddyserver.com/docs/caddyfile/directives/root), and [glob patterns](https://pkg.go.dev/path/filepath#Match) will be expanded. + + Each argument may also contain a query string, in which case the query string will also be changed if it matches that particular file. + + If the `try_policy` is `first_exist` (the default), then the last item in the list may be a number prefixed by `=` (e.g. `=404`), which as a fallback, will emit an error with that code; the error can be caught and handled with [`handle_errors`](https://caddyserver.com/docs/caddyfile/directives/handle_errors). + +- **policy** is the policy for choosing the file among the list of files. + + Default: `first_exist` + + +Examples: + +If the request does not match any static files, rewrite to your PHP index/router entrypoint: + +``` +try_files {path} /index.php +``` + +Same, but adding the original path to the query string (required by some legacy PHP apps): + +``` +try_files {path} /index.php?{query}&p={path} +``` + +Same, but also match directories: + +``` +try_files {path} {path}/ /index.php?{query}&p={path} +``` + +Attempt to rewrite to a file or directory if it exists, otherwise emit a 404 error (which can be caught and handled with `handle_errors`: + +``` +try_files {path} {path}/ =404 +``` + +Choose the most recently deployed version of a static file (e.g. serve `index.be331df.html` when `index.html` is requested): + +``` +try_files {file.base}.*.{file.ext} { + policy most_recently_modified +} +``` + +##### uri +Manipulates a request's URI. It can strip path prefix/suffix or replace substrings on the whole URI. + +This directive is distinct from `rewrite` in that `uri` _differentiably_ changes the URI, rather than resetting it to something completely different as `rewrite` does. While `rewrite` is treated specially as an internal redirect, `uri` is just another middleware. + +Multiple different operations are supported: + +``` +uri [] strip_prefix +uri [] strip_suffix +uri [] replace [] +uri [] path_regexp +``` + +- The first (non-matcher) argument specifies the operation: + + - **strip_prefix** strips the prefix from the path. + + - **strip_suffix** strips the suffix from the path. + + - **replace** performs a substring replacement across the whole URI. + + - **path_regexp** performs a regular expression replacement on the path portion of the URI. + +- **** is the prefix, suffix, or search string/regular expression. If a prefix, the leading forward slash may be omitted, since paths always start with a forward slash. + +- **** is the replacement string (only valid with `replace` and `path_regexp`). Supports using capture groups with `$name` or `${name}` syntax, or with a number for the index, such as `$1`. See the [Go documentation](https://golang.org/pkg/regexp/#Regexp.Expand) for details. + +- **** is an optional limit to the maximum number of replacements (only valid with `replace`). + + +URI mutations occur on the normalized or unescaped form of the URI. However, escape sequences can be used in the prefix or suffix patterns to match only those literal escapes at those positions in the request path. For example, `uri strip_prefix /a/b` will rewrite both `/a/b/c` and `/a%2Fb/c` to `/c`; and `uri strip_prefix /a%2Fb` will rewrite `/a%2Fb/c` to `/c`, but won't match `/a/b/c`. + +The URI path is cleaned of directory traversal dots before modifications. Additionally, multiple slashes (such as `//`) are merged unless the `` contains multiple slashes too. + +Examples + +Strip `/api` from the beginning of all request paths: + +``` +uri strip_prefix /api +``` + +Strip `.php` from the end of all request paths: + +``` +uri strip_suffix .php +``` + +Replace "/docs/" with "/v1/docs/" in any request URI: + +``` +uri replace /docs/ /v1/docs/ +``` + +Collapse all repeated slashes in the request path (but not the request query) to a single slash: + +``` +uri path_regexp /{2,} / +``` + +##### vars +Sets one or more variables to a particular value, to be used later in the request handling chain. + +The primary way to access variables is with placeholders, which have the form `{vars.variable_name}`, or with the `vars` and `vars_regexp` request matchers. You may use variables with the `templates` directive using the `placeholder` function, for example: `` + +As a special case, it's possible to override the variable named `http.auth.user.id`, which is stored in the replacer, to update the `user_id` field in access logs. + +``` +vars [] [ ] { + + ... +} +``` + +- **** is the variable name to set. + +- **** is the value of the variable. + + The value will be type converted if possible; `true` and `false` will be converted to boolean types, and numeric values will be converted to integer or float accordingly. To avoid this conversion, you may wrap the output with quotes and they will stay strings. + + +Examples + +To set a single variable, the value being conditional based on the request path, then responding with the value: + +``` +example.com { + vars /foo* isFoo "yep" + vars isFoo "nope" + + respond {vars.isFoo} +} +``` + +To set multiple variables, each converted to the appropriate scalar type: + +``` +vars { + # boolean + abc true + + # integer + def 1 + + # float + ghi 2.3 + + # string + jkl "example" +} +``` ## Docker-Compose ```yml