# Caddy
> This tutorial will show you how to use Caddy's [admin API](/docs/api), which makes it possible to automate in a programmable fashion.
---
---
title: "API Tutorial"
---
# API Tutorial
This tutorial will show you how to use Caddy's [admin API](/docs/api), which makes it possible to automate in a programmable fashion.
**Objectives:**
- 🔲 Run the daemon
- 🔲 Give Caddy a config
- 🔲 Test config
- 🔲 Replace active config
- 🔲 Traverse config
- 🔲 Use `@id` tags
**Prerequisites:**
- Basic terminal / command line skills
- Basic JSON experience
- `caddy` and `curl` in your PATH
---
To start the Caddy daemon, use the `run` subcommand:
caddy run
This blocks forever, but what is it doing? At the moment... nothing. By default, Caddy's configuration ("config") is blank. We can verify this using the [admin API](/docs/api) in another terminal:
curl localhost:2019/config/
We can make Caddy useful by giving it a config. One way to do this is by making a POST request to the [/load](/docs/api#post-load) endpoint. Just like any HTTP request, there are many ways to do this, but in this tutorial we'll use `curl`.
## Your first config
To prepare our request, we need to make a config. Caddy's configuration is simply a [JSON document](/docs/json/) (or [anything that converts to JSON](/docs/config-adapters)).
Save this to a JSON file:
```json
{
"apps": {
"http": {
"servers": {
"example": {
"listen": [":2015"],
"routes": [
{
"handle": [{
"handler": "static_response",
"body": "Hello, world!"
}]
}
]
}
}
}
}
}
```
Then upload it:
We can verify that Caddy applied our new config with another GET request:
curl localhost:2019/config/
Test that it works by going to [localhost:2015](http://localhost:2015) in your browser or using `curl`:
curl localhost:2015
Hello, world!
If you see _Hello, world!_, then congrats -- it's working! It's always a good idea to make sure your config works as you expect, especially before deploying into production.
Let's change our welcome message from "Hello world!" to something a little more motivational: "I can do hard things." Make this change in your config file, so that the handler object now looks like this:
```json
{
"handler": "static_response",
"body": "I can do hard things."
}
```
Save the config file, then update Caddy's active configuration by running the same POST request again:
For good measure, verify that the config was updated:
curl localhost:2019/config/
Test it by refreshing the page in your browser (or running `curl` again), and you will see an inspirational message!
## Config traversal
Instead of uploading the entire config file for a small change, let's use a powerful feature of Caddy's API to make the change without ever touching our config file.
Using the request URI's path, we can traverse into the config structure and update only the message string (be sure to scroll right if clipped):
You should see:
```json
[{"handle":[{"body":"Work smarter, not harder.","handler":"static_response"}]}]
```
**Important note:** This should be obvious, but once you use the API to make a change that is not in your original config file, your config file becomes obsolete. There are a few ways to handle this:
- Use the `--resume` of the [caddy run](/docs/command-line#caddy-run) command to use the last active config.
- Don't mix the use of config files with changes via the API; have one source of truth.
- [Export Caddy's new configuration](/docs/api#get-configpath) with a subsequent GET request (less recommended than the first two options).
## Using `@id` in JSON
Config traversal is certainly useful, but the paths are little long, don't you think?
We can give our handler object an [`@id` tag](/docs/api#using-id-in-json) to make it easier to access:
This adds a property to our handler object: `"@id": "msg"`, so it now looks like this:
```json
{
"@id": "msg",
"body": "Work smarter, not harder.",
"handler": "static_response"
}
```
We can then access it directly:
curl localhost:2019/id/msg
And now we can change the message with a shorter path:
---
---
title: "API"
---
# API
Caddy is configured through an administration endpoint which can be accessed via HTTP using a [REST ](https://en.wikipedia.org/wiki/Representational_state_transfer) API. You can [configure this endpoint](/docs/json/admin/) in your Caddy config.
**Default address: `localhost:2019`**
The default address can be changed by setting the `CADDY_ADMIN` environment variable. Some installation methods may set this to something different. The address in the Caddy config always takes precedence over the default.
The latest configuration will be saved to disk after any changes (unless [disabled](/docs/json/admin/config/)). You can resume the last working config after a restart with [`caddy run --resume`](/docs/command-line#caddy-run), which guarantees config durability in the event of a power cycle or similar.
To get started with the API, try our [API tutorial](/docs/api-tutorial) or, if you only have a minute, our [API quick-start guide](/docs/quick-starts/api).
---
- **[POST /load](#post-load)**
Sets or replaces the active configuration
- **[POST /stop](#post-stop)**
Stops the active configuration and exits the process
- **[GET /config/[path]](#get-configpath)**
Exports the config at the named path
- **[POST /config/[path]](#post-configpath)**
Sets or replaces object; appends to array
- **[PUT /config/[path]](#put-configpath)**
Creates new object; inserts into array
- **[PATCH /config/[path]](#patch-configpath)**
Replaces an existing object or array element
- **[DELETE /config/[path]](#delete-configpath)**
Deletes the value at the named path
- **[Using `@id` in JSON](#using-id-in-json)**
Easily traverse into the config structure
- **[Concurrent config changes](#concurrent-config-changes)**
Avoid collisions when making unsynchronized changes to config
- **[POST /adapt](#post-adapt)**
Adapts a configuration to JSON without running it
- **[GET /pki/ca/<id>](#get-pkicaltidgt)**
Returns information about a particular [PKI app](/docs/json/apps/pki/) CA
- **[GET /pki/ca/<id>/certificates](#get-pkicaltidgtcertificates)**
Returns the certificate chain of a particular [PKI app](/docs/json/apps/pki/) CA
- **[GET /reverse_proxy/upstreams](#get-reverse-proxyupstreams)**
Returns the current status of the configured proxy upstreams
## POST /load
Sets Caddy's configuration, overriding any previous configuration. It blocks until the reload completes or fails. Configuration changes are lightweight, efficient, and incur zero downtime. If the new config fails for any reason, the old config is rolled back into place without downtime.
This endpoint supports different config formats using config adapters. The request's Content-Type header indicates the config format used in the request body. Usually, this should be `application/json` which represents Caddy's native config format. For another config format, specify the appropriate Content-Type so that the value after the forward slash / is the name of the config adapter to use. For example, when submitting a Caddyfile, use a value like `text/caddyfile`; or for JSON 5, use a value such as `application/json5`; etc.
If the new config is the same as the current one, no reload will occur. To force a reload, set `Cache-Control: must-revalidate` in the request headers.
### Examples
Set a new active configuration:
## POST /stop
Gracefully shuts down the server and exits the process. To only stop the running configuration without exiting the process, use [DELETE /config/](#delete-configpath).
### Example
Stop the process:
curl -X POST "http://localhost:2019/stop"
## GET /config/[path]
Exports Caddy's current configuration at the named path. Returns a JSON body.
### Examples
Export entire config and pretty-print it:
## POST /config/[path]
Changes Caddy's configuration at the named path to the JSON body of the request. If the destination value is an array, POST appends; if an object, it creates or replaces.
As a special case, many items can be added to an array if:
1. the path ends in `/...`
2. the element of the path before `/...` refers to an array
3. the payload is an array
In this case, the elements in the payload's array will be expanded, and each one will be appended to the destination array. In Go terms, this would have the same effect as:
```go
baseSlice = append(baseSlice, newElems...)
```
### Examples
Add a listener address:
## PUT /config/[path]
Changes Caddy's configuration at the named path to the JSON body of the request. If the destination value is a position (index) in an array, PUT inserts; if an object, it strictly creates a new value.
### Example
Add a listener address in the first slot:
## PATCH /config/[path]
Changes Caddy's configuration at the named path to the JSON body of the request. PATCH strictly replaces an existing value or array element.
### Example
Replace the listener addresses:
## DELETE /config/[path]
Removes Caddy's configuration at the named path. DELETE deletes the target value.
### Examples
To unload the entire current configuration but leave the process running:
## Using `@id` in JSON
You can embed IDs in your JSON document for easier direct access to those parts of the JSON.
Simply add a field called `"@id"` to an object and give it a unique name. For example, if you had a reverse proxy handler that you wanted to access frequently:
```json
{
"@id": "my_proxy",
"handler": "reverse_proxy"
}
```
To use it, simply make a request to the `/id/` API endpoint in the same way you would to the corresponding `/config/` endpoint, but without the whole path. The ID takes the request directly into that scope of the config for you.
For example, to access the upstreams of the reverse proxy without an ID, the path would be something like
```
/config/apps/http/servers/myserver/routes/1/handle/0/upstreams
```
but with an ID, the path becomes
```
/id/my_proxy/upstreams
```
which is much easier to remember and write by hand.
## Concurrent config changes
Caddy's config API provides [ACID guarantees ](https://en.wikipedia.org/wiki/ACID) for individual requests, but changes that involve more than a single request are subject to collisions or data loss if not properly synchronized.
For example, two clients may `GET /config/foo` at the same time, make an edit within that scope (config path), then call `POST|PUT|PATCH|DELETE /config/foo/...` at the same time to apply their changes, resulting in a collision: either one will overwrite the other, or the second might leave the config in an unintended state since it was applied to a different version of the config than it was prepared against. This is because the changes are not aware of each other.
Caddy's API does not support transactions spanning multiple requests, and HTTP is a stateless protocol. However, you can use the `Etag` and `If-Match` headers to detect and prevent collisions for any and all changes as a kind of optimistic concurrency control. This is useful if there is any chance that you are using Caddy's `/config/...` endpoints concurrently without synchronization. All responses to `GET /config/...` requests have an HTTP header called `Etag` that contains the path and a hash of the contents in that scope (e.g. `Etag: "/config/apps/http/servers 65760b8e"`). Simply set the `If-Match` header on a mutative request to that of an Etag header from a previous `GET` request.
The basic algorithm for this is as follows:
1. Perform a `GET` request to any scope `S` within the config. Hold onto the `Etag` header of the response.
2. Make your desired change on the returned config.
3. Perform a `POST|PUT|PATCH|DELETE` request within scope `S`, setting the `If-Match` request header to the stored `Etag` value.
4. If the response is HTTP 412 (Precondition Failed), repeat from step 1, or give up after too many attempts.
This algorithm safely allows multiple, overlapping changes to Caddy's configuration without explicit synchronization. It is designed so that simultaneous changes to different parts of the config don't require a retry: only changes that overlap the same scope of the config can possibly cause a collision and thus require a retry.
## POST /adapt
Adapts a configuration to Caddy JSON without loading or running it. If successful, the resulting JSON document is returned in the response body.
The Content-Type header is used to specify the configuration format in the same way that [/load](#post-load) works. For example, to adapt a Caddyfile, set `Content-Type: text/caddyfile`.
This endpoint will adapt any configuration format as long as the associated [config adapter](/docs/config-adapters) is plugged in to your Caddy build.
### Examples
Adapt a Caddyfile to JSON:
## GET /pki/ca/<id>
Returns information about a particular [PKI app](/docs/json/apps/pki/) CA by its ID. If the requested CA ID is the default (`local`), then the CA will be provisioned if it has not already been. Other CA IDs will return an error if they have not been previously provisioned.
## GET /pki/ca/<id>/certificates
Returns the certificate chain of a particular [PKI app](/docs/json/apps/pki/) CA by its ID. If the requested CA ID is the default (`local`), then the CA will be provisioned if it has not already been. Other CA IDs will return an error if they have not been previously provisioned.
This endpoint is used internally by the [`caddy trust`](/docs/command-line#caddy-trust) command to allow installing the CA's root certificate to your system's trust store.
Each entry in the JSON array is a configured [upstream](/docs/json/apps/http/servers/routes/handle/reverse_proxy/upstreams/) stored in the global upstream pool.
- **address** is the dial address of the upstream.
- **num_requests** is the amount of active requests currently being handled by the upstream.
- **fails** the current number of failed requests remembered, as configured by passive health checks.
If your goal is to determine a backend's availability, you will need to cross-check relevant properties of the upstream against the handler configuration you are utilizing. For example, if you've enabled [passive health checks](/docs/json/apps/http/servers/routes/handle/reverse_proxy/health_checks/passive/) for your proxies, then you need to also take into consideration the `fails` and `num_requests` values to determine if an upstream is considered available: check that the `fails` amount is less than your configured maximum amount of failures for your proxy (i.e. [`max_fails`](/docs/json/apps/http/servers/routes/handle/reverse_proxy/health_checks/passive/max_fails/)), and that `num_requests` is less than or equal to your configured amount of maximum requests per upstream (i.e. [`unhealthy_request_count`](/docs/json/apps/http/servers/routes/handle/reverse_proxy/health_checks/passive/unhealthy_request_count/) for the whole proxy, or [`max_requests`](/docs/json/apps/http/servers/routes/handle/reverse_proxy/upstreams/max_requests/) for individual upstreams).
---
---
title: Architecture
---
Architecture
============
Caddy is a single, self-contained, static binary with zero external dependencies because it's written in Go. These values comprise important parts of the project's vision because they simplify deployment and reduce tedious troubleshooting in production environments.
If there's no dynamic linking, then how can it be extended? Caddy sports a novel plugin architecture that expands its capabilities far beyond that of any other web server, even those with external (dynamically-linked) dependencies.
Our philosophy of "fewer moving parts" ultimately results in more reliable, more manageable, less expensive sites—especially at scale. This semi-technical document describes how we achieve that goal through software engineering.
## Overview
Caddy consists of a command, core library, and modules.
The **command** provides the [command line interface](/docs/command-line) you are hopefully familiar with. It's how you launch the process from your operating system. The amount of code and logic here is fairly minimal, and has only what is needed to bootstrap the core in the user's desired way. We intentionally avoid using flags and environment variables for configuration except as they pertain to bootstrapping config.
The **[core library](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc)**, or "core" of Caddy, primarily manages configuration. It can [`Run()`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Run) a new configuration or [`Stop()`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Stop) a running config. It also provides various utilities, types, and values for modules to use.
**Modules** do everything else. Many modules come built into Caddy, which are called the _standard modules_. These are determined to be the most useful to the most users.
## Caddy core
At its core, Caddy merely loads an initial configuration ("config") or, if there isn't one, opens a socket to accept new configuration later on.
A [Caddy configuration](/docs/json/) is a JSON document, with some fields at its top level:
```json
{
"admin": {},
"logging": {},
"apps": {•••},
...
}
```
The core of Caddy knows how to work with some of these fields natively:
- [`admin`](/docs/json/admin/) so it can set up the [admin API](/docs/api) and manage the process
- [`logging`](/docs/json/logging/) so it can [emit logs](/docs/logging)
But other top-level fields (like [`apps`](/docs/json/apps/)) are opaque to the core of Caddy. In fact, all Caddy knows to do with the bytes in `apps` is deserialize them into an interface type that it can call two methods on:
1. `Start()`
2. `Stop()`
... and that's it. It calls `Start()` on each app when a config is loaded, and `Stop()` on each app when a config is unloaded.
When an app module is started, it initiates the app's module lifecycle.
## Module lifecycle
There are two kinds of modules: _host modules_ and _guest modules_.
**Host modules** (or "parent" modules) are those that load other modules.
**Guest modules** (or "child" modules) are those that get loaded. All modules are guest modules -- even app modules.
Modules get loaded, are provisioned and validated, get used, then are cleaned up, in this sequence:
1. Loaded
2. Provisioned and validated
3. Used
4. Cleaned up
Caddy kicks off the module lifecycle when a config is loaded first by initializing all the configured app modules. From there, it's turtles all the way down as each app module takes it the rest of the way.
### Load phase
Loading a module involves deserializing its JSON bytes into a typed value in memory. That's... basically it. It's just decoding JSON into a value.
### Provision phase
This phase is where most of the setup work goes. All modules get a chance to provision themselves after being loaded.
Since any properties from the JSON encoding will already have been decoded, only additional setup needs to take place here. The most common task during provisioning is setting up guest modules. In other words, provisioning a host module also results in provisioning its guest modules, all the way down.
You can get a sense for this by [traversing Caddy's JSON structure in our docs](/docs/json/). Anywhere you see `{•••}` is where guest modules may be used; and as you click into one, you can continue exploring all the way down until there are no more guest modules.
Other common provisioning tasks are setting up internal values that will be used during the module's lifetime, or standardizing inputs. For example, the [`http.matchers.remote_ip`](/docs/modules/http.matchers.remote_ip) module uses the provisioning phase to parse CIDR values out of the string inputs it received from the JSON. That way, it doesn't have to do this during every HTTP request, and is more efficient as a result.
Validation also can take place in the provision phase. If a module's resulting config is invalid, an error can be returned here which aborts the entire config load process.
### Use phase
Once a guest module is provisioned and validated, it can be used by its host module. What exactly this means is up to each host module.
Each module has an ID, which consists of a namespace and a name in that namespace. For example, [`http.handlers.reverse_proxy`](/docs/modules/http.handlers.reverse_proxy) is an HTTP handler because it is in the `http.handlers` namespace, and its name is `reverse_proxy`. All modules in the `http.handlers` namespace satisfy the same interface, known to the host module. Thus, the `http` app knows how to load and use these kinds of modules.
### Cleanup phase
When it is time for a config to be stopped, all modules get unloaded. If a module allocated any resources that should be freed, it has an opportunity to do so in the cleanup phase.
## Plugging in
A module -- or any Caddy plugin -- gets "plugged in" to Caddy by adding an `import` for the module's package. By importing the package, [the module registers itself](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#RegisterModule) with the Caddy core, so when the Caddy process starts, it knows each module by name. It can even associate between module values and names, and vice-versa.
## Managing configuration
Changing a running server's active configuration (often called a "reload") can be tricky with the high levels of concurrency and thousands of parameters that servers require. Caddy solves this problem elegantly using a design that has many benefits:
- No interruption to running services
- Granular config changes are possible
- Only one lock required (in the background)
- All reloads are atomic, consistent, isolated, and mostly durable ("ACID")
- Minimal global state
You can [watch a video about the design of Caddy 2 here](https://www.youtube.com/watch?v=EhJO8giOqQs).
A config reload works by provisioning the new modules, and if all succeed, the old ones are cleaned up. For a brief period, two configs are operational at the same time.
Each configuration is associated with a [context](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Context) which holds all the module state, so most state never escapes the scope of a config. This is good news for correctness, performance, and simplicity!
However, sometimes truly global state is necessary. For example, the reverse proxy may keep track of the health of its upstreams; since there is only one of each upstream globally, it would be bad if it forgot about them every time a minor config change was made. Fortunately, Caddy [provides facilities](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#UsagePool) similar to a language runtime's garbage collector to keep global state tidy.
One obvious approach to on-line config updates is to synchronize access to every single config parameter, even in hot paths. This is unbelievably bad in terms of performance and complexity—especially at scale—so Caddy does not use this approach.
Instead, configs are treated as immutable, atomic units: either the whole thing is replaced, or nothing gets changed. The [admin API endpoints](/docs/api)—which permit granular changes by traversing into the structure—mutate only an in-memory representation of the config, from which a whole new config document is generated and loaded. This approach has vast benefits in terms of simplicity, performance, and consistency. Since there is only one lock, it is easy for Caddy to process rapid reloads.
---
---
title: "Automatic HTTPS"
---
# Automatic HTTPS
**Caddy is the first and only web server to use HTTPS automatically _and by default_.**
Automatic HTTPS provisions TLS certificates for all your sites and keeps them renewed. It also redirects HTTP to HTTPS for you! Caddy uses safe and modern defaults -- no downtime, extra configuration, or separate tooling is required.
Here's a 28-second video showing how it works:
**Menu:**
- [Overview](#overview)
- [Activation](#activation)
- [Effects](#effects)
- [Hostname requirements](#hostname-requirements)
- [Local HTTPS](#local-https)
- [Testing](#testing)
- [ACME Challenges](#acme-challenges)
- [On-Demand TLS](#on-demand-tls)
- [Errors](#errors)
- [Storage](#storage)
- [Wildcard certificates](#wildcard-certificates)
- [Encrypted ClientHello (ECH)](#encrypted-clienthello-ech)
## Overview
**By default, Caddy serves all sites over HTTPS.**
- Caddy serves IP addresses and local/internal hostnames over HTTPS using self-signed certificates that are automatically trusted locally (if permitted).
- Examples: `localhost`, `127.0.0.1`
- Caddy serves public DNS names over HTTPS using certificates from a public ACME CA such as [Let's Encrypt ](https://letsencrypt.org) or [ZeroSSL ](https://zerossl.com).
- Examples: `example.com`, `sub.example.com`, `*.example.com`
Caddy keeps all managed certificates renewed and redirects HTTP (default port `80`) to HTTPS (default port `443`) automatically.
**For local HTTPS:**
- Caddy may prompt for a password to install its unique root certificate into your trust store. This happens only once per root; and you can remove it at any time.
- Any client accessing the site without trusting Caddy's root CA certificate will show security errors.
**For public domain names:**
- If your domain's A/AAAA records point to your server,
- ports `80` and `443` are open externally,
- Caddy can bind to those ports (_or_ those ports are forwarded to Caddy),
- your [data directory](/docs/conventions#data-directory) is writeable and persistent,
- and your domain name appears somewhere relevant in the config,
then sites will be served over HTTPS automatically. You won't have to do anything else about it. It just works!
Because HTTPS utilizes a shared, public infrastructure, you as the server admin should understand the rest of the information on this page so that you can avoid unnecessary problems, troubleshoot them when they occur, and properly configure advanced deployments.
## Activation
Caddy implicitly activates automatic HTTPS when it knows a domain name (i.e. hostname) or IP address it is serving. There are various ways to tell Caddy your domain/IP, depending on how you run or configure Caddy:
- A [site address](/docs/caddyfile/concepts#addresses) in the [Caddyfile](/docs/caddyfile)
- A [host matcher](/docs/json/apps/http/servers/routes/match/host/) at the top-level in the [JSON routes](/docs/modules/http#servers/routes)
- Command line flags like [`--domain`](/docs/command-line#caddy-file-server) or [`--from`](/docs/command-line#caddy-reverse-proxy)
- The [automate](/docs/json/apps/tls/certificates/automate/) certificate loader
Any of the following will prevent automatic HTTPS from being activated, either in whole or in part:
- Explicitly disabling it [via JSON](/docs/json/apps/http/servers/automatic_https/) or [via Caddyfile](/docs/caddyfile/options#auto-https)
- Not providing any hostnames or IP addresses in the config
- Listening exclusively on the HTTP port
- Prefixing the [site address](/docs/caddyfile/concepts#addresses) with `http://` in the Caddyfile
- Manually loading certificates (unless [`ignore_loaded_certificates`](/docs/json/apps/http/servers/automatic_https/ignore_loaded_certificates/) is set)
**Special cases:**
- Domains ending in `.ts.net` will not be managed by Caddy. Instead, Caddy will automatically attempt to get these certificates at handshake-time from the locally-running [Tailscale ](https://tailscale.com) instance. This requires that [HTTPS is enabled in your Tailscale account ](https://tailscale.com/kb/1153/enabling-https/) and the Caddy process must either be running as root, or you must configure `tailscaled` to give your Caddy user [permission to fetch certificates](https://github.com/caddyserver/caddy/pull/4541#issuecomment-1021568348).
## Effects
When automatic HTTPS is activated, the following occurs:
- Certificates are obtained and renewed for [all qualifying domain names](#hostname-requirements)
- HTTP is redirected to HTTPS (this uses [HTTP port](/docs/modules/http#http_port) `80`)
Automatic HTTPS never overrides explicit configuration, it only augments it.
If you already have a [server](/docs/json/apps/http/servers/) listening on the HTTP port, the HTTP->HTTPS redirect routes will be inserted after your routes with a host matcher, but before a user-defined catch-all route.
You can [customize or disable automatic HTTPS](/docs/json/apps/http/servers/automatic_https/) if necessary; for example, you can skip certain domain names or disable redirects (for Caddyfile, do this with [global options](/docs/caddyfile/options)).
## Hostname requirements
All hostnames (domain names) qualify for fully-managed certificates if they:
- are non-empty
- consist only of alphanumerics, hyphens, dots, and wildcard (`*`)
- do not start or end with a dot ([RFC 1034](https://tools.ietf.org/html/rfc1034#section-3.5))
In addition, hostnames qualify for publicly-trusted certificates if they:
- are not localhost (including `.localhost`, `.local`, `.internal` and `.home.arpa` TLDs)
- are not an IP address
- have only a single wildcard `*` as the left-most label
## Local HTTPS
Caddy uses HTTPS automatically for all sites with a host (domain, IP, or hostname) specified, including internal and local hosts. Some hosts are either not public (e.g. `127.0.0.1`, `localhost`) or do not generally qualify for publicly-trusted certificates (e.g. IP addresses -- you can get certificates for them, but only from some CAs). These are still served over HTTPS unless disabled.
To serve non-public sites over HTTPS, Caddy generates its own certificate authority (CA) and uses it to sign certificates. The trust chain consists of a root and intermediate certificate. Leaf certificates are signed by the intermediate. They are stored in [Caddy's data directory](/docs/conventions#data-directory) at `pki/authorities/local`.
Caddy's local CA is powered by [Smallstep libraries ](https://smallstep.com/certificates/).
Local HTTPS does not use ACME nor does it perform any DNS validation. It works only on the local machine and is trusted only where the CA's root certificate is installed.
### CA Root
The root's private key is uniquely generated using a cryptographically-secure pseudorandom source and persisted to storage with limited permissions. It is loaded into memory only to perform signing tasks, after which it leaves scope to be garbage-collected.
Although Caddy can be configured to sign with the root directly (to support non-compliant clients), this is disabled by default, and the root key is only used to sign intermediates.
The first time a root key is used, Caddy will try to install it into the system's local trust store(s). If it does not have permission to do so, it will prompt for a password. This behavior can be disabled with [`skip_install_trust` in a caddyfile](/docs/caddyfile/options#skip-install-trust) or [`"install_trust": false` in a json config](/docs/json/apps/pki/certificate_authorities/install_trust/). If this fails due to being run as an unprivileged user, you may run [`caddy trust`](/docs/command-line#caddy-trust) to retry installation as a privileged user.
After Caddy's root CA is installed, you will see it in your local trust store as "Caddy Local Authority" (unless you've configured a different name). You can uninstall it any time if you wish (the [`caddy untrust`](/docs/command-line#caddy-untrust) command makes this easy).
Note that automatically installing the certificate into the local trust stores is for convenience only and isn't guaranteed to work, especially if containers are being used or if Caddy is being run as an unprivileged system service. Ultimately, if you are relying on internal PKI, it is the system administrator's responsibility to ensure Caddy's root CA is properly added to the necessary trust stores (this is outside the scope of the web server).
### CA Intermediates
An intermediate certificate and key will also be generated, which will be used for signing leaf (individual site) certificates.
Unlike the root certificate, intermediate certificates have a much shorter lifetime and will automatically be renewed as needed.
## Testing
To test or experiment with your Caddy configuration, make sure you [change the ACME endpoint](/docs/modules/tls.issuance.acme#ca) to a staging or development URL, otherwise you are likely to hit rate limits which can block your access to HTTPS for up to a week, depending on which rate limit you hit.
One of Caddy's default CAs is [Let's Encrypt ](https://letsencrypt.org/), which has a [staging endpoint ](https://letsencrypt.org/docs/staging-environment/) that is not subject to the same [rate limits ](https://letsencrypt.org/docs/rate-limits/):
```
https://acme-staging-v02.api.letsencrypt.org/directory
```
## ACME challenges
Obtaining a publicly-trusted TLS certificate requires validation from a publicly-trusted, third-party authority. These days, this validation process is automated with the [ACME protocol ](https://tools.ietf.org/html/rfc8555), and can be performed one of three ways ("challenge types"), described below.
The first two challenge types are enabled by default. If multiple challenges are enabled, Caddy chooses one at random to avoid accidental dependence on a particular challenge. Over time, it learns which challenge type is most successful and will begin to prefer it first, but will fall back to other available challenge types if necessary.
### HTTP challenge
The HTTP challenge performs an authoritative DNS lookup for the candidate hostname's A/AAAA record, then requests a temporary cryptographic resource over port `80` using HTTP. If the CA sees the expected resource, a certificate is issued.
This challenge requires port `80` to be externally accessible. If Caddy cannot listen on port 80, packets from port `80` must be forwarded to Caddy's [HTTP port](/docs/json/apps/http/http_port/).
This challenge is enabled by default and does not require explicit configuration.
### TLS-ALPN challenge
The TLS-ALPN challenge performs an authoritative DNS lookup for the candidate hostname's A/AAAA record, then requests a temporary cryptographic resource over port `443` using a TLS handshake containing special ServerName and ALPN values. If the CA sees the expected resource, a certificate is issued.
This challenge requires port `443` to be externally accessible. If Caddy cannot listen on port 443, packets from port `443` must be forwarded to Caddy's [HTTPS port](/docs/json/apps/http/https_port/).
This challenge is enabled by default and does not require explicit configuration.
### DNS challenge
The DNS challenge performs an authoritative DNS lookup for the candidate hostname's `TXT` records, and looks for a special `TXT` record with a certain value. If the CA sees the expected value, a certificate is issued.
This challenge does not require any open ports, and the server requesting a certificate does not need to be externally accessible. However, the DNS challenge requires configuration. Caddy needs to know the credentials to access your domain's DNS provider so it can set (and clear) the special `TXT` records. If the DNS challenge is enabled, other challenges are disabled by default.
Since ACME CAs follow DNS standards when looking up `TXT` records for challenge verification, you can use CNAME records to delegate answering the challenge to other DNS zones. This can be used to delegate the `_acme-challenge` subdomain to [another zone](/docs/caddyfile/directives/tls#dns_challenge_override_domain). This is particularly useful if your DNS provider doesn't provide an API, or isn't supported by one of the DNS plugins for Caddy.
DNS provider support is a community effort. [Learn how to enable the DNS challenge for your provider at our wiki.](https://caddy.community/t/how-to-use-dns-provider-modules-in-caddy-2/8148)
## On-Demand TLS
Caddy pioneered a new technology we call **On-Demand TLS**, which dynamically obtains a new certificate during the first TLS handshake that requires it, rather than at config load. Crucially, this does **not** require hard-coding the domain names in your configuration ahead of time.
Many businesses rely on this unique feature to scale their TLS deployments at lower cost and without operational headaches when serving tens of thousands of sites.
On-demand TLS is useful if:
- you do not know all the domain names when you start or reload your server,
- domain names might not be properly configured right away (DNS records not yet set),
- you are not in control of the domain names (e.g. they are customer domains).
When on-demand TLS is enabled, you do not need to specify the domain names in your config in order to get certificates for them. Instead, when a TLS handshake is received for a server name (SNI) that Caddy does not yet have a certificate for, the handshake is held while Caddy obtains a certificate to use to complete the handshake. The delay is usually only a few seconds, and only that initial handshake is slow. All future handshakes are fast because certificates are cached and reused, and renewals happen in the background. Future handshakes may trigger maintenance for the certificate to keep it renewed, but this maintenance happens in the background if the certificate hasn't expired yet.
### Using On-Demand TLS
**On-demand TLS must be both enabled and restricted to prevent abuse.**
Enabling on-demand TLS happens in [TLS automation policies](/docs/json/apps/tls/automation/policies/) if using the JSON config, or [in site blocks with the `tls` directive](/docs/caddyfile/directives/tls) if using the Caddyfile.
To prevent abuse of this feature, you must configure restrictions. This is done in the [`automation` object of the JSON config](/docs/json/apps/tls/automation/on_demand/), or the [`on_demand_tls` global option](/docs/caddyfile/options#on-demand-tls) of the Caddyfile. Restrictions are "global" and aren't configurable per-site or per-domain. The primary restriction is an "ask" endpoint to which Caddy will send an HTTP request to ask if it has permission to obtain and manage a certificate for the domain in the handshake. This means you will need some internal backend that can, for example, query the accounts table of your database and see if a customer has signed up with that domain name.
Be mindful of how quickly your CA is able to issue certificates. If it takes more than a few seconds, this will negatively impact the user experience (for the first client only).
Due to its deferred nature and the extra configuration required to prevent abuse, we recommend enabling on-demand TLS only when your actual use case is described above.
[See our wiki article for more information about using on-demand TLS effectively.](https://caddy.community/t/serving-tens-of-thousands-of-domains-over-https-with-caddy/11179)
## Errors
Caddy does its best to continue if errors occur with certificate management.
By default, certificate management is performed in the background. This means it will not block startup or slow down your sites. However, it also means that the server will be running even before all certificates are available. Running in the background allows Caddy to retry with exponential backoff over a long period of time.
Here's what happens if there's an error obtaining or renewing a certificate:
1. Caddy retries once after a brief pause just in case it was a fluke
2. Caddy pauses briefly, then switches to the next enabled challenge type
3. After all enabled challenge types have been tried, [it tries the next configured issuer](#issuer-fallback)
- Let's Encrypt
- ZeroSSL
4. After all issuers have been tried, it backs off exponentially
- Maximum of 1 day between attempts
- For up to 30 days
During retries with Let's Encrypt, Caddy switches to their [staging environment ](https://letsencrypt.org/docs/staging-environment/) to avoid rate limit concerns. This isn't a perfect strategy, but in general it's helpful.
ACME challenges take at least a few seconds, and internal rate limiting helps mitigate accidental abuse. Caddy uses internal rate limiting in addition to what you or the CA configure so that you can hand Caddy a platter with a million domain names and it will gradually -- but as fast as it can -- obtain certificates for all of them. Caddy's internal rate limit is currently 10 attempts per ACME account per 10 seconds.
To avoid leaking resources, Caddy aborts in-flight tasks (including ACME transactions) when config is changed. While Caddy is capable of handling frequent config reloads, be mindful of operational considerations such as this, and consider batching config changes to reduce reloads and give Caddy a chance to actually finish obtaining certificates in the background.
### Issuer fallback
Caddy is the first (and so far only) server to support fully-redundant, automatic failover to other CAs in the event it cannot successfully get a certificate.
By default, Caddy enables two ACME-compatible CAs: [**Let's Encrypt** ](https://letsencrypt.org) and [**ZeroSSL** ](https://zerossl.com). If Caddy cannot get a certificate from Let's Encrypt, it will try with ZeroSSL; if both fail, it will backoff and retry again later. In your config, you can customize which issuers Caddy uses to obtain certificates, either universally or for specific names.
## Storage
Caddy will store public certificates, private keys, and other assets in its [configured storage facility](/docs/json/storage/) (or the default one, if not configured -- see link for details).
**The main thing you need to know using the default config is that the `$HOME` folder must be writeable and persistent.** To help you troubleshoot, Caddy prints its environment variables at startup if the `--environ` flag is specified.
Any Caddy instances that are configured to use the same storage will automatically share those resources and coordinate certificate management as a cluster.
Before attempting any ACME transactions, Caddy will test the configured storage to ensure it is writeable and has sufficient capacity. This helps reduce unnecessary lock contention.
## Wildcard certificates
Caddy can obtain and manage wildcard certificates when it is configured to serve a site with a qualifying wildcard name. A site name qualifies for a wildcard if only its left-most domain label is a wildcard. For example, `*.example.com` qualifies, but these do not: `sub.*.example.com`, `foo*.example.com`, `*bar.example.com`, and `*.*.example.com`. (This is a restriction of the WebPKI.)
If using the Caddyfile, Caddy takes site names literally with regards to the certificate subject names. In other words, a site defined as `sub.example.com` will cause Caddy to manage a certificate for `sub.example.com`, and a site defined as `*.example.com` will cause Caddy to manage a wildcard certificate for `*.example.com`. You can see this demonstrated on our [Common Caddyfile Patterns](/docs/caddyfile/patterns#wildcard-certificates) page. If you need different behavior, the [JSON config](/docs/json/) gives you more precise control over certificate subjects and site names ("host matchers").
As of Caddy 2.10, when automating a wildcard certificate, Caddy will use the wildcard certificate for individual subdomains in the configuration. It will not get certificates for individual subdomains unless explicitly configured to do so.
Wildcard certificates represent a wide degree of authority and should only be used when you have so many subdomains that managing individual certificates for them would strain the PKI or cause you to hit CA-enforced rate limits, or if the privacy tradeoff is worth the risk of exposing that much of the DNS zone in the case of a key compromise. Note that wildcard certificates alone do not offer privacy of concealing specific subdomains: they are still exposed in TLS ClientHello packets unless Encrypted ClientHello (ECH) is enabled. (See below.)
**Note:** [Let's Encrypt requires ](https://letsencrypt.org/docs/challenge-types/) the [DNS challenge](#dns-challenge) to obtain wildcard certificates.
## Encrypted ClientHello (ECH)
Normally, TLS handshakes involve sending the ClientHello, including the Server Name Indicator (SNI; the domain being connected to), in plaintext. That's because it contains the parameters necessary for encrypting the connection that comes after the handshake. This, of course, exposes the domain name, which is the most sensitive part of the ClientHello, to anyone who can eavesdrop connections, even if they are not in your immediate physical vicinity. It reveals which service you are connecting to when the destination IP may serve many different sites, and it's how some governments censor the Internet.
With Encrypted ClientHello, the client can protect the domain name by wrapping the true ClientHello in an "outer" ClientHello that establishes parameters for decrypting the "inner" ClientHello. However, many moving parts need to come together perfectly for this to work and deliver actual privacy benefits.
First, the client needs to know what parameters, or configuration, to use to encrypt the ClientHello. This information includes a public key and "outer" domain (the "public name"), among other things. This configuration has to be published or distributed somehow in a reliable fashion.
You could theoretically write it down on a piece of paper and hand it out to everybody, but most major browsers support looking up HTTPS-type DNS records containing ECH parameters when connecting to a site. Hence, you will need to: (1) generate an ECH configuration (public/private key pair, among other parameters), and then (2) create an HTTPS-type DNS record containing the base64-encoded ECH configuration.
Or... you could let Caddy do that all for you. Caddy is the first and only web server that can automatically generate, publish, and serve ECH configurations.
Once the HTTPS record is published, clients will need to perform a DNS lookup for the HTTPS record when connecting to your site. Normally, DNS lookups are in plaintext, which compromises the security of the resulting ECH handshakes, so browsers will need to use a secure DNS protocol like DNS-over-HTTPS (DoH) or DNS-over-TLS (DoT). Depending on the browser, this may need to be manually enabled.
Once the client has securely downloaded the ECH config, it uses the embedded public key to encrypt the ClientHello, and proceeds to connect to your site. Caddy then decrypts the inner ClientHello and proceeds to serve your site, without the domain name ever appearing in plaintext over the wire.
### Deployment considerations
ECH is a nuanced technology. Even though Caddy completely automates ECH, many things need to be considered in order for maximum privacy benefits. You should also be aware of various trade-offs.
#### Publication
Caddy will only create an HTTPS record for a domain if there is already a record for that domain. This prevents breaking DNS lookups for a subdomain that may be covered by a wildcard. Ensure that your sites have at least an A/AAAA record pointing to your server. If you only use a wildcard for DNS records, then the wildcard domain will need to appear in your Caddy config as well.
Caddy will not publish an HTTPS record for a domain that has a CNAME record.
#### ECH GREASE
If you open Wireshark and then connect to any site (even one that does not support ECH) in a modern version of a major browser like Firefox or Chrome (even with ECH disabled), you may notice its handshake includes the `encrypted_client_hello` extension:

The purpose of this is to make true ECH handshakes indistinguishable from plaintext ones. If ECH handshakes looked different than normal ones, censors could just block ECH handshakes with minimal fallout/collateral damage. But if they blocked any handshake with a plausible ECH extension, they would essentially turn off most of the Internet. (The goal is to increase the cost of widespread censorship.)
This is mainly important to know when troubleshooting connections.
#### Key rotation
Like certificate keys, it is not good practice (and can be downright insecure) to use the same key for a long time. As such, ECH keys should be rotated on a regular basis. Unlike certificates, ECH configs don't strictly expire. But servers should rotate them nonetheless.
Key rotation is tricky though, because clients need to know about the updated keys. If the server simply replaced old keys with new ones, all ECH handshakes would fail unless clients were immediately notified about the new keys. But simply publishing the updated keys isn't enough. The reality is, DNS records have TTLs, and resolvers cache responses, etc. It can take minutes, hours, or even days for clients to query the updated HTTPS records and start using the new ECH config.
For that reason, servers should keep supporting old ECH configs for a period of time. Not doing so risks exposing server names in plaintext _at scale_. Caddy rotates keys every once in a while, and supports rotated keys for some time, until they are eventually dropped.
However, that may not be enough. Some clients still won't get the updated keys for various reasons, and any time that happens, there is a risk of exposing the server name. So there needs to be another way to give clients the updated config _in band_ with the connection. That's what the _outer name_ (or _public name_) is for.
#### Public name
The "outer" ClientHello is a normal ClientHello with two subtle differences that are only known to the origin server:
1. The SNI extension is fake
2. The ECH extension is real
That "outer" SNI extension contains the public name that protects your real domains. This name can be anything, but **your server must be authoritative for the public name** because Caddy _will_ obtain a certificate for it.
If a client tries to make an ECH connection but the server can't decrypt the inner ClientHello, it can actually complete the handshake using the _outer_ ClientHello with a certificate for the outer name. This secure connection is strictly _only_ used to send the client the current ECH config; i.e. it is a temporary TLS connection for the sole purposes of completing the initial TLS connection. No application data is transmitted: just the ECH key. Once the client has the updated key, it can establish the TLS connection as intended.
In this manner, the true server name remains protected and out-of-sync clients remain able to connect, which are both vital elements of security.
The outer name may be one of your site's domains, a subdomain, or any other domain name that points to your server. We recommend choosing exactly one generic name. For example, Cloudflare serves millions of sites behind `cloudflare-ech.com`. This is important for increasing the size of your anonymity set.
Public names should not be empty; i.e. a public name must be configured for things to work. Caddy does not currently enforce this (and may later), but the ECH specification requires the public name to be at least 1 byte long. Some software will accept empty names, others won't. This can lead to confusing behaviors such as browsers using ECH but servers rejecting it as invalid; or browsers not using ECH (because it is invalid) even though the config is in the DNS record properly. It is the responsibility of the site owner to ensure proper ECH configuration and publication to ensure privacy.
#### Anonymity set
To maximize the privacy benefits of ECH, strive to maximize the size of your _anonymity set_. In essense, this set is comprised of client-facing servers that have identical behavior to observers. The idea is that an observer cannot easily reduce/deduce the possible sites or services clients are connecting to.
In practice, we recommend having only one public name for all your sites. (There is only 1 public name per ECH config, so this implies having only 1 active ECH config at any given time.) If you operate Caddy in a cluster, Caddy automatically shares and coordinates ECH configs with other instances, which takes care of this for you.
Taken to the extreme, this implies that every site on the Internet could or should be behind a single IP address and one public name...
#### Centralization
... which brings us to our next topic: centralization. One of the criticisms of ECH is that it tends to motivate centralization. It does this in at least two ways: (1) by clients favoring DoH/DoT for DNS lookups, which sends all DNS lookups through a small handful of providers, and (2) by maximizing the size of the anonymity set at scale.
When DoH or DoT is used, DNS lookups all go through the DoH/DoT provider. Between the client and the provider, the DNS data is encrypted, but between the provider and the DNS server, it is not encrypted. Global DoH/DoT effectively funnels all the juicy plaintext DNS traffic into a few big pipes that are ripe for observation... or failure.
Similarly, if we truly maximize the anonymity set at scale, all sites would be protected behind a single public name, like `cloudflare-ech.com`. This is good for privacy, but then the entire Internet is at the mercy of Cloudflare and that one domain name. Now, maximizing to that extent is not necessary or practical, but the theoretical implications remain valid.
We recommend each organization or individual choose a single name for all their sites and use that, and in most cases that should offer sufficient privacy. However, please consult experts with your individual threat models for your specific case.
#### Subdomain privacy
With ECH, it is now theoretically possible to keep subdomains secret/private from side channels if deployed correctly.
Most sites do not need this, as, generally speaking, subdomains are public information. We advise against putting sensitive information in domain names. That said...
To avoid leaking sensitive subdomains to Certificate Transparency (CT) logs, use a wildcard certificate instead. In other words, instead of putting `sub.example.com` in your config, put `*.example.com`. (See [Wildcard certificates](#wildcard-certificates) for important information.)
Then, enable ECH in Caddy. A wildcard certificate combined with ECH should properly hide subdomains, as long as every client that tries to connect to it uses ECH and has a strong implementation. (You are still at the mercy of clients to preserve privacy.)
### Enabling ECH
Since functioning ECH requires publishing configs to DNS records, you will need a Caddy build with a [caddy-dns module](https://github.com/caddy-dns) plugged in for your DNS provider.
Then, with a Caddyfile, specify your DNS provider config in the global options, as well as the ECH public name you want to use:
```caddy
{
dns
ech example.com
}
```
Remember:
- The DNS provider module must be plugged in and you must have the right configuration for your provider/account.
- The ECH public name should point to your server. Caddy will get a certificate for it. It does not have to be one of your site's domains.
If using JSON, add these properties to the `tls` app:
```json
"encrypted_client_hello": {
"configs": [
{
"public_name": "example.com"
}
]
},
"dns": {
"name": "",
// provider configuration
}
```
These configurations will enable ECH and publish ECH configs for all your sites. The JSON config offers more flexibility if you need to customize the behavior or have an advanced setup.
### Verifying ECH
There is still not much tooling around ECH, so at time of writing, the best and most universal way to verify that it's working is to use Wireshark and look for your public name in the ServerName field.
First, start your server and see that the logs mention something like "published ECH configuration list" for your domains. (If you get any errors with publication, ensure your DNS provider module supports [libdns 1.0](https://github.com/libdns/libdns) and file an issue with your provider's repository if you encounter problems.) Caddy should also get a certificate for the public name.
Next, make sure your browser has ECH enabled; this may require enabling DoH/DoT. It's also a good idea to clear your browser's (or system's) DNS cache, to ensure it will pick up the newly published HTTPS records. We also recommend closing the browser or at least opening a new private tab to ensure it does not reuse existing connections.
Then, open Wireshark and start listening on the appropriate network interface. While Wireshark is collecting packets, load your site in your browser. You can then pause Wireshark. Find your TLS ClientHello, and you should see the _public name_ in the ServerName field, rather than the actual domain name you connected to.
Remember: you may still see an `encrypted_client_hello` extension even if ECH is not used. The key indicator is the SNI value. You should never see the true site name in plaintext with Wireshark if ECH is working properly.
If you encounter deployment issues with ECH, first ask in our [forum](https://caddy.community). If it's a bug, you can [file an issue](https://github.com/caddyserver/caddy/issues) on GitHub.
### ECH in storage
ECH configurations are stored in the [data directory](/docs/conventions#data-directory) in the configured storage module (the default being the file system) under the `ech/configs` folder.
The next folder is an ECH config ID, which are randomly generated and relatively unimportant. The randomness is recommended by the spec to help mitigate fingerprinting/tracking.
A metadata sidecar file helps Caddy keep track of when publications last occurred. This prevents hammering your DNS provider at every config reload. If you have to reset this state, you may safely delete the metadata file. However, this may also reset the time when the key will be rotated. You can also go into the file and clear out just the information about publication.
---
---
title: "Build from source"
---
# Build from source
There are multiple options for building Caddy, if you need a customized build (e.g. with plugins):
- [Git](#git): Build from Git repo
- [`xcaddy`](#xcaddy): Build using `xcaddy`
- [Docker](#docker): Build a custom Docker image
Requirements:
- [Go](https://golang.org/doc/install) 1.20 or newer
The [Package Support Files](#package-support-files-for-custom-builds-for-debianubunturaspbian) section contains instructions for users who installed Caddy using the APT command on Debian-derivative system yet need the custom build executable for their operations.
## Git
Requirements:
- Go installed (see above)
Clone the repository:
If you don't have git, you can download the source code as a file archive [from GitHub](https://github.com/caddyserver/caddy). Each [release](https://github.com/caddyserver/caddy/releases) also has source snapshots.
Build:
cd caddy/cmd/caddy/go build
Go programs are easy to compile for other platforms. Just set the `GOOS`, `GOARCH`, and/or `GOARM` environment variables that are different. ([See the go documentation for details.](https://golang.org/doc/install/source#environment))
For example, to compile Caddy for Windows when you're not on Windows:
GOOS=windows go build
Or similarly for Linux ARMv6 when you're not on Linux or on ARMv6:
GOOS=linux GOARCH=arm GOARM=6 go build
## xcaddy
The [`xcaddy` command](https://github.com/caddyserver/xcaddy) is the easiest way to build Caddy with version information and/or plugins.
Requirements:
- Go installed (see above)
- Make sure [`xcaddy`](https://github.com/caddyserver/xcaddy/releases) is in your `PATH`
You do **not** need to download the Caddy source code (it will do that for you).
Then building Caddy (with version information) is as easy as:
As you can see, you can customize the versions of plugins with `@` syntax. Versions can be a tag name, commit SHA, or branch.
Cross-platform compilation with `xcaddy` works the same as with the `go` command. For example, to cross-compile for macOS:
GOOS=darwin xcaddy build
## Docker
You can use the `:builder` image as a short-cut to building a new Caddy binary with custom modules:
```Dockerfile
FROM caddy:-builder AS builder
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
xcaddy build \
--with github.com/caddyserver/nginx-adapter \
--with github.com/hairyhenderson/caddy-teapot-module@v0.0.3-0
FROM caddy:
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
```
Make sure to replace `` with the latest version of Caddy to start.
Note the second `FROM` instruction — this produces a much smaller image by simply overlaying the newly-built binary on top of the regular `caddy` image.
The builder uses `xcaddy` to build Caddy with the provided modules, similar to the process [outlined above](#xcaddy). The `--mount=type=cache,target=/go/pkg/mod` and `--mount=type=cache,target=/root/.cache/go-build` options are used to cache the Go module dependencies and build artifacts, respectively, which speeds up subsequent builds. The flag is [a feature of Docker](https://docs.docker.com/build/cache/optimize/#use-cache-mounts), not of `xcaddy`.
To use Docker Compose, see our recommended [`compose.yml`](/docs/running#docker-compose) and usage instructions.
## Package support files for custom builds for Debian/Ubuntu/Raspbian
This procedure aims to simplify running custom `caddy` binaries while keeping support files from the `caddy` package.
This procedure allows users to take advantage of the default configuration, systemd service files and bash-completion from the official package.
Requirements:
- Install the `caddy` package according to [these instructions](/docs/install#debian-ubuntu-raspbian)
- Build your custom `caddy` binary (see above sections), or [download](/download) a custom build
- Your custom `caddy` binary should be located in the current directory
Procedure:
Explanation:
- `dpkg-divert` will move `/usr/bin/caddy` binary to `/usr/bin/caddy.default` and put a diversion in place in case any package want to install a file to this location.
- `update-alternatives` will create a symlink from the desired caddy binary to `/usr/bin/caddy`
- `systemctl restart caddy` will shut down the default version of the Caddy server and start the custom one.
You can change between the custom and default `caddy` binaries by executing the below, and following the on screen information. Then, restart the Caddy service.
update-alternatives --config caddy
To upgrade Caddy after this point, you may run [`caddy upgrade`](/docs/command-line#caddy-upgrade). This attempts to [download](/download) a build with the same plugins as your current build, with the latest version of Caddy, then replace the current binary with the new one.
---
---
title: Caddyfile Concepts
---
# Caddyfile Concepts
This document will help you learn about the HTTP Caddyfile in detail.
1. [Structure](#structure)
- [Blocks](#blocks)
- [Directives](#directives)
- [Tokens and quotes](#tokens-and-quotes)
2. [Global options](#global-options)
3. [Addresses](#addresses)
4. [Matchers](#matchers)
5. [Placeholders](#placeholders)
6. [Snippets](#snippets)
7. [Named Routes](#named-routes)
8. [Comments](#comments)
9. [Environment variables](#environment-variables)
## Structure
The Caddyfile's structure can be described visually:

Key points:
- An optional [**global options block**](#global-options) can be the very first thing in the file.
- [Snippets](#snippets) or [named routes](#named-routes) may optionally appear next.
- Otherwise, the first line of the Caddyfile is **always** the [address(es)](#addresses) of the site to serve.
- All [directives](#directives) and [matchers](#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](#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:
```
... {
...
}
```
- The open curly brace `{` must be at the end of its line and preceded by a space.
- The close curly brace `}` must be on its own line.
When there is only one site block, the curly braces (and indentation) are optional. This is for convenience to quickly define a single site, for example, this:
```caddy
localhost
reverse_proxy /api/* localhost:9001
file_server
```
is equivalent to:
```caddy
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:
```caddy
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**](/docs/caddyfile/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:
```caddy
localhost {
file_server
}
```
Or a reverse proxy:
```caddy
localhost {
reverse_proxy localhost:9000
}
```
In these examples, [`file_server`](/docs/caddyfile/directives/file_server) and [`reverse_proxy`](/docs/caddyfile/directives/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:
```caddy
localhost {
reverse_proxy localhost:9000 localhost:9001 {
lb_policy first
}
}
```
Here, `lb_policy` is a subdirective to [`reverse_proxy`](/docs/caddyfile/directives/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`](/docs/caddyfile/directives/basic_auth) cannot be used within [`file_server`](/docs/caddyfile/directives/file_server) because the file server does not know how to do authentication; but you can use directives within [`route`](/docs/caddyfile/directives/route), [`handle`](/docs/caddyfile/directives/handle), and [`handle_path`](/docs/caddyfile/directives/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](/docs/caddyfile/directives#directive-order) unless in a [`route`](/docs/caddyfile/directives/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:
```caddy-d
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:
```caddy-d
directive "abc def"
```
Quotes can be escaped if you need to use quotes in quoted tokens, too:
```caddy-d
directive "\"abc def\""
```
To avoid escaping quotes, you can instead use backticks \` \` to enclose tokens; for example:
```caddy-d
directive `{"foo": "bar"}`
```
Inside quoted tokens, all other characters are treated literally, including spaces, tabs, and newlines. Multi-line tokens are thus possible:
```caddy-d
directive "first line
second line"
```
Heredocs are also supported:
```caddy
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](https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc)) which is nice for readability inside [blocks](#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](/docs/caddyfile/options):
```caddy
{
...
}
```
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:
```caddy
{
debug
}
```
**[Read the Global Options page](/docs/caddyfile/options) to learn more.**
## 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](/docs/automatic-https#hostname-requirements) |
| `*.example.com` | HTTPS with managed [wildcard publicly-trusted certificate](/docs/caddyfile/patterns#wildcard-certificates) |
| `localhost` | HTTPS with managed [locally-trusted certificate](/docs/automatic-https#local-https) |
| `http://` | HTTP catch-all, affected by [`http_port`](/docs/caddyfile/options#http-port) |
| `https://` | HTTPS catch-all, affected by [`https_port`](/docs/caddyfile/options#http-port) |
| `http://example.com` | HTTP explicitly, with a `Host` matcher |
| `example.com:443` | HTTPS due to matching the [`https_port`](/docs/caddyfile/options#http-port) default |
| `:443` | HTTPS catch-all due to matching the [`https_port`](/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`) |
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](/docs/caddyfile/patterns#wildcard-certificates) 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](/docs/automatic-https#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, separated with spaces and commas (at least one space is necessary). The following three examples are equivalent:
```caddy
# Comma separated site addresses
localhost:8080, example.com, www.example.com {
...
}
```
or
```caddy
# Space separated site addresses
localhost:8080 example.com www.example.com {
...
}
```
or
```caddy
# 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](#placeholders) **cannot** be used in addresses, but you may use Caddyfile-style [environment variables](#environment-variables) in them:
```caddy
{$DOMAIN:localhost} {
...
}
```
By default, sites bind on all network interfaces. If you wish to override this, use the [`bind` directive](/docs/caddyfile/directives/bind) or the [`default_bind` global option](/docs/caddyfile/options#default-bind) to do so.
## Matchers
HTTP handler [directives](#directives) apply to all requests by default (unless otherwise documented).
[Request matchers](/docs/caddyfile/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:
```caddy-d
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.
**[Read the Request Matchers page](/docs/caddyfile/matchers) to learn more.**
## Placeholders
[Placeholders](/docs/conventions#placeholders) are a simple way to inject dynamic values into your static configuration. They can be used as arguments to directives and subdirectives.
Placeholders are bounded on either side by curly braces `{ }` and contain the identifier inside, for example: `{foo.bar}`. The opening placeholder brace can be escaped `\{like.this}` to prevent replacement. Placeholder identifiers are typically namespaced with dots to avoid collisions across modules.
Which placeholders are available depends on the context. Not all placeholders are available in all parts of the config. For example, [the HTTP app sets placeholders](/docs/json/apps/http/#docs) that are only available in areas of the config related to handling HTTP requests (i.e. in HTTP handler [directives](#directives) and [matchers](#matchers), but _not_ in [`tls` configuration](/docs/caddyfile/directives/tls)). Some directives or matchers may set their own placeholders too which can be used by anything that follows them. Some placeholders [are globally available](/docs/conventions#placeholders).
You can use any placeholders in the Caddyfile, but for convenience you can also use some of these equivalent shorthands which are expanded when the Caddyfile is parsed:
| 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.*}` |
| `{resp.*}` | `{http.intercept.*}` |
| `{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.*}` |
Not all config fields support placeholders, but most do where you would expect it. Support for placeholders needs to have been explicitly added to those fields. Plugin authors can [read this article](/docs/extending-caddy/placeholders) to learn how to add support for placeholders in their own modules.
## Snippets
You can define special blocks called snippets by giving them a name surrounded in parentheses:
```caddy
(logging) {
log {
output file /var/log/caddy.log
format json
}
}
```
And then you can reuse this anywhere you need, using the special [`import`](/docs/caddyfile/directives/import) directive:
```caddy
example.com {
import logging
}
www.example.com {
import logging
}
```
The [`import`](/docs/caddyfile/directives/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:
```caddy
{
email admin@example.com
}
import sites/*
```
You can pass arguments to an imported configuration (snippets or files) and use them like so:
```caddy
(snippet) {
respond "Yahaha! You found {args[0]}!"
}
a.example.com {
import snippet "Example A"
}
b.example.com {
import snippet "Example B"
}
```
⚠️ Experimental | v2.9.x+
You can also pass an optional block to an imported snippet, and use them as follows.
```caddy
(snippet) {
{block}
respond "OK"
}
a.example.com {
import snippet {
header +foo bar
}
}
b.example.com {
import snippet {
header +bar foo
}
}
```
**[Read the `import` directive page](/docs/caddyfile/directives/import) to learn more.**
## Named Routes
⚠️ Experimental
Named routes use syntax similar to [snippets](#snippets); they're a special block defined outside of site blocks, prefixed with `&(` and ending in `)` with the name in between.
```caddy
&(app-proxy) {
reverse_proxy app-01:8080 app-02:8080 app-03:8080
}
```
And then you can reuse this named route within any site:
```caddy
example.com {
invoke app-proxy
}
www.example.com {
invoke app-proxy
}
```
This is particularly useful to reduce memory usage if the same route is needed in many different sites, or if multiple different matcher conditions are needed to invoke the same route.
**[Read the `invoke` directive page](/docs/caddyfile/directives/invoke) to learn more.**
## Comments
Comments start with `#` and proceed until the end of the line:
```caddy-d
# 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:
```caddy
{$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, an environment variable `UPSTREAMS="app1:8080 app2:8080 app3:8080"` would expand to multiple [tokens](#tokens-and-quotes):
```caddy
example.com {
reverse_proxy {$UPSTREAMS}
}
```
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:
```caddy
{$DOMAIN:localhost} {
}
```
If you want to **defer the substitution** of an environment variable until runtime, you can use the [standard `{env.*}` placeholders](/docs/conventions#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 ](https://github.com/caddy-dns/cloudflare) installed and wish to configure the [DNS challenge](/docs/automatic-https#dns-challenge), you can pass your `CLOUDFLARE_API_TOKEN` environment variable to the plugin like this:
```caddy
{
acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
```
If you're running Caddy as a systemd service, see [these instructions](/docs/running#overrides) for setting service overrides to define your environment variables.
---
---
title: abort (Caddyfile directive)
---
# 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
```caddy-d
abort []
```
## Examples
Forcefully close a connection received for unknown domains when using a wildcard certificate:
```caddy
*.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
}
}
```
---
---
title: acme_server (Caddyfile directive)
---
# 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.
## Client configuration
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.)
## Syntax
```caddy-d
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](/docs/caddyfile/options#pki-options) to configure alternate CAs.
- **lifetime** (Default: `12h`) is a [duration](/docs/conventions#durations) which specifies the validity period for issued certificates. This value must be less than the lifetime of the [intermediate certificate](/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](/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 host 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](/docs/caddyfile/options#pki-options), and issuing its own certificate using the `internal` issuer:
```caddy
{
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:
```caddy
{
acme_ca https://acme.example.com/acme/home/directory
acme_ca_root /path/to/home_ca_root.crt
}
example.com {
respond "Hello, world!"
}
```
---
---
title: basic_auth (Caddyfile directive)
---
# 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`](/docs/command-line#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.
Prior to v2.8.0, this directive was named `basicauth`, but was renamed for consistency with other directives.
## Syntax
```caddy-d
basic_auth [] [ []] {
...
}
```
- **<hash_algorithm>** is the name of the password hashing algorithm (or KDF) used for the hashes in this configuration. Default: `bcrypt`
- **<realm>** is a custom realm name.
- **<username>** is a username or user ID.
- **<hashed_password>** is the password hash.
## Examples
Require authentication for all requests to `example.com`:
```caddy
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):
```caddy
example.com {
root * /srv
basic_auth /secret/* {
# Username "Bob", password "hiccup"
Bob $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG
}
file_server
}
```
---
---
title: bind (Caddyfile directive)
---
# 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](/docs/caddyfile/concepts#addresses) (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](/docs/conventions#network-addresses), but may not include a port.
## Syntax
```caddy-d
bind
```
- **<hosts...>** 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):
```caddy
example.com {
bind 127.0.0.1
}
```
To include IPv6:
```caddy
example.com {
bind 127.0.0.1 [::1]
}
```
To bind to `10.0.0.1:8080`:
```caddy
example.com:8080 {
bind 10.0.0.1
}
```
To bind to a Unix domain socket at `/run/caddy`:
```caddy
example.com {
bind unix//run/caddy
}
```
To change the file permission to be writable by all users ([defaults](/docs/conventions#network-addresses) to `0200`, which is only writable by the owner):
```caddy
example.com {
bind unix//run/caddy|0222
}
```
To bind one domain to two different interfaces, with different responses:
```caddy
example.com {
bind 10.0.0.1
respond "One"
}
example.com {
bind 10.0.0.2
respond "Two"
}
```
---
---
title: encode (Caddyfile directive)
---
# encode
Encodes responses using the configured encoding(s). A typical use for encoding is compression.
## Syntax
```caddy-d
encode [] [] {
# encoding formats
gzip []
zstd []
minimum_length
match
}
```
- **<formats...>** 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. If omitted, `zstd` (preferred) and `gzip` are enabled by default.
- **gzip** enables Gzip compression, optionally at a specified level.
- **zstd** enables Zstandard compression, optionally at a specified level (possible values = default, fastest, better, best). The default compression level is roughly equivalent to the default Zstandard mode (level 3).
- **minimum_length** the minimum number of bytes a response should have to be encoded (default: 512).
- **match** is a [response matcher](/docs/caddyfile/response-matchers). Only matching responses are encoded. The default looks like this:
```caddy-d
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/*
}
```
## Examples
Enable Gzip compression:
```caddy-d
encode gzip
```
Enable Zstandard and Gzip compression (with Zstandard implicitly preferred, since it is first):
```caddy-d
encode zstd gzip
```
As this is the default value, the previous configuration is strictly equivalent to:
```caddy-d
encode
```
And in a full site, compressing static files served by [`file_server`](file_server):
```caddy
example.com {
root * /srv
encode
file_server
}
```
---
---
title: error (Caddyfile directive)
---
# 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`](handle_errors) directive to invoke your custom error handling logic.
## Syntax
```caddy-d
error [] | [] {
message
}
```
- **<status>** is the HTTP status code to write. Default is `500`.
- **<message>** 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`](handle_errors) to write a response:
```caddy
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
}
```
---
---
title: file_server (Caddyfile directive)
---
# 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](root).
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`](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`](handle_errors) directive to define error routes, and display custom error pages.
When using `browse`, the default output is produced by the the HTML template. Clients may request the directory listing as either JSON or plaintext, by using the `Accept: application/json` or `Accept: text/plain` headers respectively. The JSON output can be useful for scripting, and the plaintext output can be useful for human terminal usage.
## Syntax
```caddy-d
file_server [] [browse] {
fs
root
hide
index
browse [] {
reveal_symlinks
sort []
}
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`](/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`](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`](try_files) or [`templates`](templates)) to know the same site root, use the [`root`](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 default template to stdout. The embedded template can also be found [here in the source code ](https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/fileserver/browse.html). Browse templates can use actions from [the standard templates module](/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.
- **sort** changes the default sort for directory listings. The first parameter is the field/column to sort by: `name`, `namedirfirst`, `size`, or `time`. The second argument is an optional direction: `asc` or `desc`. For example, `sort name desc` will sort by name in descending order.
- **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](https://en.wikipedia.org/wiki/Sidecar_file). Supported formats are `gzip` (`.gz`), `zstd` (`.zst`) and `br` (`.br`). If formats are omitted, they default to `br zstd gzip` (in that order).
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](encode) 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](handle_errors). 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`](handle_errors) routes). Practically, this is only useful inside of a [`route`](route) block with other handler directives following `file_server`, because this directive is effectively [ordered last](/docs/caddyfile/directives#directive-order).
## Examples
A static file server out of the current directory:
```caddy-d
file_server
```
With file listings enabled:
```caddy-d
file_server browse
```
Only serve static files within the `/static` folder:
```caddy-d
file_server /static/*
```
The `file_server` directive is usually paired with the [`root` directive](root) to set the root path from which to serve files:
```caddy
example.com {
root * /srv
file_server
}
```
Hide all `.git` folders and their contents:
```caddy-d
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.br`, `/path/to/file.zst` and `/path/to/file.gz` in that order and serves the first available file with corresponding `Content-Encoding`:
```caddy-d
file_server {
precompressed
}
```
---
---
title: forward_auth (Caddyfile directive)
---
# forward_auth
An opinionated directive which proxies a clone of the request to an authentication gateway, which can decide whether handling should continue, or needs to be sent to a login page.
- [Syntax](#syntax)
- [Expanded Form](#expanded-form)
- [Examples](#examples)
- [Authelia](#authelia)
- [Tailscale](#tailscale)
Caddy's [`reverse_proxy`](/docs/caddyfile/directives/reverse_proxy) is capable of performing "pre-check requests" to an external service, but this directive is tailored specifically for the authentication use case. 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](#expanded-form) below as a basis and customize it to your needs.
All the subdirectives of [`reverse_proxy`](/docs/caddyfile/directives/reverse_proxy) are supported, and passed through to the underlying `reverse_proxy` handler.
## Syntax
```caddy-d
forward_auth [] [] {
uri
copy_headers {
}
}
```
- **<upstreams...>** 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.
Since this directive is an opinionated wrapper over a reverse proxy, you can use any of [`reverse_proxy`](/docs/caddyfile/directives/reverse_proxy#syntax)'s subdirectives to customize it.
## Expanded form
The `forward_auth` directive is the same as the following configuration. Auth gateways like [Authelia](https://www.authelia.com/) 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.
```caddy-d
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 {
# for example, for each copy_headers field...
request_header Remote-User {rp.header.Remote-User}
request_header Remote-Email {rp.header.Remote-Email}
}
}
```
## Examples
### Authelia
Delegating authentication to [Authelia](https://www.authelia.com/), before serving your app via a reverse proxy:
```caddy
# Serve the authentication gateway itself
auth.example.com {
reverse_proxy authelia:9091
}
# Serve your app
app1.example.com {
forward_auth authelia:9091 {
uri /api/authz/forward-auth
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
}
reverse_proxy app1:8080
}
```
For more information, see [Authelia's documentation](https://www.authelia.com/integration/proxies/caddy/) for integrating with Caddy.
### Tailscale
Delegating authentication to [Tailscale](https://tailscale.com/) (currently named [`nginx-auth`](https://tailscale.com/blog/tailscale-auth-nginx/), but it still works with Caddy), and using the alternative syntax for `copy_headers` to *rename* the copied headers (note the `>` in each header):
```caddy-d
forward_auth unix//run/tailscale.nginx-auth.sock {
uri /auth
header_up Remote-Addr {remote_host}
header_up Remote-Port {remote_port}
header_up Original-URI {uri}
copy_headers {
Tailscale-User>X-Webauth-User
Tailscale-Name>X-Webauth-Name
Tailscale-Login>X-Webauth-Login
Tailscale-Tailnet>X-Webauth-Tailnet
Tailscale-Profile-Picture>X-Webauth-Profile-Picture
}
}
```
---
---
title: fs (Caddyfile directive)
---
# 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](/docs/caddyfile/options#filesystem), 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](file_server) to serve static files, or the [`try_files` directive](try_files) to perform rewrites based on the existence of files. Typically also used with [`root` directive](root) to set the root path within the file system.
## Syntax
```caddy-d
fs []
```
## Examples
Using an file system named `foo`, using an imaginary module named `custom` which might require authentication:
```caddy
{
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:
```caddy
example.com {
fs /images* foo
root /srv
file_server
}
```
---
---
title: handle (Caddyfile directive)
---
# 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](/docs/caddyfile/directives#sorting-algorithm) by their matchers. The [`handle_path`](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.
## Syntax
```caddy-d
handle [] {
}
```
- **** is a list of HTTP handler directives or directive blocks, one per line, just like would be used outside of a handle block.
## Similar directives
There are other directives that can wrap HTTP handler directives, but each has its use depending on the behavior you want to convey:
- [`handle_path`](handle_path) does the same as `handle`, but it strips a prefix from the request before running its handlers.
- [`handle_errors`](handle_errors) is like `handle`, but is only invoked when Caddy encounters an error during request handling.
- [`route`](route) wraps other directives like `handle` does, but with two distinctions:
1. route blocks are not mutually exclusive to each other,
2. directives within a route are not [re-ordered](/docs/caddyfile/directives#directive-order), giving you more control if needed.
## Examples
Handle requests in `/foo/` with the static file server, and other requests with the reverse proxy:
```caddy
example.com {
handle /foo/* {
file_server
}
handle {
reverse_proxy 127.0.0.1:8080
}
}
```
You can mix `handle` and [`handle_path`](handle_path) in the same site, and they will still be mutually exclusive from each other:
```caddy
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:
```caddy
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)
}
}
```
---
---
title: handle_errors (Caddyfile directive)
---
# 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](root) or [vars](vars) will be preserved in error handlers, too. Additionally, [new placeholders](#placeholders) are available when handling errors.
Note that certain directives, for example [`reverse_proxy`](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`](error) directive to explicitly trigger an error based on your own routing decisions.
## Syntax
```caddy-d
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](/docs/caddyfile/directives) and [matchers](/docs/caddyfile/matchers), one per line.
## Placeholders
The following placeholders are available while handling errors. They are [Caddyfile shorthands](/docs/caddyfile/concepts#placeholders) for the full placeholders which can be found in [the JSON docs for an HTTP server's error routes](/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`](file_server) preserves the error's HTTP status code when run in `handle_errors` (assumes you set a [site root](root) in your site beforehand):
```caddy-d
handle_errors {
rewrite * /{err.status_code}.html
file_server
}
```
A single error page that uses [`templates`](templates) to write a custom error message:
```caddy-d
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`](/docs/caddyfile/matchers#file) matcher:
```caddy-d
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 😸:
```caddy-d
handle_errors {
rewrite * /{err.status_code}
reverse_proxy https://http.cat {
header_up Host {upstream_hostport}
replace_status {err.status_code}
}
}
```
Simply use [`respond`](respond) to return the error code and name
```caddy-d
handle_errors {
respond "{err.status_code} {err.status_text}"
}
```
To handle specific error codes differently:
```caddy-d
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`](/docs/caddyfile/matchers#expression) matcher against the status codes, and using [`handle`](handle) for mutual exclusivity:
```caddy-d
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"
}
}
```
---
---
title: handle_path (Caddyfile directive)
---
# handle_path
Works the same as the [`handle` directive](handle), but implicitly uses [`uri strip_prefix`](uri) 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.
## Syntax
```caddy-d
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](/docs/caddyfile/matchers#path-matchers) is accepted, and is required; you cannot use named matchers with `handle_path`.
## Examples
This configuration:
```caddy-d
handle_path /prefix/* {
...
}
```
👆 is effectively the same as this 👇, but the `handle_path` form 👆 is slightly more succinct
```caddy-d
handle /prefix/* {
uri strip_prefix /prefix
...
}
```
A full Caddyfile example, where `handle_path` and `handle` are mutually exclusive; but, be aware of the [subfolder problem ](https://caddy.community/t/the-subfolder-problem-or-why-cant-i-reverse-proxy-my-app-into-a-subfolder/8575)
```caddy
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
}
}
```
---
---
title: header (Caddyfile directive)
---
# 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`](request_header) directive.
## Syntax
```caddy-d
header [] [[+|-|?|>] [|] []] {
# Add
+
# Set
# Set with defer
>
# Delete
-
# Replace
# Replace with defer
>
# Default
?
[defer]
match
}
```
- **<field>** 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 response.
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.
- **<value>** is the header field value, when adding or setting a field.
- **<find>** is the substring or regular expression to search for.
- **<replace>** is the replacement value; required if performing a search-and-replace. Use `$1` or `$2` and so on to reference capture groups from the search pattern. If the replacement value is `""`, then the matching text is removed from the value. See the [Go documentation](https://golang.org/pkg/regexp/#Regexp.Expand) for details.
- **defer** defers the execution of header operations until the response is being sent to the client. This option is automatically enabled under the following conditions:
- When any header fields are deleted using `-`.
- When setting a default value with `?`.
- When using the `>` prefix on a set or replace operation.
- When one or more `match` conditions are present.
- **match** is an inline [response matcher](/docs/caddyfile/response-matchers). Header operations are applied only to responses that satisfy the specified conditions.
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](/docs/modules/http.handlers.headers#response/require), using `?` configures a [response matcher](/docs/caddyfile/response-matchers) 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 responses:
```caddy-d
header Custom-Header "My value"
```
Strip the "Hidden" header field:
```caddy-d
header -Hidden
```
Replace `http://` with `https://` in any Location header:
```caddy-d
header Location http:// https://
```
Set security and privacy headers on all pages: (**WARNING:** only use if you understand the implications!)
```caddy-d
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:
```caddy-d
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:
```caddy-d
header ?Cache-Control "max-age=3600"
reverse_proxy upstream:443
```
Mark all successful responses to GET requests as cacheable for upto an hour:
```caddy-d
@GET method GET
header @GET Cache-Control "max-age=3600" {
match status 2xx
}
reverse_proxy upstream:443
```
Prevent caching of error responses in the event of an exception in the upstream server:
```caddy-d
header {
-Cache-Control
-CDN-Cache-Control
match status 500
}
reverse_proxy upstream:443
```
Mark light mode responses as separately cacheable from dark mode responses if the upstream server supports client hints:
```caddy-d
header {
Cache-Control "max-age=3600"
Vary "Sec-CH-Prefers-Color-Scheme"
match {
header Accept-CH "*Sec-CH-Prefers-Color-Scheme*"
header Critical-CH "Sec-CH-Prefers-Color-Scheme"
}
}
reverse_proxy upstream:443
```
Prevent overly-permissive CORS headers by replacing wildcard values with a specific domain:
```caddy-d
header >Access-Control-Allow-Origin "\*" "allowed-partner.com"
reverse_proxy upstream:443
```
**Note**: In replacement operations, the `` value is interpreted as a regular expression. To match the `*` character, it must be escaped with a backslash as shown in the above example.
Alternatively, you may use a [response matcher](/docs/caddyfile/response-matchers) to match a header value verbatim:
```caddy-d
header Access-Control-Allow-Origin "allowed-partner.com" {
match header Access-Control-Allow-Origin *
}
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:
```caddy-d
header /no-cache* >Cache-Control no-cache
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:
```caddy-d
header >Set-Cookie (.*) "$1; SameSite=None;"
```
---
---
title: import (Caddyfile directive)
---
# import
Includes a [snippet](/docs/caddyfile/concepts#snippets) 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.
## Syntax
```caddy-d
import [] [{block}]
```
- **<pattern>** is the filename, glob pattern, or name of [snippet](/docs/caddyfile/concepts#snippets) 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.
- **<args...>** 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](https://gobyexample.com/slices):
- `{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](/docs/caddyfile/concepts#tokens-and-quotes) 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.
⚠️ Experimental | v2.9.x+
- **{block}** is an optional block to pass to the imported tokens. This placeholder is a special case, and is evaluated recursively at Caddyfile-parse-time, not at runtime. They can be used in two forms:
- `{block}` where the content of the whole provided block will be substituted for the placeholder
- `{blocks.key}` where `key` is the first token of a parameter within the provided block
## Examples
Import all files in an adjacent sites-enabled folder (except hidden files):
```caddy-d
import sites-enabled/*
```
Import a snippet that sets CORS headers using an import argument:
```caddy
(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:
```caddy
(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:
```caddy
(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
}
```
⚠️ Experimental | v2.9.x+
Import a snippet which responds with a configurable "hello world" message and content-type:
```caddy
(hello-world) {
header {
Cache-Control max-age=3600
X-Foo bar
{blocks.content_type}
}
respond /hello-world 200 {
{blocks.body}
}
}
example.com {
import hello-world {
content_type {
Content-Type text/html
}
body {
body "
hello world
"
}
}
}
```
Import a snippet which provides extendable options for a reverse proxy:
```caddy
(extendable-proxy) {
reverse_proxy {
{blocks.proxy_target}
{blocks.proxy_options}
}
}
example.com {
import extendable-proxy {
proxy_target {
to 10.0.0.1
}
proxy_options {
transport http {
tls
}
}
}
}
```
Import a snippet that serves any set of directives, but with a pre-loaded middleware:
```caddy
(instrumented-route) {
header {
Alt-Svc `h3="0.0.0.0:443"; ma=2592000`
}
tracing {
span args[0]
}
{block}
}
example.com {
import instrumented-route example-com {
respond "OK"
}
}
```
---
---
title: intercept (Caddyfile directive)
---
# intercept
A generalized abstraction of the [response interception](reverse_proxy#intercepting-responses) feature from the [`reverse_proxy` directive](reverse_proxy). This may be used with any handler that produces responses, including those from plugins like [FrankenPHP](https://frankenphp.dev/)'s `php_server`.
This directive allows you to [match responses](/docs/caddyfile/response-matchers), and the first matching `handle_response` route or `replace_status` will be invoked. When invoked, the original response body is held back, giving the opportunity to that route to write a different response body, with a new status code or with any necessary response header manipulations. If the route does _not_ write a new response body, then original response body is written instead.
## Syntax
```caddy-d
intercept [] {
@name {
status
header []
}
replace_status []
handle_response [] {
}
}
```
- **@name** is a named [response matcher](/docs/caddyfile/response-matchers) block. 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 the original response is matched by the given response matcher. If a matcher is omitted, all responses are intercepted. When multiple `handle_response` blocks are defined, the first matching block will be applied. Inside the block, all other [directives](/docs/caddyfile/directives) can be used.
Within `handle_response` routes, the following placeholders are available to pull information from the original response:
- `{resp.status_code}` The status code of the original response.
- `{resp.header.*}` The headers from the original response.
## Examples
When using [FrankenPHP](https://frankenphp.dev/)'s `php_server`, you can use `intercept` to implement `X-Accel-Redirect` support, serving static files as requested by the PHP app:
```caddy
localhost {
root * /srv
intercept {
@accel header X-Accel-Redirect *
handle_response @accel {
root * /path/to/private/files
rewrite {resp.header.X-Accel-Redirect}
method GET
file_server
}
}
php_server
}
```
---
---
title: invoke (Caddyfile directive)
---
# invoke
⚠️ Experimental
Invokes a [named route](/docs/caddyfile/concepts#named-routes).
This is useful when paired with HTTP handler directives that have their own in-memory state, or if they are expensive to provision on load. If you have hundreds of sites or more, invoking a named route can help reduce memory usage.
## Syntax
```caddy-d
invoke []
```
- **<route-name>** is the name of the previously defined route that should be invoked. If the route is not found, then an error will be triggered.
## Examples
Defines a [named route](/docs/caddyfile/concepts#named-routes) with a [`reverse_proxy`](/docs/caddyfile/directives/reverse_proxy) which can be reused in multiple sites, with the same in-memory load balancing state reused for every site.
```caddy
&(app-proxy) {
reverse_proxy app-01:8080 app-02:8080 app-03:8080 {
lb_policy least_conn
health_uri /healthz
health_interval 5s
}
}
# Apex domain allows accessing the app via an /app subpath
# and the main site otherwise.
example.com {
handle_path /app* {
invoke app-proxy
}
handle {
root * /srv
file_server
}
}
# The app is also accessible via a subdomain.
app.example.com {
invoke app-proxy
}
```
---
---
title: log (Caddyfile directive)
---
# log
Enables and configures HTTP request logging (also known as access logs).
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](log_skip).
To add custom fields to the log entries, use the [`log_append` directive](log_append).
- [Syntax](#syntax)
- [Output modules](#output-modules)
- [stderr](#stderr)
- [stdout](#stdout)
- [discard](#discard)
- [file](#file)
- [net](#net)
- [Format modules](#format-modules)
- [console](#console)
- [json](#json)
- [filter](#filter)
- [delete](#delete)
- [rename](#rename)
- [replace](#replace)
- [ip_mask](#ip-mask)
- [query](#query)
- [cookie](#cookie)
- [regexp](#regexp)
- [hash](#hash)
- [append](#append)
- [Examples](#examples)
By default, headers with potentially sensitive information (`Cookie`, `Set-Cookie`, `Authorization` and `Proxy-Authorization`) will be logged as `REDACTED` in access logs. This behaviour can be disabled with the [`log_credentials`](/docs/caddyfile/options#log-credentials) global server option.
## Syntax
```caddy-d
log [] {
hostnames
no_hostname
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. See [an example](#multiple-outputs) below.
- **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](/docs/caddyfile/patterns#wildcard-certificates). See [an example](#wildcard-logs) below.
- **no_hostname** prevents the logger from being associated with any of the site block's hostnames. By default, the logger is associated with the [site address](/docs/caddyfile/concepts#addresses) that the `log` directive appears in.
This is useful when you want to log requests to different files based on some condition, such as the request path or method, using the [`log_name` directive](/docs/caddyfile/directives/log_name).
- **output** configures where to write the logs. See [`output` modules](#output-modules) below.
Default: `stderr`.
- **format** describes how to encode, or format, the logs. See [`format` modules](#format-modules) below.
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.
### Output modules
The **output** subdirective lets you customize where logs get written.
#### stderr
Standard error (console, is the default).
```caddy-d
output stderr
```
#### stdout
Standard output (console).
```caddy-d
output stdout
```
#### discard
No output.
```caddy-d
output discard
```
#### file
A file. By default, log files are rotated ("rolled") to prevent disk space exhaustion.
Log rolling is provided by [lumberjack ](https://github.com/natefinch/lumberjack)
```caddy-d
output file {
mode
roll_disabled
roll_size
roll_uncompressed
roll_local_time
roll_keep
roll_keep_for
}
```
- **<filename>** is the path to the log file.
- **mode** is the Unix file mode/permissions to use for the log file. The mode consists of between 1 and 4 octal digits (same as the numeric format accepted by the Unix [chmod ](https://en.wikipedia.org/wiki/Chmod) command, except that an all-zero mode is interpreted as the default mode `600`). For example: `0600` would set the mode to `rw-,---,---` (read/write access to the log file's owner, and no access to anyone else); `0640` would set the mode to `rw-,r--,---` (read/write access to file's owner, only read access to the group); `644` sets the mode to `rw-,r--,r--` provides read/write access to the log file's owner, but only read access to the group owner and other users.
- **roll_disabled** disables log rolling. This can lead to disk space depletion, so only use this if your log files are maintained some other way.
- **roll_size** is the size at which to roll the log file. The current implementation supports megabyte resolution; fractional values are rounded up to the next whole megabyte. For example, `1.1MiB` is rounded up to `2MiB`.
Default: `100MiB`
- **roll_uncompressed** turns off gzip log compression.
Default: gzip compression is enabled.
- **roll_local_time** sets the rolling to use local timestamps in filenames.
Default: uses UTC time.
- **roll_keep** is how many log files to keep before deleting the oldest ones.
Default: `10`
- **roll_keep_for** is how long to keep rolled files as a [duration string](/docs/conventions#durations). The current implementation supports day resolution; fractional values are rounded up to the next whole day. For example, `36h` (1.5 days) is rounded up to `48h` (2 days). Default: `2160h` (90 days)
#### net
A network socket. If the socket goes down, it will dump logs to stderr while it attempts to reconnect.
```caddy-d
output net {
dial_timeout
soft_start
}
```
- **<address>** is the [address](/docs/conventions#network-addresses) to write logs to.
- **dial_timeout** is how long to wait for a successful connection to the log socket. Log emissions may be blocked for up to this long if the socket goes down.
- **soft_start** will ignore errors when connecting to the socket, allowing you to load your config even if the remote log service is down. Logs will be emitted to stderr instead.
### Format modules
The **format** subdirective lets you customize how logs get encoded (formatted). It appears within a `log` block.
In addition to the syntax for each individual encoder, these common properties can be set on most encoders:
```caddy-d
format {
message_key
level_key
time_key
name_key
caller_key
stacktrace_key
line_ending
time_format
time_local
duration_format
level_format
}
```
- **message_key** The key for the message field of the log entry. Default: `msg`
- **level_key** The key for the level field of the log entry. Default: `level`
- **time_key** The key for the time field of the log entry. Default: `ts`
- **name_key** The key for the name field of the log entry. Default: `name`
- **caller_key** The key for the caller field of the log entry.
- **stacktrace_key** The key for the stacktrace field of the log entry.
- **line_ending** The line endings to use.
- **time_format** The format for timestamps.
Default: `wall_milli` if the format defaulted to `console`, `unix_seconds_float` otherwise.
May be one of:
- `unix_seconds_float` Floating-point number of seconds since the Unix epoch.
- `unix_milli_float` Floating-point number of milliseconds since the Unix epoch.
- `unix_nano` Integer number of nanoseconds since the Unix epoch.
- `iso8601` Example: `2006-01-02T15:04:05.000Z0700`
- `rfc3339` Example: `2006-01-02T15:04:05Z07:00`
- `rfc3339_nano` Example: `2006-01-02T15:04:05.999999999Z07:00`
- `wall` Example: `2006/01/02 15:04:05`
- `wall_milli` Example: `2006/01/02 15:04:05.000`
- `wall_nano` Example: `2006/01/02 15:04:05.000000000`
- `common_log` Example: `02/Jan/2006:15:04:05 -0700`
- Or, any compatible time layout string; see the [Go documentation](https://pkg.go.dev/time#pkg-constants) for full details.
Note that the parts of the format string are special constants for the layout; so `2006` is the year, `01` is the month, `Jan` is the month as a string, `02` is the day. Do not use the actual current date numbers in the format string.
- **time_local** Logs with the local system time rather than the default of UTC time.
- **duration_format** The format for durations.
Default: `seconds`.
May be one of:
- `s`, `second` or `seconds` Floating-point number of seconds elapsed.
- `ms`, `milli` or `millis` Floating-point number of milliseconds elapsed.
- `ns`, `nano` or `nanos` Integer number of nanoseconds elapsed.
- `string` Using Go's built-in string format, for example `1m32.05s` or `6.31ms`.
- **level_format** The format for levels.
Default: `color` if the format defaulted to `console`, `lower` otherwise.
May be one of:
- `lower` Lowercase.
- `upper` Uppercase.
- `color` Uppercase, with ANSI colors.
#### console
The console encoder formats the log entry for human readability while preserving some structure.
```caddy-d
format console
```
#### json
Formats each log entry as a JSON object.
```caddy-d
format json
```
#### filter
Allows per-field filtering.
```caddy-d
format filter {
fields {
...
}
...
wrap ...
}
```
Nested fields can be referenced by representing a layer of nesting with `>`. In other words, for an object like `{"a":{"b":0}}`, the inner field can be referenced as `a>b`.
The following fields are fundamental to the log and cannot be filtered because they are added by the underlying logging library as special cases: `ts`, `level`, `logger`, and `msg`.
Specifying `wrap` is optional; if omitted, a default is chosen depending on whether the current output module is [`stderr`](#stderr) or [`stdout`](#stdout), and is an interactive terminal, in which case [`console`](#console) is chosen, otherwise [`json`](#json) is chosen.
As a shortcut, the `fields` block can be omitted and the filters can be specified directly within the `filter` block.
These are the available filters:
##### delete
Marks a field to be skipped from being encoded.
```caddy-d
delete
```
##### rename
Rename the key of a log field.
```caddy-d
rename
```
##### replace
Marks a field to be replaced with the provided string at encoding time.
```caddy-d
replace
```
##### ip_mask
Masks IP addresses in the field using a CIDR mask, i.e. the number of bits from the IP to retain, starting from the left side. If the field is an array of strings (e.g. HTTP headers), each value in the array is masked. The value may be a comma separated string of IP addresses.
There is separate configuration for IPv4 and IPv6 addresses, since they have a different total number of bits.
Most commonly, the fields to filter would be:
- `request>remote_ip` for the directly connecting client
- `request>client_ip` for the parsed "real client" when [`trusted_proxies`](/docs/caddyfile/options#trusted-proxies) is configured
- `request>headers>X-Forwarded-For` if behind a reverse proxy
```caddy-d
ip_mask [ []] {
ipv4
ipv6
}
```
##### query
Marks a field to have one or more actions performed, to manipulate the query part of a URL field. Most commonly, the field to filter would be `request>uri`.
```caddy-d
query {
delete
replace
hash
}
```
The available actions are:
- **delete** removes the given key from the query.
- **replace** replaces the value of the given query key with **replacement**. Useful to insert a redaction placeholder; you'll see that the query key was in the URL, but the value is hidden.
- **hash** replaces the value of the given query key with the first 4 bytes of the SHA-256 hash of the value, lowercase hexadecimal. Useful to obscure the value if it's sensitive, while being able to notice whether each request had a different value.
##### cookie
Marks a field to have one or more actions performed, to manipulate a `Cookie` HTTP header's value. Most commonly, the field to filter would be `request>headers>Cookie`.
```caddy-d
cookie {
delete
replace
hash
}
```
The available actions are:
- **delete** removes the given cookie by name from the header.
- **replace** replaces the value of the given cookie with **replacement**. Useful to insert a redaction placeholder; you'll see that the cookie was in the header, but the value is hidden.
- **hash** replaces the value of the given cookie with the first 4 bytes of the SHA-256 hash of the value, lowercase hexadecimal. Useful to obscure the value if it's sensitive, while being able to notice whether each request had a different value.
If many actions are defined for the same cookie name, only the first action will be applied.
##### regexp
Marks a field to have a regular expression replacement applied at encoding time. If the field is an array of strings (e.g. HTTP headers), each value in the array has replacements applied.
```caddy-d
regexp
```
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).
In the replacement string, capture groups can be referenced with `${group}` where `group` is either the name or number of the capture group in the expression. Capture group `0` is the full regexp match, `1` is the first capture group, `2` is the second capture group, and so on.
##### hash
Marks a field to be replaced with the first 4 bytes (8 hex characters) of the SHA-256 hash of the value at encoding time. If the field is a string array (e.g. HTTP headers), each value in the array is hashed.
Useful to obscure the value if it's sensitive, while being able to notice whether each request had a different value.
```caddy-d
hash
```
#### append
Appends field(s) to all log entries.
```caddy-d
format append {
fields {
}
wrap ...
}
```
It is most useful for adding information about the Caddy instance that is producing the log entries, possibly via an environment variable. The field values may be global placeholders (e.g. `{env.*}`), but _not_ per-request placeholders due to logs being written outside of the HTTP request context.
Specifying `wrap` is optional; if omitted, a default is chosen depending on whether the current output module is [`stderr`](#stderr) or [`stdout`](#stdout), and is an interactive terminal, in which case [`console`](#console) is chosen, otherwise [`json`](#json) is chosen.
The `fields` block can be omitted and the fields can be specified directly within the `append` block.
## 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](/docs/caddyfile/options#log):
```caddy
example.com {
log
}
```
Write logs to a file (with log rolling, which is enabled by default):
```caddy
example.com {
log {
output file /var/log/access.log
}
}
```
Customize log rolling:
```caddy
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:
```caddy
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](/docs/caddyfile/options#log-credentials) to enable logging `Cookie` header values):
```caddy
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`](/docs/caddyfile/options#trusted-proxies) is configured:
```caddy
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:
```caddy
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](/docs/caddyfile/patterns#wildcard-certificates), by overriding `hostnames` for each logger. This uses a [snippet](/docs/caddyfile/concepts#snippets) to avoid repetition:
```caddy
(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"
}
}
```
To write the access logs for a particular subdomain to two different files, with different formats (one with [`transform-encoder` plugin ](https://github.com/caddyserver/transform-encoder) and the other with [`json`](#json)).
This works by overriding the logger name as `foo` in the site block, then including the access logs produced by that logger in the two loggers in global options with `include http.log.access.foo`:
```caddy
{
log access-formatted {
include http.log.access.foo
output file /var/log/access-foo.log
format transform "{common_log}"
}
log access-json {
include http.log.access.foo
output file /var/log/access-foo.json
format json
}
}
foo.example.com {
log foo
}
```
---
---
title: log_append (Caddyfile directive)
---
# log_append
Appends a field to the access log for the current request.
This should be used alongside the [`log` directive](log) which is required to enable access logging in the first place.
The value may be a static string, or a [placeholder](/docs/caddyfile/concepts#placeholders) which will be replaced with the value of the placeholder at the time of the request.
## Syntax
```caddy-d
log_append []
```
## Examples
Display in the logs the area of the site that the request is being served from, either `static` or `dynamic`:
```caddy
example.com {
log
handle /static* {
log_append area "static"
respond "Static response!"
}
handle {
log_append area "dynamic"
reverse_proxy localhost:9000
}
}
```
Display in the logs, which reverse proxy upstream was effectively used (either `node1`, `node2` or `node3`) and
the time spent proxying to the upstream in milliseconds as well as how long it took the proxy upstream to write the response header:
```caddy
example.com {
log
handle {
reverse_proxy node1:80 node2:80 node3:80 {
lb_policy random_choose 2
}
log_append upstream_host {rp.upstream.host}
log_append upstream_duration_ms {rp.upstream.duration_ms}
log_append upstream_latency_ms {rp.upstream.latency_ms}
}
}
```
---
---
title: log_name (Caddyfile directive)
---
# log_name
Overrides the logger name to use for a request when writing access logs with the [`log` directive](log).
This directive is useful when you want to log requests to different files based on some condition, such as the request path or method.
More than one logger name can be specified, such that the request's log gets pushed to more than one matching logger.
This is often paired with the `log` directive's [`no_hostname`](log#no_hostname) option, which prevents the logger from being associated with any of the site block's hostnames, so that only requests that set `log_name` will push logs to that logger.
## Syntax
```caddy-d
log_name []
```
## Examples
You may want to log requests to different files, for example you might want to log health checks to a separate file from the main access logs.
Using `no_hostname` in a `log` prevents the logger from being associated with any of the site block's hostnames (i.e. `localhost` here), so that only requests that have `log_name` set to that logger's name will receive logs.
```caddy
localhost {
log {
output file ./caddy.access.log
}
log health_check_log {
output file ./caddy.access.health.log
no_hostname
}
handle /healthz* {
log_name health_check_log
respond "Healthy"
}
handle {
respond "Hello World"
}
}
```
---
---
title: log_skip (Caddyfile directive)
---
# log_skip
Skips access logging for matched requests.
This should be used alongside the [`log` directive](log) 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.
## Syntax
```caddy-d
log_skip []
```
## Examples
Skip access logging for static files stored in a subpath:
```caddy
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:
```caddy-d
@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:
```caddy-d
handle_path /static* {
root * /srv/static
log_skip
file_server
}
```
---
---
title: map (Caddyfile directive)
---
# map
Sets values of custom placeholders switched on an input value.
It compares the source value against the input side of the map, and for one that matches, it applies the output value(s) to each destination. Destinations become placeholder names. Default output values may also be specified for each destination.
Mapped placeholders are not evaluated until they are used, so even for very large mappings, this directive is quite efficient.
## Syntax
```caddy-d
map [] {
[~]
default
}
```
- **<source>** is the input value to switch on. Usually a placeholder.
- **<destinations...>** are the placeholders to create that hold the output values.
- **<input>** is the input value to match. If prefixed with `~`, it is treated as a regular expression.
- **<outputs...>** is one or more output values to store in the associated placeholder. The first output is written to the first destination, the second output to the second destination, etc.
As a special case, the Caddyfile parser treats outputs that are a literal hyphen (`-`) as null/nil values. This is useful if you want to fall back to a default value for that particular output in the case of the given input, but want to use non-default values for other outputs.
The outputs 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](/docs/caddyfile/concepts#tokens-and-quotes) and they will stay strings.
The number of outputs for each mapping must not exceed the number of destinations; however, for convenience, there may be fewer outputs than destinations, and any missing outputs will be filled in implicitly.
If a regular expression was used as the input, then the capture groups can be referenced with `${group}` where `group` is either the name or number of the capture group in the expression. Capture group `0` is the full regexp match, `1` is the first capture group, `2` is the second capture group, and so on.
- **<default>** specifies the output values to store if no inputs are matched.
## Examples
The following example demonstrates most aspects of this directive:
```caddy-d
map {host} {my_placeholder} {magic_number} {
example.com "some value" 3
foo.example.com "another value"
~(.*)\.example\.com$ "${1} subdomain" 5
~.*\.net$ - 7
~.*\.xyz$ - 15
default "unknown domain" 42
}
```
This directive switches on the value of `{host}`, i.e. the domain name of the request.
- If the request is for `example.com`, set `{my_placeholder}` to `some value`, and `{magic_number}` to `3`.
- Else, if the request is for `foo.example.com`, set `{my_placeholder}` to `another value`, and let `{magic_number}` default to `42`.
- Else, if the request is for any subdomain of `example.com`, set `{my_placeholder}` to a string containing the value of the first regexp capture group, i.e the entire subdomain, and set `{magic_number}` to 5.
- Else, if the request is for any host that ends in `.net` or `.xyz`, set only `{magic_number}` to `7` or `15`, respectively. Leave `{my_placeholder}` unset.
- Else (for all other hosts), the default values will apply: `{my_placeholder}` will be set to `unknown domain` and `{magic_number}` will be set to `42`.
---
---
title: method (Caddyfile directive)
---
# method
Changes the HTTP method on the request.
## Syntax
```caddy-d
method []
```
- **<method>** is the HTTP method to change the request to.
## Examples
Change the method for all requests under `/api` to `POST`:
```caddy-d
method /api* POST
```
---
---
title: metrics (Caddyfile directive)
---
# metrics
Configures a Prometheus metrics exposition endpoint so the gathered metrics can
be exposed for scraping. **Metrics must be [turned on in your global options](/docs/caddyfile/options#metrics) first.**
Note that a `/metrics` endpoint is also attached to the [admin API](/docs/api),
which is not configurable, and is not available when the admin API is disabled.
This endpoint will return metrics in the [Prometheus exposition format](https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format)
or, if negotiated, in the [OpenMetrics exposition format](https://pkg.go.dev/github.com/prometheus/client_golang@v1.9.0/prometheus/promhttp#HandlerOpts)
(`application/openmetrics-text`).
See also [Monitoring Caddy with Prometheus metrics](/docs/metrics).
## Syntax
```caddy-d
metrics [] {
disable_openmetrics
}
```
- **disable_openmetrics** disables OpenMetrics negotiation. Usually not
necessary except when needing to work around parsing bugs.
## Examples
Expose metrics at the default `/metrics` path:
```caddy-d
metrics /metrics
```
Expose metrics at another path:
```caddy-d
metrics /foo/bar/baz
```
Serve metrics at a separate subdomain:
```caddy
metrics.example.com {
metrics
}
```
Disable OpenMetrics negotiation:
```caddy-d
metrics /metrics {
disable_openmetrics
}
```
---
---
title: php_fastcgi (Caddyfile directive)
---
# php_fastcgi
An opinionated directive that proxies requests to a PHP FastCGI server such as php-fpm.
- [Syntax](#syntax)
- [Expanded Form](#expanded-form)
- [Explanation](#explanation)
- [Examples](#examples)
Caddy's [`reverse_proxy`](reverse_proxy) is capable of serving any FastCGI application, but this directive is tailored specifically for PHP apps. This directive is a convenient shortcut, replacing a [longer configuration](#expanded-form).
It expects that any `index.php` at the site root acts as a router. If that is not desirable, either reconfigure the [`try_files` subdirective](#try_files) to modify the default rewrite behaviour, or take the [expanded form](#expanded-form) as a basis and customize it to your needs.
In addition to the subdirectives listed below, this directive also supports all the subdirectives of [`reverse_proxy`](reverse_proxy#syntax). For example, you may enable load balancing and health checks.
**Most modern PHP apps work fine without extra subdirectives or customization.** Subdirectives are usually only used in certain edge cases or with legacy PHP apps.
## Syntax
```caddy-d
php_fastcgi [] {
root
split
index |off
try_files
env []
resolve_root_symlink
capture_stderr
dial_timeout
read_timeout
write_timeout
}
```
- **** are the [addresses](/docs/conventions#network-addresses) of the FastCGI servers. Typically, either a TCP socket, or a unix socket file.
- **root** sets the root folder to the site. It's recommended to always use the [`root` directive](root) in conjunction with `php_fastcgi`, but overriding this can be useful when your PHP-FPM upstream is using a different root than Caddy (see [an example](#docker)). Defaults to the value of the [`root` directive](root) if used, otherwise defaults to Caddy's current working directory.
- **split** sets the substrings for splitting the URI into two parts. The first matching substring will be used to split the "path info" from the path. The first piece is suffixed with the matching substring and will be assumed as the actual resource (CGI script) name. The second piece will be set to PATH_INFO for the CGI script to use. Default: `.php`
- **index** specifies the filename to treat as the directory index file. This affects the file matcher in the [expanded form](#expanded-form). Default: `index.php`. Can be set to `off` to disable rewrite fallback to `index.php` when a matching file is not found.
- **try_files** specifies an override for the default try-files rewrite. See the [`try_files` directive](try_files) for details. Default: `{path} {path}/index.php index.php`.
- **env** sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables. By default, all the relevant FastCGI environment variables are already set (including HTTP headers) but you may add or override variables as needed.
- **resolve_root_symlink** when the [`root`](#root) directory is a symbolic link (symlink), this enables resolving it to its actual value. This is sometimes used as a deployment strategy, by simply swapping the symlink to point to the new version in another directory. Disabled by default to avoid repeated system calls.
- **capture_stderr** enables capturing and logging of any messages sent by the upstream fastcgi server on `stderr`. Logging is done at `WARN` level by default. If the response has a `4xx` or `5xx` status, then the `ERROR` level will be used instead. By default, `stderr` is ignored.
- **dial_timeout** is a [duration value](/docs/conventions#durations) that sets how long to wait when connecting to the upstream socket. Default: `3s`.
- **read_timeout** is a [duration value](/docs/conventions#durations) that sets how long to wait when reading from the FastCGI upstream. Default: no timeout.
- **write_timeout** is a [duration value](/docs/conventions#durations) that sets how long to wait when sending to the FastCGI upstream. Default: no timeout.
Since this directive is an opinionated wrapper over a reverse proxy, you can use any of [`reverse_proxy`](reverse_proxy#syntax)'s subdirectives to customize it.
## Expanded form
The `php_fastcgi` directive (without subdirectives) is the same as the following configuration. Most modern PHP apps work well with this preset. If yours does not, feel free to borrow from this and customize it as needed instead of using the `php_fastcgi` shortcut.
```caddy-d
route {
# Add trailing slash for directory requests
# This redirection is automatically disabled if "{http.request.uri.path}/index.php"
# doesn't appear in the try_files list
@canonicalPath {
file {path}/index.php
not path */
}
redir @canonicalPath {http.request.orig_uri.path}/ 308
# If the requested file does not exist, try index files and assume index.php always exists
@indexFiles file {
try_files {path} {path}/index.php index.php
try_policy first_exist_fallback
split_path .php
}
rewrite @indexFiles {file_match.relative}
# Proxy PHP files to the FastCGI responder
@phpFiles path *.php
reverse_proxy @phpFiles {
transport fastcgi {
split .php
}
}
}
```
### Explanation
- The first section deals with canonicalizing the request path. The goal is to ensure that requests that target a directory on disk actually have the trailing slash `/` added to the request path, so that only a single URL is valid for requests to that directory.
This canonicalization occurs only if the `try_files` subdirective contains `{path}/index.php` (the default).
This is performed by using a request matcher that matches only requests that _don't_ end in a slash, and which map to a directory on disk which contains an `index.php` file, and if it matches, performs an HTTP 308 redirect with the trailing slash appended. So for example, it would redirect a request with path `/foo` to `/foo/` (appending a `/`, to canonicalize the path to the directory), if `/foo/index.php` exists on disk.
- The next section deals with performing path rewrites based on whether a matching file exists on disk. This also has the side-effect of remembering the part of the path after `.php` (if the request path had `.php` in it). This is important for Caddy to correctly set the FastCGI environment variables.
- First, it checks if `{path}` is a file that exists on disk. If so, it rewrites to that path. This essentially short-circuits the rest, and makes sure that requests to files that _do exist_ on disk don't get otherwise rewritten (see next steps below). So if for example you have a `/js/app.js` file on disk, then the request to that path will be kept the same.
- Second, it checks if `{path}/index.php` is a file that exists on disk. If so, it rewrites to that path. For requests to a directory like `/foo/` it'll then look for `/foo//index.php` (which gets normalized to `/foo/index.php`), and rewrite the request to that path if it exists. This behaviour is sometimes useful if you're running another PHP app in a subdirectory of your webroot.
- Lastly, it'll always rewrite to `index.php` (it almost always exists for modern PHP apps). This allows your PHP app to handle any request for paths that _don't_ map to files on disk, by using the `index.php` script as its entrypoint.
- And finally, the last section is what actually proxies the request to your PHP FastCGI (or PHP-FPM) service to actually run your PHP code. The request matcher will only match requests which end in `.php`, so, any file that _isn't_ a PHP script and that _does_ exist on disk, will _not_ be handled by this directive, and will fall through.
The `php_fastcgi` directive is not usually enough on its own. It should almost always be paired with the [`root` directive](root) to set the location of your files on disk (for modern PHP apps, this may be `/var/www/html/public`, where the `public` directory is what contains your `index.php`), and the [`file_server` directive](file_server) to serve your static files (your JS, CSS, images, etc) which aren't otherwise handled by this directive and fell through.
## Examples
Proxy all PHP requests to a FastCGI responder listening at `127.0.0.1:9000`:
```caddy-d
php_fastcgi 127.0.0.1:9000
```
Same, but only for requests under `/blog/`:
```caddy-d
php_fastcgi /blog/* localhost:9000
```
When using PHP-FPM listening via a unix socket:
```caddy-d
php_fastcgi unix//run/php/php8.2-fpm.sock
```
The [`root` directive](root) is almost always used to specify the directory containing the PHP scripts, and the [`file_server` directive](file_server) to serve static files:
```caddy
example.com {
root * /var/www/html/public
php_fastcgi 127.0.0.1:9000
file_server
}
```
When serving multiple PHP apps with Caddy, your webroot for each app must be different so that Caddy can read and serve your static files separately and detect if PHP files exist.
If you're using Docker, often your PHP-FPM containers will have the files mounted at the same root. In that case, the solution is to mount the files to your Caddy container in different directories, then use the [`root` subdirective](#root) to set the root for each container:
```caddy
app1.example.com {
root * /srv/app1/public
php_fastcgi app1:9000 {
root /var/www/html/public
}
file_server
}
app2.example.com {
root * /srv/app2/public
php_fastcgi app2:9000 {
root /var/www/html/public
}
file_server
}
```
For a PHP site which does not use `index.php` as an entrypoint, you may fallback to emitting a `404` error instead. The error may be caught and handled with the [`handle_errors` directive](handle_errors):
```caddy
example.com {
php_fastcgi localhost:9000 {
try_files {path} {path}/index.php =404
}
handle_errors {
respond "{err.status_code} {err.status_text}"
}
}
```
---
---
title: push (Caddyfile directive)
---
# push
Configures the server to pre-emptively send resources to the client using HTTP/2 server push.
Resources can be linked for server push by specifying the Link header(s) of the response. This directive will automatically push resources described by upstream Link headers in these formats:
- `; as=script`
- `; as=script,; as=style`
- `; nopush`
- `;;...`
where `` begins with a forward slash `/` (i.e. is a URI path with the same host). Only same-host resources can be pushed. If a linked resource is external or if it has the `nopush` attribute, it will not be pushed.
By default, push requests will include some headers deemed safe to copy from the original request:
- Accept-Encoding
- Accept-Language
- Accept
- Cache-Control
- User-Agent
as it is assumed many requests would fail without these headers; these do not need to be configured manually.
Push requests are virtualized internally, so they are very lightweight.
## Syntax
```caddy-d
push [] [] {
[GET|HEAD]
headers {
[+] [ []]
-
}
}
```
- **<resource>** is the target URI path to push. If used within the block, may optionally be preceded by the method (GET or POST; GET is default).
- **<headers>** manipulates the headers of the push request using the same syntax as the [`header` directive](/docs/caddyfile/directives/header). Some headers are carried over by default and do not need to be explicitly configured (see above).
## Examples
Push any resources described by `Link` headers in the response:
```caddy-d
push
```
Same, but also push `/resources/style.css` for all requests:
```caddy-d
push * /resources/style.css
```
Push `/foo.jpg` only when `/foo.html` is requested by the client:
```caddy-d
push /foo.html /foo.jpg
```
---
---
title: redir (Caddyfile directive)
---
# 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](/docs/caddyfile/directives#directive-order) is very early.
## Syntax
```caddy-d
redir [] []
```
- **<to>** is the target location. Becomes the response's [`Location` header ](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location).
- **<code>** 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`:
```caddy
www.example.com {
redir https://example.com
}
```
Same, but preserve the existing URI by appending the [`{uri}` placeholder](/docs/caddyfile/concepts#placeholders):
```caddy
www.example.com {
redir https://example.com{uri}
}
```
Same, but permanent:
```caddy
www.example.com {
redir https://example.com{uri} permanent
}
```
Redirect your old `/about-us` page to your new `/about` page:
```caddy
example.com {
redir /about-us /about
reverse_proxy localhost:9000
}
```
---
---
title: request_body (Caddyfile directive)
---
# request_body
Manipulates or sets restrictions on the bodies of incoming requests.
## Syntax
```caddy-d
request_body [] {
max_size
set
}
```
- **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`.
⚠️ Experimental | v2.10.0+
- **set** allows setting the request body to specific content. The content can include placeholders to dynamically insert data.
## Examples
Limit request body sizes to 10 megabytes:
```caddy
example.com {
request_body {
max_size 10MB
}
reverse_proxy localhost:8080
}
```
Set the request body with a JSON structure containing a SQL query:
```caddy
example.com {
handle /jazz {
request_body {
set `\{"statementText":"SELECT name, genre, debut_year FROM artists WHERE genre = 'Jazz'"}`
}
reverse_proxy localhost:8080 {
header_up Content-Type application/json
method POST
rewrite * /execute-sql
}
}
}
```
---
---
title: request_header (Caddyfile directive)
---
# 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](/docs/caddyfile/directives/reverse_proxy#header_up) of `reverse_proxy` instead, as those manipulations are proxy-aware.
To manipulate HTTP response headers, you may use the [`header`](header) directive.
## Syntax
```caddy-d
request_header [] [[+|-] [|] []]
```
- **<field>** 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.
- **<value>** is the header field value, if adding or setting a field.
- **<find>** is the substring or regular expression to search for.
- **<replace>** is the replacement value; required if performing a search-and-replace.
## Examples
Remove the Referer header from the request:
```caddy-d
request_header -Referer
```
Delete all headers containing an underscore from the request:
```caddy-d
request_header -*_*
```
---
---
title: respond (Caddyfile directive)
---
# 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](/docs/caddyfile/directives/header).
## Syntax
```caddy-d
respond [] | [] {
body
close
}
```
- **<status>** 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`
- **<body>** 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.
## Examples
Write an empty 200 status with an empty body to all health checks, and a simple response body to all other requests:
```caddy
example.com {
respond /health-check 200
respond "Hello, world!"
}
```
Write an error response and close the connection:
```caddy
example.com {
respond /secret/* "Access denied" 403 {
close
}
}
```
Write an HTML response, using [heredoc syntax](/docs/caddyfile/concepts#heredocs) to control whitespace, and also setting the `Content-Type` header to match the response body:
```caddy
example.com {
header Content-Type text/html
respond <
Foo
Foo
HTML 200
}
```
---
---
title: reverse_proxy (Caddyfile directive)
---
# reverse_proxy
Proxies requests to one or more backends with configurable transport, load balancing, health checking, request manipulation, and buffering options.
- [Syntax](#syntax)
- [Upstreams](#upstreams)
- [Upstream addresses](#upstream-addresses)
- [Dynamic upstreams](#dynamic-upstreams)
- [SRV](#srv)
- [A/AAAA](#aaaaa)
- [Multi](#multi)
- [Load balancing](#load-balancing)
- [Active health checks](#active-health-checks)
- [Passive health checks](#passive-health-checks)
- [Events](#events)
- [Streaming](#streaming)
- [Headers](#headers)
- [Rewrites](#rewrites)
- [Transports](#transports)
- [The `http` transport](#the-http-transport)
- [The `fastcgi` transport](#the-fastcgi-transport)
- [Intercepting responses](#intercepting-responses)
- [Examples](#examples)
## Syntax
```caddy-d
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_upstream
health_port
health_interval
health_passes
health_fails
health_timeout
health_method
health_status
health_request_body
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 [