# 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:
curl localhost:2019/load \
	-H "Content-Type: application/json" \
	-d @caddy.json
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:
curl localhost:2019/load \
	-H "Content-Type: application/json" \
	-d @caddy.json
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):
curl \
	localhost:2019/config/apps/http/servers/example/routes/0/handle/0/body \
	-H "Content-Type: application/json" \
	-d '"Work smarter, not harder."'
You can verify that it worked with a similar GET request, for example:
curl localhost:2019/config/apps/http/servers/example/routes
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:
curl \
	localhost:2019/config/apps/http/servers/example/routes/0/handle/0/@id \
	-H "Content-Type: application/json" \
	-d '"msg"'
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:
curl \
	localhost:2019/id/msg/body \
	-H "Content-Type: application/json" \
	-d '"Some shortcuts are good."'
And check it again:
curl localhost:2019/id/msg/body
--- --- 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:
curl "http://localhost:2019/load" \
	-H "Content-Type: application/json" \
	-d @caddy.json
Note: curl's `-d` flag removes newlines, so if your config format is sensitive to line breaks (e.g. the Caddyfile), use `--data-binary` instead:
curl "http://localhost:2019/load" \
	-H "Content-Type: text/caddyfile" \
	--data-binary @Caddyfile
## 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:
curl "http://localhost:2019/config/" | jq
{
	"apps": {
		"http": {
			"servers": {
				"myserver": {
					"listen": [
						":443"
					],
					"routes": [
						{
							"match": [
								{
									"host": [
										"example.com"
									]
								}
							],
							"handle": [
								{
									"handler": "file_server"
								}
							]
						}
					]
				}
			}
		}
	}
}
Export just the listener addresses:
curl "http://localhost:2019/config/apps/http/servers/myserver/listen"
[":443"]
## 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:
curl \
	-H "Content-Type: application/json" \
	-d '":8080"' \
	"http://localhost:2019/config/apps/http/servers/myserver/listen"
Add multiple listener addresses:
curl \
	-H "Content-Type: application/json" \
	-d '[":8080", ":5133"]' \
	"http://localhost:2019/config/apps/http/servers/myserver/listen/..."
## 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:
curl -X PUT \
	-H "Content-Type: application/json" \
	-d '":8080"' \
	"http://localhost:2019/config/apps/http/servers/myserver/listen/0"
## 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:
curl -X PATCH \
	-H "Content-Type: application/json" \
	-d '[":8081", ":8082"]' \
	"http://localhost:2019/config/apps/http/servers/myserver/listen"
## 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:
curl -X DELETE "http://localhost:2019/config/"
To stop only one of your HTTP servers:
curl -X DELETE "http://localhost:2019/config/apps/http/servers/myserver"
## 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:
curl "http://localhost:2019/adapt" \
	-H "Content-Type: text/caddyfile" \
	--data-binary @Caddyfile
## 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.
curl "http://localhost:2019/pki/ca/local" | jq
{
	"id": "local",
	"name": "Caddy Local Authority",
	"root_common_name": "Caddy Local Authority - 2022 ECC Root",
	"intermediate_common_name": "Caddy Local Authority - ECC Intermediate",
	"root_certificate": "-----BEGIN CERTIFICATE-----\nMIIB ... gRw==\n-----END CERTIFICATE-----\n",
	"intermediate_certificate": "-----BEGIN CERTIFICATE-----\nMIIB ... FzQ==\n-----END CERTIFICATE-----\n"
}
## 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.
curl "http://localhost:2019/pki/ca/local/certificates"
-----BEGIN CERTIFICATE-----
MIIByDCCAW2gAwIBAgIQViS12trTXBS/nyxy7Zg9JDAKBggqhkjOPQQDAjAwMS4w
...
By75JkP6C14OfU733oElfDUMa5ctbMY53rWFzQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBpDCCAUmgAwIBAgIQTS5a+3LUKNxC6qN3ZDR8bDAKBggqhkjOPQQDAjAwMS4w
...
9M9t0FwCIQCAlUr4ZlFzHE/3K6dARYKusR1ck4A3MtucSSyar6lgRw==
-----END CERTIFICATE-----
## GET /reverse_proxy/upstreams Returns the current status of the configured reverse proxy upstreams (backends) as a JSON document.
curl "http://localhost:2019/reverse_proxy/upstreams" | jq
[
	{"address": "10.0.1.1:80", "num_requests": 4, "fails": 2},
	{"address": "10.0.1.2:80", "num_requests": 5, "fails": 4},
	{"address": "10.0.1.3:80", "num_requests": 3, "fails": 3}
]
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: ![ECH GREASE](/resources/images/ech-grease.png) 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:
git clone "https://github.com/caddyserver/caddy.git"
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:
xcaddy build
To build with plugins, use `--with`:
xcaddy build \
    --with github.com/caddyserver/nginx-adapter
	--with github.com/caddyserver/ntlm-transport@v0.1.1
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:
sudo dpkg-divert --divert /usr/bin/caddy.default --rename /usr/bin/caddy
sudo mv ./caddy /usr/bin/caddy.custom
sudo update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.default 10
sudo update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.custom 50
sudo systemctl restart caddy
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: ![Caddyfile structure](/old/resources/images/caddyfile-visual.png) 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 ![external link](/old/resources/images/external-link.svg)](https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/fileserver/browse.html). Browse templates can use actions from [the standard templates module](/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 [] handle_response [] { # special directives only available in handle_response copy_response [] [] { status } copy_response_headers [] { include exclude } } } ``` ## Upstreams - **<upstreams...>** is a list of upstreams (backends) to which to proxy. - **to** is an alternate way to specify the list of upstreams, one (or more) per line. - **dynamic** configures a _dynamic upstreams_ module. This allows getting the list of upstreams dynamically for every request. See [dynamic upstreams](#dynamic-upstreams) below for a description of standard dynamic upstream modules. Dynamic upstreams are retrieved at every proxy loop iteration (i.e. potentially multiple times per request if load balancing retries are enabled) and will be preferred over static upstreams. If an error occurs, the proxy will fall back to using any statically-configured upstreams. ### Upstream addresses Static upstream addresses can take the form of a URL that contains only scheme and host/port, or a conventional [Caddy network address](/docs/conventions#network-addresses). Valid examples: - `localhost:4000` - `127.0.0.1:4000` - `[::1]:4000` - `http://localhost:4000` - `https://example.com` - `h2c://127.0.0.1` - `example.com` - `unix//var/php.sock` - `unix+h2c//var/grpc.sock` - `localhost:8001-8006` - `[fe80::ea9f:80ff:fe46:cbfd%eth0]:443` By default, connections are made to the upstream over plaintext HTTP. When using the URL form, a scheme can be used to set some [`transport`](#transports) defaults as a shorthand. - Using `https://` as the scheme will use the [`http` transport](#the-http-transport) with [`tls`](#tls) enabled. Additionally, you may need to override the `Host` header such that it matches the TLS SNI value, which is used by servers for routing and certificate selection. See the [HTTPS](#https) section below for more details. - Using `h2c://` as the scheme will use the [`http` transport](#the-http-transport) with [HTTP versions](#versions) set to allow cleartext HTTP/2 connections. - Using `http://` as the scheme is identical to having omitted the scheme, since HTTP is already the default. This syntax is included for symmetry with the other scheme shortcuts. Schemes cannot be mixed, since they modify the common transport configuration (a TLS-enabled transport cannot carry both HTTPS and plaintext HTTP). Any explicit transport configuration will not be overwritten, and omitting schemes or using other ports will not assume a particular transport. When using IPv6 with a zone (e.g. link-local addresses with a specific network interface), a scheme **cannot** be used as a shortcut because the `%` will result in a URL-parse error; configure the transport explicitly instead. When using the [network address](/docs/conventions#network-addresses) form, the network type is specified as a prefix to the upstream address. This cannot be combined with a URL scheme. As a special case, `unix+h2c/` is supported as a shortcut for the `unix/` network plus the same effects as the `h2c://` scheme. Port ranges are supported as a shortcut, which expands to multiple upstreams with the same host. Upstream addresses **cannot** contain paths or query strings, as that would imply simultaneous rewriting the request while proxying, which behavior is not defined or supported. You may use the [`rewrite`](/docs/caddyfile/directives/rewrite) directive should you need this. If the address is not a URL (i.e. does not have a scheme), then [placeholders](/docs/caddyfile/concepts#placeholders) can be used, but this makes the upstream _dynamically static_, meaning that potentially many different backends act as a single, static upstream in terms of health checks and load balancing. We recommend using a [dynamic upstreams](#dynamic-upstreams) module instead, if possible. When using placeholders, a port **must** be included (either by the placeholder replacement, or as a static suffix to the address). ### Dynamic upstreams Caddy's reverse proxy comes standard with some dynamic upstream modules. Note that using dynamic upstreams has implications for load balancing and health checks, depending on specific policy configuration: active health checks do not run for dynamic upstreams; and load balancing and passive health checks are best served if the list of upstreams is relatively stable and consistent (especially with round-robin). Ideally, dynamic upstream modules only return healthy, usable backends. #### SRV Retrieves upstreams from SRV DNS records. ```caddy-d dynamic srv [] { service proto name refresh resolvers dial_timeout dial_fallback_delay } ``` - **<full_name>** is the full domain name of the record to look up (i.e. `_service._proto.name`). - **service** is the service component of the full name. - **proto** is the protocol component of the full name. Either `tcp` or `udp`. - **name** is the name component. Or, if `service` and `proto` are empty, the full domain name to query. - **refresh** is how often to refresh cached results. Default: `1m` - **resolvers** is the list of DNS resolvers to override system resolvers. - **dial_timeout** is the timeout for dialing the query. - **dial_fallback_delay** is how long to wait before spawning an RFC 6555 Fast Fallback connection. Default: `300ms` #### A/AAAA Retrieves upstreams from A/AAAA DNS records. ```caddy-d dynamic a [ ] { name port refresh resolvers dial_timeout dial_fallback_delay versions ipv4|ipv6 } ``` - **name** is the domain name to query. - **port** is the port to use for the backend. - **refresh** is how often to refresh cached results. Default: `1m` - **resolvers** is the list of DNS resolvers to override system resolvers. - **dial_timeout** is the timeout for dialing the query. - **dial_fallback_delay** is how long to wait before spawning an RFC 6555 Fast Fallback connection. Default: `300ms` - **versions** is the list of IP versions to resolve for. Default: `ipv4 ipv6` which correspond to both A and AAAA records respectively. #### Multi Append the results of multiple dynamic upstream modules. Useful if you want redundant sources of upstreams, for example: a primary cluster of SRVs backed up by a secondary cluster of SRVs. ```caddy-d dynamic multi { [...] } ``` - **<source>** is the name of the module for the dynamic upstreams, followed by its configuration. More than one may be specified. ## Load balancing Load balancing is typically used to split traffic between multiple upstreams. By enabling retries, it can also be used with one or more upstreams, to hold requests until a healthy upstream can be selected (e.g. to wait and mitigate errors while rebooting or redeploying an upstream). This is enabled by default, with the `random` policy. Retries are disabled by default. - **lb_policy** is the name of the load balancing policy, along with any options. Default: `random`. For policies that involve hashing, the [highest-random-weight (HRW)](https://en.wikipedia.org/wiki/Rendezvous_hashing) algorithm is used to ensure that a client or request with the same hash key is mapped to the same upstream, even if the list of upstreams change. Some policies support fallback as an option, if noted, in which case they take a [block](/docs/caddyfile/concepts#blocks) with `fallback ` which takes another load balancing policy. For those policies, the default fallback is `random`. Configuring a fallback allows using a secondary policy if the primary does not select one, allowing for powerful combinations. Fallbacks can be nested multiple times if desired. For example, `header` can be used as primary to allow for developers to choose a specific upstream, with a fallback of `first` for all other connections to implement primary/secondary failover. ```caddy-d lb_policy header X-Upstream { fallback first } ``` - `random` randomly chooses an upstream - `random_choose ` selects two or more upstreams randomly, then chooses one with least load (`n` is usually 2) - `first` chooses the first available upstream, from the order they are defined in the config, allowing for primary/secondary failover; remember to enable health checks along with this, otherwise failover will not occur - `round_robin` iterates each upstream in turn - `weighted_round_robin ` iterates each upstream in turn, respecting the weights provided. The amount of weight arguments should match the amount of upstreams configured. Weights should be non-negative integers. For example with two upstreams and weights `5 1`, the first upstream would be selected 5 times in a row before the second upstream is selected once, then the cycle repeats. If zero is used as a weight, this will disable selecting the upstream for new requests. - `least_conn` choose upstream with fewest number of current requests; if more than one host has the least number of requests, then one of those hosts is chosen at random - `ip_hash` maps the remote IP (the immediate peer) to a sticky upstream - `client_ip_hash` maps the client IP to a sticky upstream; this is best paired with the [`servers > trusted_proxies` global option](/docs/caddyfile/options#trusted-proxies) which enables real client IP parsing, otherwise it behaves the same as `ip_hash` - `uri_hash` maps the request URI (path and query) to a sticky upstream - `query [key]` maps a request query to a sticky upstream, by hashing the query value; if the specified key is not present, the fallback policy will be used to select an upstream (`random` by default) - `header [field]` maps a request header to a sticky upstream, by hashing the header value; if the specified header field is not present, the fallback policy will be used to select an upstream (`random` by default) - `cookie [ []]` on the first request from a client (when there's no cookie), the fallback policy will be used to select an upstream (`random` by default), and a `Set-Cookie` header is added to the response (default cookie name is `lb` if not specified). The cookie value is the upstream dial address of the chosen upstream, hashed with HMAC-SHA256 (using `` as the shared secret, empty string if not specified). On subsequent requests where the cookie is present, the cookie value will be mapped to the same upstream if it's available; if not available or not found, a new upstream is selected with the fallback policy, and the cookie is added to the response. If you wish to use a particular upstream for debugging purposes, you may hash the upstream address with the secret, and set the cookie in your HTTP client (browser or otherwise). For example, with PHP, you could run the following to compute the cookie value, where `10.1.0.10:8080` is the address of one of your upstreams, and `secret` is your configured secret. ```php echo hash_hmac('sha256', '10.1.0.10:8080', 'secret'); // cdd96966817dd14a99f47ee17451464f29998da170814a16b483e4c1ff4c48cf ``` You can set the cookie in your browser via the Javascript console, for example to set the cookie named `lb`: ```js document.cookie = "lb=cdd96966817dd14a99f47ee17451464f29998da170814a16b483e4c1ff4c48cf"; ``` - **lb_retries** is how many times to retry selecting available backends for each request if the next available host is down. By default, retries are disabled (zero). If [`lb_try_duration`](#lb_try_duration) is also configured, then retries may stop early if the duration is reached. In other words, the retry duration takes precedence over the retry count. - **lb_try_duration** is a [duration value](/docs/conventions#durations) that defines how long to try selecting available backends for each request if the next available host is down. By default, retries are disabled (zero duration). Clients will wait for up to this long while the load balancer tries to find an available upstream host. A reasonable starting point might be `5s` since the HTTP transport's default dial timeout is `3s`, so that should allow for at least one retry if the first selected upstream cannot be reached; but feel free to experiment to find the right balance for your use case. - **lb_try_interval** is a [duration value](/docs/conventions#durations) that defines how long to wait between selecting the next host from the pool. Default is `250ms`. Only relevant when a request to an upstream host fails. Be aware that setting this to `0` with a non-zero `lb_try_duration` can cause the CPU to spin if all backends are down and latency is very low. - **lb_retry_match** restricts with which requests retries are allowed. A request must match this condition in order to be retried if the connection to the upstream succeeded but the subsequent round-trip failed. If the connection to the upstream failed, a retry is always allowed. By default, only `GET` requests are retried. The syntax for this option is the same as for [named request matchers](/docs/caddyfile/matchers#named-matchers), but without the `@name`. If you only need a single matcher, you may configure it on the same line. For multiple matchers, a block is necessary. ### Active health checks Active health checks perform health checking in the background on a timer. To enable this, `health_uri` or `health_port` are required. - **health_uri** is the URI path (and optional query) for active health checks. - **health_upstream** is the ip:port to use for active health checks, if different from the upstream. This should be used in tandem with `health_header` and `{http.reverse_proxy.active.target_upstream}`. - **health_port** is the port to use for active health checks, if different from the upstream's port. Ignored if `health_upstream` is used. - **health_interval** is a [duration value](/docs/conventions#durations) that defines how often to perform active health checks. Default: `30s`. - **health_passes** is the number of consecutive health checks required before marking the backend as healthy again. Default: `1`. - **health_fails** is the number of consecutive health checks required before marking the backend as unhealthy. Default: `1`. - **health_timeout** is a [duration value](/docs/conventions#durations) that defines how long to wait for a reply before marking the backend as down. Default: `5s`. - **health_method** is the HTTP method to use for the active health check. Default: `GET`. - **health_status** is the HTTP status code to expect from a healthy backend. Can be a 3-digit status code, or a status code class ending in `xx`. For example: `200` (which is the default), or `2xx`. - **health_request_body** is a string representing the request body to send with the active health check. - **health_body** is a substring or regular expression to match on the response body of an active health check. If the backend does not return a matching body, it will be marked as down. - **health_follow_redirects** will cause the health check to follow redirects provided by upstream. By default, a redirect response would cause the health check to count as a fail. - **health_headers** allows specifying headers to set on the active health check requests. This is useful if you need to change the `Host` header, or if you need to provide some authentication to your backend as part of your health checks. ### Passive health checks Passive health checks happen inline with actual proxied requests. To enable this, `fail_duration` is required. - **fail_duration** is a [duration value](/docs/conventions#durations) that defines how long to remember a failed request. A duration > `0` enables passive health checking; the default is `0` (off). A reasonable starting point might be `30s` to balance error rates with responsiveness when bringing an unhealthy upstream back online; but feel free to experiment to find the right balance for your use case. - **max_fails** is the maximum number of failed requests within `fail_duration` that are needed before considering a backend to be down; must be >= `1`; default is `1`. - **unhealthy_status** counts a request as failed if the response comes back with one of these status codes. Can be a 3-digit status code or a status code class ending in `xx`, for example: `404` or `5xx`. - **unhealthy_latency** is a [duration value](/docs/conventions#durations) that counts a request as failed if it takes this long to get a response. - **unhealthy_request_count** is the permissible number of simultaneous requests to a backend before marking it as down. In other words, if a particular backend is currently handling this many requests, then it's considered "overloaded" and other backends will be preferred instead. This should be a reasonably large number; configuring this means that the proxy will have a limit of `unhealthy_request_count × upstreams_count` total simultaneous requests, and any requests after that point will result in an error due to no upstreams being available. ## Events When an upstream transitions from being healthy to unhealthy or vice-versa, [an event](/docs/caddyfile/options#event-options) is emitted. These events can be used to trigger other actions, such as sending a notification or logging a message. The events are as follows: - `healthy` is emitted when an upstream is marked healthy when it was previous unhealthy - `unhealthy` is emitted when an upstream is marked unhealthy when it was previous healthy In both cases, the `host` is included as metadata in the event to identify the upstream that changed state. It can be used as a placeholder with `{event.data.host}` with the `exec` event handler, for example. ## Streaming By default, the proxy partially buffers the response for wire efficiency. The proxy also supports WebSocket connections, performing the HTTP upgrade request then transitioning the connection to a bidirectional tunnel. - **flush_interval** is a [duration value](/docs/conventions#durations) that adjusts how often Caddy should flush the response buffer to the client. By default, no periodic flushing is done. A negative value (typically -1) suggests "low-latency mode" which disables response buffering completely and flushes immediately after each write to the client, and does not cancel the request to the backend even if the client disconnects early. This option is ignored and responses are flushed immediately to the client if one of the following applies from the response: - `Content-Type: text/event-stream` - `Content-Length` is unknown - HTTP/2 on both sides of the proxy, `Content-Length` is unknown, and `Accept-Encoding` is either not set or is "identity" - **request_buffers** will cause the proxy to read up to `` amount of bytes from the request body into a buffer before sending it upstream. This is very inefficient and should only be done if the upstream requires reading request bodies without delay (which is something the upstream application should fix). This accepts all size formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). - **response_buffers** will cause the proxy to read up to `` amount of bytes from the response body to be read into a buffer before being returned to the client. This should be avoided if at all possible for performance reasons, but could be useful if the backend has tighter memory constraints. This accepts all size formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). - **stream_timeout** is a [duration value](/docs/conventions#durations) after which streaming requests such as WebSockets will be forcibly closed at the end of the timeout. This essentially cancels connections if they stay open too long. A reasonable starting point might be `24h` to cull connections older than a day. Default: no timeout. - **stream_close_delay** is a [duration value](/docs/conventions#durations) which delays streaming requests such as WebSockets from being forcibly closed when the config is unloaded; instead, the stream will remain open until the delay is complete. In other words, enabling this prevents streams from immediately closing when Caddy's config is reloaded. Enabling this may be a good idea to avoid a thundering herd of reconnecting clients which had their connections closed by the previous config closing. A reasonable starting point might be something like `5m` to allow users 5 minutes to leave the page naturally after a config reload. Default: no delay. ## Headers The proxy can **manipulate headers** between itself and the backend: - **header_up** sets, adds (with the `+` prefix), deletes (with the `-` prefix), or performs a replacement (by using two arguments, a search and replacement) in a request header going upstream to the backend. - **header_down** sets, adds (with the `+` prefix), deletes (with the `-` prefix), or performs a replacement (by using two arguments, a search and replacement) in a response header coming downstream from the backend. For example, to set a request header, overwriting any existing values: ```caddy-d header_up Some-Header "the value" ``` To add a response header; note that there can be multiple values for a header field: ```caddy-d header_down +Some-Header "first value" header_down +Some-Header "second value" ``` To delete a request header, preventing it from reaching the backend: ```caddy-d header_up -Some-Header ``` To delete all matching request headers, using a suffix match: ```caddy-d header_up -Some-* ``` To delete _all_ request headers, to be able to individually add the ones you want (not recommended): ```caddy-d header_up -* ``` To perform a regular expression replacement on a request header: ```caddy-d header_up Some-Header "^prefix-([A-Za-z0-9]*)$" "replaced-$1-suffix" ``` The regular expression language used is RE2, included in Go. See the [RE2 syntax reference](https://github.com/google/re2/wiki/Syntax) and the [Go regexp syntax overview](https://pkg.go.dev/regexp/syntax). The replacement string is [expanded](https://pkg.go.dev/regexp#Regexp.Expand), allowing use of captured values, for example `$1` being the first capture group. ### Defaults By default, Caddy passes through incoming headers—including `Host`—to the backend without modifications, with three exceptions: - It sets or augments the [`X-Forwarded-For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) header field. - It sets the [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) header field. - It sets the [`X-Forwarded-Host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host) header field. For these `X-Forwarded-*` headers, by default, the proxy will ignore their values from incoming requests, to prevent spoofing. If Caddy is not the first server being connected to by your clients (for example when a CDN is in front of Caddy), you may configure `trusted_proxies` with a list of IP ranges (CIDRs) from which incoming requests are trusted to have sent good values for these headers. It is strongly recommended that you configure this via the [`servers > trusted_proxies` global option](/docs/caddyfile/options#trusted-proxies) instead of in the proxy, so that this applies to all proxy handlers in your server, and this has the benefit of enabling client IP parsing. Additionally, when using the [`http` transport](#the-http-transport), the `Accept-Encoding: gzip` header will be set, if it is missing in the request from the client. This allows the upstream to serve compressed content if it can. This behavior can be disabled with [`compression off`](#compression) on the transport. ### HTTPS Since (most) headers retain their original value when being proxied, it is often necessary to override the `Host` header with the configured upstream address when proxying to HTTPS, such that the `Host` header matches the TLS ServerName value: ```caddy-d reverse_proxy https://example.com { header_up Host {upstream_hostport} } ``` The `X-Forwarded-Host` header is still passed [by default](#defaults), so the upstream may still use that if it needs to know the original `Host` header value. The same applies when terminating TLS in caddy and proxying via HTTP, whether to a port or a unix socket. Indeed, caddy itself must receive the correct Host, when it is the target of `reverse_proxy`. In the unix socket case, the `upstream_hostport` will be the socket path, and the Host must be set explicitly. ## Rewrites By default, Caddy performs the upstream request with the same HTTP method and URI as the incoming request, unless a rewrite was performed in the middleware chain before it reaches `reverse_proxy`. Before proxying it, the request is cloned; this ensures that any modifications done to the request during the handler do not leak to other handlers. This is useful in situations where the handling needs to continue after the proxy. In addition to [header manipulations](#headers), the request's method and URI may be changed before it is sent to the upstream: - **method** changes the HTTP method of the cloned request. If the method is changed to `GET` or `HEAD`, then the incoming request's body will _not_ be sent upstream by this handler. This is useful if you wish to allow a different handler to consume the request body. - **rewrite** changes the URI (path and query) of the cloned request. This is similar to the [`rewrite` directive](/docs/caddyfile/directives/rewrite), except that it doesn't persist the rewrite past the scope of this handler. These rewrites are often useful for a pattern like "pre-check requests", where a request is sent to another server to help make a decision on how to continue handling the current request. For example, the request could be sent to an authentication gateway to decide whether the request was from an authenticated user (e.g. the request has a session cookie) and should continue, or should instead be redirected to a login page. For this pattern, Caddy provides a shortcut directive [`forward_auth`](/docs/caddyfile/directives/forward_auth) to skip most of the config boilerplate. ## Transports Caddy's proxy **transport** is pluggable: - **transport** defines how to communicate with the backend. Default is `http`. ### The `http` transport ```caddy-d transport http { read_buffer write_buffer max_response_header proxy_protocol v1|v2 dial_timeout dial_fallback_delay response_header_timeout expect_continue_timeout resolvers tls tls_client_auth | tls_insecure_skip_verify tls_curves tls_timeout tls_trust_pool tls_server_name tls_renegotiation tls_except_ports keepalive [off|] keepalive_interval keepalive_idle_conns keepalive_idle_conns_per_host versions compression off max_conns_per_host network_proxy } ``` - **read_buffer** is the size of the read buffer in bytes. It accepts all formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). Default: `4KiB`. - **write_buffer** is the size of the write buffer in bytes. It accepts all formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). Default: `4KiB`. - **max_response_header** is the maximum amount of bytes to read from response headers. It accepts all formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). Default: `10MiB`. - **proxy_protocol** enables [PROXY protocol](https://github.com/haproxy/haproxy/blob/master/doc/proxy-protocol.txt) (popularized by HAProxy) on the connection to the upstream, prepending the real client IP data. This is best paired with the [`servers > trusted_proxies` global option](/docs/caddyfile/options#trusted-proxies) if Caddy is behind another proxy. Versions `v1` and `v2` are supported. This should only be used if you know the upstream server is able to parse PROXY protocol. By default, this is disabled. - **dial_timeout** is the maximum [duration](/docs/conventions#durations) to wait when connecting to the upstream socket. Default: `3s`. - **dial_fallback_delay** is the maximum [duration](/docs/conventions#durations) to wait before spawning an RFC 6555 Fast Fallback connection. A negative value disables this. Default: `300ms`. - **response_header_timeout** is the maximum [duration](/docs/conventions#durations) to wait for reading response headers from the upstream. Default: No timeout. - **expect_continue_timeout** is the maximum [duration](/docs/conventions#durations) to wait for the upstreams's first response headers after fully writing the request headers if the request has the header `Expect: 100-continue`. Default: No timeout. - **read_timeout** is the maximum [duration](/docs/conventions#durations) to wait for the next read from the backend. Default: No timeout. - **write_timeout** is the maximum [duration](/docs/conventions#durations) to wait for the next writes to the backend. Default: No timeout. - **resolvers** is a list of DNS resolvers to override system resolvers. - **tls** uses HTTPS with the backend. This will be enabled automatically if you specify backends using the `https://` scheme, or if any of the below `tls_*` options are configured. - **tls_client_auth** enables TLS client authentication one of two ways: (1) by specifying a domain name for which Caddy should obtain a certificate and keep it renewed, or (2) by specifying a certificate and key file to present for TLS client authentication with the backend. - **tls_insecure_skip_verify** turns off TLS handshake verification, making the connection insecure and vulnerable to man-in-the-middle attacks. _Do not use in production._ - **tls_curves** is a list of elliptic curves to support for the upstream connection. Caddy's defaults are modern and secure, so you should only need to configure this if you have specific requirements. - **tls_timeout** is the maximum [duration](/docs/conventions#durations) to wait for the TLS handshake to complete. Default: No timeout. - **tls_trust_pool** configures the source of trusted certificate authorities similar to the [`trust_pool` sub-directive](/docs/caddyfile/directives/tls#trust_pool) described on the `tls` directive documentation. The list of trust pool sources available in standard Caddy installation is available [here](/docs/caddyfile/directives/tls#trust-pool-providers). - **tls_server_name** sets the server name used when verifying the certificate received in the TLS handshake. By default, this will use the upstream address' host part. You only need to override this if your upstream address does not match the certificate the upstream is likely to use. For example if the upstream address is an IP address, then you would need to configure this to the hostname being served by the upstream server. A request placeholder may be used, in which case a clone of the HTTP transport config will be used on every request, which may incur a performance penalty. - **tls_renegotiation** sets the TLS renegotiation level. TLS renegotiation is the act of performing subsequent handshakes after the first. The level may be one of: - `never` (the default) disables renegotiation. - `once` allows a remote server to request renegotiation once per connection. - `freely` allows a remote server to repeatedly request renegotiation. - **tls_except_ports** when TLS is enabled, if the upstream target uses one of the given ports, TLS will be disabled for those connections. This may be useful when configuring dynamic upstreams, where some upstreams expect HTTP and others expect HTTPS requests. - **keepalive** is either `off` or a [duration value](/docs/conventions#durations) that specifies how long to keep connections open (timeout). Default: `2m`. ⚠️ Requests to HTTP/1.1 upstreams may fail due to "connection reset by peer" errors if the keepalive duration exceeds the upstream server's keepalive timeout. Idempotent requests will be retried by Go's HTTP transport, but Caddy will respond with status code 502 in other cases. - **keepalive_interval** is the [duration](/docs/conventions#durations) between liveness probes. Default: `30s`. - **keepalive_idle_conns** defines the maximum number of connections to keep alive. Default: No limit. - **keepalive_idle_conns_per_host** if non-zero, controls the maximum idle (keep-alive) connections to keep per-host. Default: `32`. - **versions** allows customizing which versions of HTTP to support. Valid options are: `1.1`, `2`, `h2c`, `3`. Default: `1.1 2`, or if the [upstream's scheme](#upstream-addresses) is `h2c://`, then the default is `h2c 2`. `h2c` enables cleartext HTTP/2 connections to the upstream. This is a non-standard feature that does not use Go's default HTTP transport, so it is exclusive of other features. `3` enables HTTP/3 connections to the upstream. ⚠️ This is an experimental feature and is subject to change. - **compression** can be used to disable compression to the backend by setting it to `off`. - **max_conns_per_host** optionally limits the total number of connections per host, including connections in the dialing, active, and idle states. Default: No limit. - **network_proxy** specifies the name of a network proxy module to use for requests to the upstream server. If not explicitly configured, Caddy respects proxy configured via environment variables as per the [Go stdlib](https://pkg.go.dev/golang.org/x/net/http/httpproxy#FromEnvironment), i.e. `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`. When a value is provided for this parameter, requests will flow through the reverse proxy in the following order: Client (users) → `reverse_proxy` → `network_proxy` → upstream. Built-in modules are: - `none`, which is used to ignore the environment settings of `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`. - `url `, which is used to specify a single URL overriding the environment configuration. ### The `fastcgi` transport ```caddy-d transport fastcgi { root split env resolve_root_symlink dial_timeout read_timeout write_timeout capture_stderr } ``` - **root** is the root of the site. Default: `{http.vars.root}` or current working directory. - **split** is where to split the path to get PATH_INFO at the end of the URI. - **env** sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables. - **resolve_root_symlink** enables resolving the `root` directory to its actual value by evaluating a symbolic link, if one exists. - **dial_timeout** is how long to wait when connecting to the upstream socket. Accepts [duration values](/docs/conventions#durations). Default: `3s`. - **read_timeout** is how long to wait when reading from the FastCGI server. Accepts [duration values](/docs/conventions#durations). Default: no timeout. - **write_timeout** is how long to wait when sending to the FastCGI server. Accepts [duration values](/docs/conventions#durations). Default: no timeout. - **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. ## Intercepting responses The reverse proxy can be configured to intercept responses from the backend. To facilitate this, [response matchers](/docs/caddyfile/response-matchers) can be defined (similar to the syntax for request matchers) and the first matching `handle_response` route will be invoked. When a response handler is invoked, the response from the backend is not written to the client, and the configured `handle_response` route will be executed instead, and it is up to that route to write a response. If the route does _not_ write a response, then request handling will continue with any handlers that are [ordered after](/docs/caddyfile/directives#directive-order) this `reverse_proxy`. - **@name** is the name of a [response matcher](/docs/caddyfile/response-matchers). As long as each response matcher has a unique name, multiple matchers can be defined. A response can be matched on the status code and presence or value of a response header. - **replace_status** simply changes the status code of response when matched by the given matcher. - **handle_response** defines the route to execute when matched by the given matcher (or, if a matcher is omitted, all responses). The first matching block will be applied. Inside a `handle_response` block, any other [directives](/docs/caddyfile/directives) can be used. Additionally, inside `handle_response`, two special handler directives may be used: - **copy_response** copies the response body received from the backend back to the client. Optionally allows changing the status code of the response while doing so. This directive is [ordered before `respond`](/docs/caddyfile/directives#directive-order). - **copy_response_headers** copies the response headers from the backend to the client, optionally including _OR_ excluding a list of headers fields (cannot specify both `include` and `exclude`). This directive is [ordered after `header`](/docs/caddyfile/directives#directive-order). Three placeholders will be made available within the `handle_response` routes: - `{rp.status_code}` The status code from the backend's response. - `{rp.status_text}` The status text from the backend's response. - `{rp.header.*}` The headers from the backend's response. While the reverse proxy response handler can copy the new response received from the proxy back to the client, it cannot pass on that new response to a subsequent reverse proxy. Every use of `reverse_proxy` receives the body from the original request (or as modified with a different module). ## Examples Reverse proxy all requests to a local backend: ```caddy example.com { reverse_proxy localhost:9005 } ``` [Load-balance](#load-balancing) all requests [between 3 backends](#upstreams): ```caddy example.com { reverse_proxy node1:80 node2:80 node3:80 } ``` Same, but only requests within `/api`, and sticky by using the [`cookie` policy](#lb_policy): ```caddy example.com { reverse_proxy /api/* node1:80 node2:80 node3:80 { lb_policy cookie api_sticky } } ``` Using [active health checks](#active-health-checks) to determine which backends are healthy, and enabling [retries](#lb_try_duration) on failed connections, holding the request until a healthy backend is found: ```caddy example.com { reverse_proxy node1:80 node2:80 node3:80 { health_uri /healthz lb_try_duration 5s } } ``` Configure some [transport options](#transports): ```caddy example.com { reverse_proxy localhost:8080 { transport http { dial_timeout 2s response_header_timeout 30s } } } ``` Reverse proxy to an [HTTPS upstream](#https): ```caddy example.com { reverse_proxy https://example.com { header_up Host {upstream_hostport} } } ``` Reverse proxy to an HTTPS upstream, but [⚠️ disable TLS verification](#tls_insecure_skip_verify). This is NOT RECOMMENDED, since it disables all security checks that HTTPS offers; proxying over HTTP in private networks is preferred if possible, because it avoids the false sense of security: ```caddy example.com { reverse_proxy 10.0.0.1:443 { transport http { tls_insecure_skip_verify } } } ``` Instead you may establish trust with the upstream by explicitly [trusting the upstream's certificate](#tls_trust_pool), and (optionally) setting TLS-SNI to match the hostname in the upstream's certificate: ```caddy example.com { reverse_proxy 10.0.0.1:443 { transport http { tls_trust_pool file /path/to/cert.pem tls_server_name app.example.com } } } ``` [Strip a path prefix](handle_path) before proxying; 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 { handle_path /prefix/* { reverse_proxy localhost:9000 } } ``` Replace a path prefix before proxying, using a [`rewrite`](/docs/caddyfile/directives/rewrite): ```caddy example.com { handle_path /old-prefix/* { rewrite * /new-prefix{path} reverse_proxy localhost:9000 } } ``` `X-Accel-Redirect` support, i.e. serving static files as requested, by [intercepting the response](#intercepting-responses): ```caddy example.com { reverse_proxy localhost:8080 { @accel header X-Accel-Redirect * handle_response @accel { root * /path/to/private/files rewrite * {rp.header.X-Accel-Redirect} method * GET file_server } } } ``` Custom error page for errors from upstream, by [intercepting error responses](#intercepting-responses) by status code: ```caddy example.com { reverse_proxy localhost:8080 { @error status 500 503 handle_response @error { root * /path/to/error/pages rewrite * /{rp.status_code}.html file_server } } } ``` Get backends [dynamically](#dynamic-upstreams) from [`A`/`AAAA` record](#aaaaa) DNS queries: ```caddy example.com { reverse_proxy { dynamic a example.com 9000 } } ``` Get backends [dynamically](#dynamic-upstreams) from [`SRV` record](#srv) DNS queries: ```caddy example.com { reverse_proxy { dynamic srv _api._tcp.example.com } } ``` Using [active health checks](#active-health-checks) and `health_upstream` can be helpful when creating an intermediate service to do a more thorough health check. `{http.reverse_proxy.active.target_upstream}` can then be used as a header to provide the original upstream to the health check service. ```caddy example.com { reverse_proxy node1:80 node2:80 node3:80 { health_uri /health health_upstream 127.0.0.1:53336 health_headers { Full-Upstream {http.reverse_proxy.active.target_upstream} } } } ``` --- --- title: rewrite (Caddyfile directive) --- # rewrite Rewrites the request URI internally. A rewrite changes some or all of the request URI. Note that the URI does not include scheme or authority (host & port), and clients typically do not send fragments. Thus, this directive is mostly used for **path** and **query** string manipulation. The `rewrite` directive implies the intent to accept the request, but with modifications. It is mutually exclusive to other `rewrite` directives in the same block, so it is safe to define rewrites that would otherwise cascade into each other as only the first matching rewrite will be executed. A [request matcher](/docs/caddyfile/matchers) that matches a request before the `rewrite` might not match the same request after the `rewrite`. If you want your `rewrite` to share a route with other handlers, use the [`route`](route) or [`handle`](handle) directives. ## Syntax ```caddy-d rewrite [] ``` - **<to>** is the URI to rewrite the request to. Only the components of the URI (path or query string) that are specified in the rewrite will be operated on. The URI path is any substring that comes before `?`. If `?` is omitted, then the whole token is considered to be the path. Prior to v2.8.0, the `` argument could be confused by the parser for a [matcher token](/docs/caddyfile/matchers#syntax) if it began with `/`, so it was necessary to specify a wildcard matcher token (`*`). ## Similar directives There are other directives that perform rewrites, but imply a different intent or do the rewrite without a complete replacement of the URI: - [`uri`](uri) manipulates a URI (strip prefix, suffix, or substring replacement). - [`try_files`](try_files) rewrites the request based on the existence of files. ## Examples Rewrite all requests to `index.html`, leaving any query string unchanged: ```caddy example.com { rewrite * /index.html } ``` Prefixing all requests with `/api`, preserving the rest of the URI, then reverse proxying to an app: ```caddy api.example.com { rewrite * /api{uri} reverse_proxy localhost:8080 } ``` Replace the query string on API requests with `a=b`, leaving the path unchanged: ```caddy example.com { rewrite * ?a=b } ``` For only requests to `/api/`, preserve the existing query string and add a key-value pair: ```caddy example.com { rewrite /api/* ?{query}&a=b } ``` Change both the path and query string, preserving the original query string while adding the original path as the `p` parameter: ```caddy example.com { rewrite * /index.php?{query}&p={path} } ``` --- --- title: root (Caddyfile directive) --- # root Sets the root path of the site, used by various matchers and directives that access the file system. If unset, the default site root is the current working directory. Specifically, this directive sets the `{http.vars.root}` placeholder. It is mutually exclusive to other `root` directives in the same block, so it is safe to define multiple roots with matchers that intersect: they will not cascade and overwrite each other. This directive does not automatically enable serving static files, so it is often used in conjunction with the [`file_server` directive](file_server) or the [`php_fastcgi` directive](php_fastcgi). ## Syntax ```caddy-d root [] ``` - **<path>** is the path to use for the site root. Prior to v2.8.0, the `` argument could be confused by the parser for a [matcher token](/docs/caddyfile/matchers#syntax) if it began with `/`, so it was necessary to specify a wildcard matcher token (`*`). ## Examples Set the site root to `/home/bob/public_html` (assumes Caddy is running as the user `bob`): ```caddy-d root * /home/bob/public_html ``` Set the site root to `public_html` (relative to current working directory) for all requests: ```caddy-d root public_html ``` Change the site root only for requests in `/foo/*`: ```caddy-d root /foo/* /home/user/public_html/foo ``` The `root` directive is commonly paired with [`file_server`](file_server) to serve static files and/or with [`php_fastcgi`](php_fastcgi) to serve a PHP site: ```caddy example.com { root * /srv file_server } ``` --- --- title: route (Caddyfile directive) --- # route Evaluates a group of directives literally and as a single unit. Directives contained in a route block will not be [reordered internally](/docs/caddyfile/directives#directive-order). Only HTTP handler directives (directives which add handlers or middleware to the chain) can be used in a route block. This directive is a special case in that its subdirectives are also regular directives. ## Syntax ```caddy-d route [] { } ``` - **** is a list of directives or directive blocks, one per line, just like outside of a route block; except these directives will not be reordered. Only HTTP handler directives can be used. ## Utility The `route` directive is helpful in certain advanced use cases or edge cases to take absolute control over parts of the HTTP handler chain. Because the order of HTTP middleware evaluation is significant, the Caddyfile will normally reorder directives after parsing to make the Caddyfile easier to use; you don't have to worry about what order you type things. While the [built-in order](/docs/caddyfile/directives#directive-order) is compatible with most sites, sometimes you need to take manual control over the order, either for the whole site or just a part of it. That's what the `route` directive is for. To illustrate, consider the case of two terminating handlers: [`redir`](redir) and [`file_server`](file_server). Both write the response to the client and do not call the next handler in the chain, so only one of these will be executed for a certain request. So which comes first? Normally, `redir` is executed before `file_server` because usually you would want to issue a redirect only in specific cases and serve files in the general case. However, there may be occasions where the second directive (`redir`) has a more specific matcher than the second (`file_server`). In other words, you want to redirect in the general case, and serve only a specific file. So you might try a Caddyfile like this (but this will not work as expected!): ```caddy example.com { file_server /specific.html redir https://anothersite.com{uri} } ``` The problem is that after the [directives are sorted](/docs/caddyfile/directives#sorting-algorithm), `redir` comes before `file_server`. But in this case the matcher for `redir` (an implicit [`*`](/docs/caddyfile/matchers#wildcard-matchers)) is a superset of the matcher for `file_server` (`*` is a superset of `/specific.html`). Fortunately, the solution is easy: just wrap those two directives in a `route` block, to ensure that `file_server` is executed before `redir`: ```caddy example.com { route { file_server /specific.html redir https://anothersite.com{uri} } } ``` And now `file_server` will be chained in before `redir` because the order is taken literally. ## 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`](handle) wraps other directives like `route` does, but with two distinctions: 1) handle blocks are mutually exclusive to each other, and 2) directives within a handle are [re-ordered](/docs/caddyfile/directives#directive-order) normally. - [`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. ## Examples Proxy requests to `/api` as-is, and rewrite all other requests based on whether they match a file on disk, otherwise `/index.html`. Then that file is served. Since [`try_files`](try_files) has a higher directive order than [`reverse_proxy`](reverse_proxy), then it would normally get sorted higher and run first; this would cause the API requests to all get rewritten to `/index.html` and fail to match `/api*`, so none of them would get proxied and instead would result in a `404` from [`file_server`](file_server). Wrapping it all in a `route` ensures that `reverse_proxy` always runs first, before the request is rewritten. ```caddy example.com { root * /srv route { reverse_proxy /api* localhost:9000 try_files {path} /index.html file_server } } ``` --- --- title: templates (Caddyfile directive) --- # templates Executes the response body as a [template](/docs/modules/http.handlers.templates) document. Templates provide functional primitives for making simple dynamic pages. Features include HTTP subrequests, HTML file includes, Markdown rendering, JSON parsing, basic data structures, randomness, time, and more. ## Syntax ```caddy-d templates [] { mime between root } ``` - **mime** are the MIME types the templates middleware will act on; any responses that do not have a qualifying `Content-Type` will not be evaluated as templates. Default: `text/html text/plain`. - **between** are the opening and closing delimiters for template actions. You can change them if they interfere with the rest of your document. Default: `{{printf "{{ }}"}}`. - **root** is the site root, when using functions that access the file system. Defaults to the site root set by the [`root`](root) directive, or the current working directory if not set. Documentation for the built-in template functions can be found in [templates module](/docs/modules/http.handlers.templates#docs). ## Examples For a complete example of a site using templates to serve markdown, take a look at the source for [this very website](https://github.com/caddyserver/website)! Specifically, take a look at the [`Caddyfile`](https://github.com/caddyserver/website/blob/master/Caddyfile) and [`src/docs/index.html`](https://github.com/caddyserver/website/blob/master/src/docs/index.html). Enable templates for a static site: ```caddy example.com { root * /srv templates file_server } ``` To serve a simple static response using a template, make sure to set `Content-Type`: ```caddy example.com { header Content-Type text/plain templates respond `Current year is: {{printf "{{"}}now | date "2006"{{printf "}}"}}` } ``` --- --- title: tls (Caddyfile directive) --- # tls Configures TLS for the site. **Caddy's default TLS settings are secure. Only change these settings if you have a good reason and understand the implications.** The most common use of this directive will be to specify an ACME account email address, change the ACME CA endpoint, or to provide your own certificates. Compatibility note: Due to its sensitive nature as a security protocol, deliberate adjustments to TLS defaults may be made in new minor or patch releases. Old or broken TLS versions, ciphers, features, etc. may be removed at any time. If your deployment is extremely sensitive to changes, you should explicitly specify those values which must remain constant, and be vigilant about upgrades. In almost every case, we recommend using the default settings. ## Syntax ```caddy-d tls [internal|] | [ ] { protocols [] ciphers curves alpn load ca ca_root key_type ed25519|p256|p384|rsa2048|rsa4096 dns [] propagation_timeout propagation_delay dns_ttl dns_challenge_override_domain resolvers eab on_demand reuse_private_keys client_auth { mode [request|require|verify_if_given|require_and_verify] trust_pool verifier } issuer [] get_certificate [] insecure_secrets_log } ``` - **internal** means to use Caddy's internal, locally-trusted CA to produce certificates for this site. To further configure the [`internal`](#internal) issuer, use the [`issuer`](#issuer) subdirective. - **<email>** is the email address to use for the ACME account managing the site's certificates. You may prefer to use the [`email` global option](/docs/caddyfile/options#email) instead, to configure this for all your sites at once. - **<cert_file>** and **<key_file>** are the paths to the certificate and private key PEM files. Specifying just one is invalid. - **protocols** specifies the minimum and maximum protocol versions. DO NOT change these unless you know what you're doing. Configuring this is rarely necessary, because Caddy will always use modern defaults. Default min: `tls1.2`, Default max: `tls1.3` - **ciphers** specifies the list of cipher suite names in descending preference order. DO NOT change these unless you know what you're doing. Note that cipher suites are not customizable for TLS 1.3; and not all TLS 1.2 ciphers are enabled by default. The supported names are (in order of preference by the Go stdlib): - `TLS_AES_128_GCM_SHA256` - `TLS_CHACHA20_POLY1305_SHA256` - `TLS_AES_256_GCM_SHA384` - `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` - `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` - `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` - `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384` - `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` - `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` - `TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA` - `TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA` - `TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA` - `TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA` - `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA` - **curves** specifies the list of EC groups to support. It is recommended to not change the defaults. Supported values are: - `x25519mlkem768` (PQC) - `x25519` - `secp256r1` - `secp384r1` - `secp521r1` - **alpn** is the list of values to advertise in the [ALPN extension ](https://developer.mozilla.org/en-US/docs/Glossary/ALPN) of the TLS handshake. - **load** specifies a list of folders from which to load PEM files that are certificate+key bundles. - **ca** changes the ACME CA endpoint. This is most often used to set [Let's Encrypt's staging endpoint ](https://letsencrypt.org/docs/staging-environment/) when testing, or an internal ACME server. (To change this value for the whole Caddyfile, use the `acme_ca` [global option](/docs/caddyfile/options) instead.) - **ca_root** specifies a PEM file that contains a trusted root certificate for the ACME CA endpoint, if not in the system trust store. - **key_type** is the type of key to use when generating CSRs. Only set this if you have a specific requirement. - **dns** enables the [DNS challenge](/docs/automatic-https#dns-challenge) using the specified provider plugin, which must be plugged in from one of the [`caddy-dns` ](https://github.com/caddy-dns) repositories. Each provider plugin may have their own syntax following their name; refer to their docs for details. Maintaining support for each DNS provider is a community effort. [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) - **propagation_timeout** is a [duration value](/docs/conventions#durations) that sets the maximum time to wait for the DNS TXT records to appear when using the DNS challenge. Set to `-1` to disable propagation checks. Default 2 minutes. - **propagation_delay** is a [duration value](/docs/conventions#durations) that sets how long to wait before starting DNS TXT records propagation checks when using the DNS challenge. Default `0` (no wait). - **dns_ttl** is a [duration value](/docs/conventions#durations) that sets the TTL of the `TXT` record used for the DNS challenge. Rarely needed. - **dns_challenge_override_domain** overrides the domain to use for the DNS challenge. This is to delegate the challenge to a different domain. You may want to use this if your primary domain's DNS provider does not have a [DNS plugin ](https://github.com/caddy-dns) available. You can instead add a `CNAME` record with subdomain `_acme-challenge` to your primary domain, pointing to a secondary domain for which you _do_ have a plugin. This option _does not_ require special support from the plugin. When ACME issuers try to solve the DNS challenge for your primary domain, they will then follow the `CNAME` to your secondary domain to find the `TXT` record. **Note:** Use full canonical name from the CNAME record as value here - `_acme-challenge` subdomain won't be prepended automatically. - **resolvers** customizes the DNS resolvers used when performing the DNS challenge; these take precedence over system resolvers or any default ones. If set here, the resolvers will propagate to all configured certificate issuers. This is typically a list of IP addresses. For example, to use [Google Public DNS ](https://developers.google.com/speed/public-dns): ```caddy-d resolvers 8.8.8.8 8.8.4.4 ``` - **eab** configures ACME external account binding (EAB) for this site, using the key ID and MAC key provided by your CA. - **on_demand** enables [On-Demand TLS](/docs/automatic-https#on-demand-tls) for the hostnames given in the site block's address(es). **Security warning:** Doing so in production is insecure unless you also configure the [`on_demand_tls` global option](/docs/caddyfile/options#on-demand-tls) to mitigate abuse. - **reuse_private_keys** enables reuse of private keys when renewing certificates. By default, a new key is created for every new certificate to mitigate pinning and reduce the scope of key compromise. Key pinning is against industry best practices. This option is not recommended unless you have a specific reason to use it; this may be subject to removal in a future version. - **client_auth** enables and configures TLS client authentication: - **mode** is the mode for authenticating the client. Allowed values are: | Mode | Description | | --- | --- | | request | Ask clients for a certificate, but allow even if there isn't one; do not verify it | | require | Require clients to present a certificate, but do not verify it | | verify_if_given | Ask clients for a certificate; allow even if there isn't one, but verify it if there is | | require_and_verify | Require clients to present a valid certificate that is verified | Default: `require_and_verify` if `trust_pool` module is provided; otherwise, `require`. - **trust_pool** configures the source of certificate authorities (CA) providing certificates against which to validate client certificates. The certificate authority used providing the pool of trusted certificates and the configuration within the segment depends on the configured source of trust pool module. The standard modules available in Caddy are [listed below](#trust-pool-providers). The full list of modules, including 3rd-party, is listed in the [`trust_pool` JSON documentation](/docs/json/apps/http/servers/tls_connection_policies/client_authentication/#trust_pool). Multiple `trusted_*` directives may be used to specify multiple CA or leaf certificates. Client certificates which are not listed as one of the leaf certificates or signed by any of the specified CAs will be rejected according to the **mode**. - **verifier** enables the use of a custom client certificate verifier module. These can perform custom client authentication checks, such as ensuring the certificate is not revoked. - **issuer** configures a custom certificate issuer, or a source from which to obtain certificates. Which issuer is used and the options that follow in this segment depend on the [issuer modules](#issuers) that are available. Some of the other subdirectives such as `ca` and `dns` are actually shortcuts for configuring the `acme` issuer (and this subdirective was added later), so specifying this directive and some of the others is confusing and thus prohibited. This subdirective can be specified multiple times to configure multiple, redundant issuers; if one fails to issue a cert, the next one will be tried. - **get_certificate** enables getting certificates from a [manager module](#certificate-managers) at handshake-time. - **insecure_secrets_log** enables logging of TLS secrets to a file. This is also known as `SSLKEYLOGFILE`. Uses NSS key log format, which can then be parsed by Wireshark or other tools. ⚠️ **Security Warning:** This is insecure as it allows other programs or tools to decrypt TLS connections, and therefore completely compromises security. However, this capability can be useful for debugging and troubleshooting. ### Trust Pool Providers These are the standard trust pool providers that can be used in the `trust_pool` subdirective: #### inline The `inline` module parses the trusted root certificates as listed in the Caddyfile directly in base64 DER-encoded format. The `trust_der` directive may be repeated multiple times. ```caddy-d trust_pool inline { trust_der } ``` - **trust_der** is a base64 DER-encoded CA certificate against which to validate client certificates. #### file The `file` module reads the trusted root certificates from PEM files from disk. The `pem_file` directive can accept multiple file paths on the same line and may be repeated multiple times. ```caddy-d ... file [...] { pem_file ... } ``` - **pem_file** is a path to a PEM CA certificate file against which to validate client certificates. #### pki_root The `pki_root` module obtains the _root_ and trusts certificates from the certificate authority defined in the [PKI app](/docs/caddyfile/options#pki-options). The `authority` directive can accept multiple authorities at the same time and may be repeated multiple times. ```caddy-d ... pki_root [...] { authority ... } ``` - **authority** is the name of the certificate authority configured in the PKI app. #### pki_intermediate The `pki_intermediate` module obtains the _intermediate_ and trusts certificates from the certificate authority defined in the [PKI app](/docs/caddyfile/options#pki-options). The `authority` directive can accept multiple authorities at the same time and may be repeated multiple times. ```caddy-d ... pki_intermediate [...] { authority ... } ``` - **authority** is the name of the certificate authority configured in the PKI app. #### storage The `storage` module extracts the trusted certificates root from Caddy [storage](/docs/caddyfile/options#storage). The `authority` directive can accept multiple authorities at the same time and may be repeated multiple times. ```caddy-d ... storage [...] { storage keys ... } ``` - **storage** is an optional storage module to use. If not specified, the default storage module will be used. If specified, it may be specified only once. - **keys** is the list of storage keys at which the PEM files of the certificates are stored. The directive accepts multiple values on the same line and may be specified multiple times. #### http The `http` module obtains the trusted certificates from HTTP endpoints. The `endpoints` directive can accept multiple endpoints at the same time and may be repeated multiple times. ```caddy-d ... http [] { endpoints tls } ``` - **endpoints** is the list of HTTP endpoints from which to obtain certificates. The directive accepts multiple values on the same line and may be specified multiple times. - **tls** is an optional TLS configuration to use when connecting to the HTTP endpoint. The segment parsing is defined in the [following section](#tls-1). ##### TLS ```caddy-d ... { ca insecure_skip_verify handshake_timeout server_name renegotiation } ``` - **ca** is an optional directive to define the provider of trust pool. The configuration follows the same behavior of [`trust_pool`](#trust_pool). If specified, it may be specified only once. - **insecure_skip_verify** turns off TLS handshake verification, making the connection insecure and vulnerable to man-in-the-middle attacks. _Do not use in production._ The verification is done against either the certificate authorities trusted by the system or as determined by the [`ca`](#ca) directive. - **handshake_timeout** is the maximum [duration](/docs/conventions#durations) to wait for the TLS handshake to complete. Default: No timeout.. - **server_name** sets the server name used when verifying the certificate received in the TLS handshake. By default, this will use the upstream address' host part. - **renegotiation** sets the TLS renegotiation level. TLS renegotiation is the act of performing subsequent handshakes after the first. The level may be one of: - `never` (the default) disables renegotiation. - `once` allows a remote server to request renegotiation once per connection. - `freely` allows a remote server to repeatedly request renegotiation. ### Issuers These issuers come standard with the `tls` directive: #### acme Obtains certificates using the ACME protocol. Note that `acme` is a default issuer (using Let's Encrypt), so configuring it explicitly is usually unnecessary. ```caddy-d ... acme [] { dir test_dir email timeout disable_http_challenge disable_tlsalpn_challenge alt_http_port alt_tlsalpn_port eab trusted_roots dns [ []] propagation_timeout propagation_delay dns_ttl dns_challenge_override_domain resolvers preferred_chains [smallest] { root_common_name any_common_name } profile } ``` - **dir** is the URL to the ACME CA's directory. Default: `https://acme-v02.api.letsencrypt.org/directory` - **test_dir** is an optional fallback directory to use when retrying challenges; if all challenges fail, this endpoint will be used during retries; useful if a CA has a staging endpoint where you want to avoid rate limits on their production endpoint. Default: `https://acme-staging-v02.api.letsencrypt.org/directory` - **email** is the ACME account contact email address. - **timeout** is a [duration value](/docs/conventions#durations) that sets how long to wait before timing out an ACME operation. - **disable_http_challenge** will disable the HTTP challenge. - **disable_tlsalpn_challenge** will disable the TLS-ALPN challenge. - **alt_http_port** is an alternate port on which to serve the HTTP challenge; it has to happen on port 80 so you must forward packets to this alternate port. - **alt_tlsalpn_port** is an alternate port on which to serve the TLS-ALPN challenge; it has to happen on port 443 so you must forward packets to this alternate port. - **eab** specifies an External Account Binding which may be required with some ACME CAs. - **trusted_roots** is one or more root certificates (as PEM filenames) to trust when connecting to the ACME CA server. - **dns** configures the DNS challenge. A provider must be configured here, unless the [`dns` global option](/docs/caddyfile/options#dns) specifies a globally-applicable DNS provider module. - **propagation_timeout** is a [duration value](/docs/conventions#durations) that sets the maximum time to wait for the DNS TXT records to appear when using the DNS challenge. Set to `-1` to disable propagation checks. Default 2 minutes. - **propagation_delay** is a [duration value](/docs/conventions#durations) that sets how long to wait before starting DNS TXT records propagation checks when using the DNS challenge. Default 0 (no wait). - **dns_ttl** is a [duration value](/docs/conventions#durations) that sets the TTL of the `TXT` record used for the DNS challenge. Rarely needed. - **dns_challenge_override_domain** overrides the domain to use for the DNS challenge. This is to delegate the challenge to a different domain. You may want to use this if your primary domain's DNS provider does not have a [DNS plugin ](https://github.com/caddy-dns) available. You can instead add a `CNAME` record with subdomain `_acme-challenge` to your primary domain, pointing to a secondary domain for which you _do_ have a plugin. This option _does not_ require special support from the plugin. When ACME issuers try to solve the DNS challenge for your primary domain, they will then follow the `CNAME` to your secondary domain to find the `TXT` record. **Note:** Use full canonical name from the CNAME record as value here - `_acme-challenge` subdomain won't be prepended automatically. - **resolvers** customizes the DNS resolvers used when performing the DNS challenge; these take precedence over system resolvers or any default ones. If set here, the resolvers will propagate to all configured certificate issuers. This is typically a list of IP addresses. For example, to use [Google Public DNS ](https://developers.google.com/speed/public-dns): ```caddy-d resolvers 8.8.8.8 8.8.4.4 ``` - **preferred_chains** specifies which certificate chains Caddy should prefer; useful if your CA provides multiple chains. Use one of the following options: - **smallest** will tell Caddy to prefer chains with the fewest amount of bytes. - **root_common_name** is a list of one or more common names; Caddy will choose the first chain that has a root that matches with at least one of the specified common names. - **any_common_name** is a list of one or more common names; Caddy will choose the first chain that has an issuer that matches with at least one of the specified common names. - **profile** is the name of the [ACME profile](https://datatracker.ietf.org/doc/draft-aaron-acme-profiles/) to apply when ordering certificates. If you specify one, all configured (implictly or otherwise) CAs must support this profile. Refer to your CA's documentation for available profiles; some CAs may not support profiles. EXPERIMENTAL: The ACME profile specification is still in draft state, so this feature/function is subject to change or removal. #### zerossl Obtains certificates using [ZeroSSL's proprietary certificate issuance API](https://zerossl.com/documentation/api/). An API key is required and payment may also be required depending on your plan. Note that this issue is distinct from [ZeroSSL's ACME endpoint](https://zerossl.com/documentation/acme/). To use ZeroSSL's ACME endpoint, use the `acme` issuer described above configured with ZeroSSL's ACME directory endpoint. ```caddy-d ... zerossl { validity_days alt_http_port dns ... propagation_delay propagation_timeout resolvers dns_ttl } ``` - **validity_days** defines the certificate lifetime. Only certain values are accepted; see [ZeroSSL's docs](https://zerossl.com/documentation/api/create-certificate/) for details. - **alt_http_port** is the port to use for completing ZeroSSL's HTTP validation, if not port 80. - **dns** enables CNAME validation method using the named DNS provider with the given configuration for automatic record provisioning. The DNS provider plugin must be installed from the [`caddy-dns` ](https://github.com/caddy-dns) repositories. Each provider plugin may have their own syntax following their name; refer to their docs for details. Maintaining support for each DNS provider is a community effort. - **propagation_delay** is how long to wait before checking for CNAME record propagation. - **propagation_timeout** is how long to wait for CNAME record propagation before giving up. - **resolvers** defines custom DNS resolvers to use when checking for CNAME record propagation. - **dns_ttl** configures the TTL for CNAME records created as part of the validation process. #### internal Obtains certificates from an internal certificate authority. ```caddy-d ... internal { ca lifetime sign_with_root } ``` - **ca** is the name of the internal CA to use. Default: `local`. See the [PKI app global options](/docs/caddyfile/options#pki-options) to configure the `local` CA, or to create alternate CAs. By default, the root CA certificate has a `3600d` lifetime (10 years) and the intermediate has a `7d` lifetime (7 days). Caddy will attempt to install the root CA certificate to the system trust store, but this may fail when Caddy is running as an unprivileged user, or when running in a Docker container. In that case, the root CA certificate will need to be manually installed, either by using the [`caddy trust`](/docs/command-line#caddy-trust) command, or by [copying out of the container](/docs/running#usage). - **lifetime** is a [duration value](/docs/conventions#durations) that sets the validity period for internally issued leaf certificates. Default: `12h`. It is NOT recommended to change this, unless absolutely necessary. It must be shorter than the intermediate's lifetime. - **sign_with_root** forces the root to be the issuer instead of the intermediate. This is NOT recommended and should only be used when devices/clients do not properly validate certificate chains (very uncommon). ### Certificate Managers Certificate manager modules are distinct from issuer modules in that use of manager modules implies that an external tool or service is keeping the certificate renewed, whereas an issuer module implies that Caddy itself is managing the certificate. (Issuer modules take a Certificate Signing Request (CSR) as input, but certificate manager modules take a TLS ClientHello as input.) These manager modules come standard with the `tls` directive: #### tailscale Get certificates from a locally-running [Tailscale ](https://tailscale.com) instance. [HTTPS must be enabled in your Tailscale account](https://tailscale.com/kb/1153/enabling-https/) (or your open source [Headscale server ](https://github.com/juanfont/headscale)); 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). _**NOTE: This is usually unnecessary!** Caddy automatically uses Tailscale for all `*.ts.net` domains without any extra configuration._ ```caddy-d get_certificate tailscale # often unnecessary! ``` #### http Get certificates by making an HTTP(S) request. The response must have a `200` status code and the body must contain a PEM chain including the full certificate (with intermediates) as well as the private key. ```caddy-d get_certificate http ``` - **url** is the fully-qualified URL to which to make the request. It is strongly advised that this be a local endpoint for performance reasons. The URL will be augmented with the following query string parameters: - `server_name`: SNI value - `signature_schemes`: comma-separated list of hex IDs of signature algorithms - `cipher_suites`: comma-separated list of hex IDS of cipher suites - `local_ip`: IP address to which the client made the request ## Examples Use a custom certificate and key. The certificate should have [SANs](https://en.wikipedia.org/wiki/Subject_Alternative_Name) that match the site address: ```caddy example.com { tls cert.pem key.pem } ``` Use [locally-trusted](/docs/automatic-https#local-https) certificates for all hosts on the current site block, rather than public certificates via ACME / Let's Encrypt (useful in dev environments): ```caddy example.com { tls internal } ``` Use locally-trusted certificates, but managed [On-Demand](/docs/automatic-https#on-demand-tls) instead of in the background. This allows you to point any domain at your Caddy instance and have it automatically provision a certificate for you. This SHOULD NOT be used if your Caddy instance is publicly accessible, since an attacker could use it to exhaust your server's resources: ```caddy https:// { tls internal { on_demand } } ``` Use custom options for the internal CA (cannot use the `tls internal` shortcut): ```caddy example.com { tls { issuer internal { ca foo } } } ``` Specify an email address for your ACME account (but if only one email is used for all sites, we recommend the `email` [global option](/docs/caddyfile/options) instead): ```caddy example.com { tls your@email.com } ``` Enable the DNS challenge for a domain managed on Cloudflare with account credentials in an environment variable. This unlocks wildcard certificate support, which requires DNS validation: ```caddy *.example.com { tls { dns cloudflare {env.CLOUDFLARE_API_TOKEN} } } ``` Get the certificate chain via HTTP, instead of having Caddy manage it. Note that [`get_certificate`](#certificate-managers) implies [`on_demand`](#on_demand) is enabled, fetching certificates using a module instead of triggering ACME issuance: ```caddy https:// { tls { get_certificate http http://localhost:9007/certs } } ``` Enable TLS Client Authentication and require clients to present a valid certificate that is verified against all the provided CA's via the [`trust_pool`](#trust_pool) `file` provider: ```caddy example.com { tls { client_auth { trust_pool file ../caddy.ca.cer ../root.ca.cer } } } ``` --- --- title: tracing (Caddyfile directive) --- # tracing Enables integration with OpenTelemetry tracing facilities, using [`opentelemetry-go` ](https://github.com/open-telemetry/opentelemetry-go). When enabled, it will propagate an existing trace context or initialize a new one. It uses [gRPC](https://github.com/grpc/) as an exporter protocol and W3C [tracecontext](https://www.w3.org/TR/trace-context/) and [baggage](https://www.w3.org/TR/baggage/) as propagators. The trace ID and span ID are added to [access logs](/docs/caddyfile/directives/log) as the standard `traceID` and `spanID` fields. Additionally, the `{http.vars.trace_id}` and `{http.vars.span_id}` placeholders are available; for example, you can use them in a [`request_header`](request_header) to pass the IDs to your app. ## Syntax ```caddy-d tracing { [span ] } ``` - **<span_name>** is a span name. Please see span [naming guidelines](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/specification/trace/api.md). [Placeholders](/docs/caddyfile/concepts#placeholders) may be used in span names; keep in mind that tracing happens as early as possible, so only request placeholders may be used, and not response placeholders. ## Configuration ### Environment variables It can be configured using the environment variables defined by the [OpenTelemetry Environment Variable Specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md). For the exporter configuration details, please see [spec](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/specification/protocol/exporter.md). For example: ```bash export OTEL_EXPORTER_OTLP_HEADERS="myAuthHeader=myToken,anotherHeader=value" export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://my-otlp-endpoint:55680 ``` ## Examples Here is a **Caddyfile** example: ```caddy example.com { handle /api* { tracing { span api } request_header X-Trace-Id {http.vars.trace_id} reverse_proxy localhost:8081 } handle { tracing { span app } reverse_proxy localhost:8080 } } ``` --- --- title: try_files (Caddyfile directive) --- # try_files Rewrites the request URI path to the first of the listed files which exists in the site root. If no files match, no rewrite is performed. ## Syntax ```caddy-d try_files { policy first_exist|first_exist_fallback|smallest_size|largest_size|most_recently_modified } ``` - **** is the list of files to try. The URI path will be rewritten to the first one that exists. To match directories, append a trailing forward slash `/` to the path. All file paths are relative to the site [root](root), and [glob patterns](https://pkg.go.dev/path/filepath#Match) will be expanded. Each argument may also contain a query string, in which case the query string will also be changed if it matches that particular file. If the `try_policy` is `first_exist` (the default), then the last item in the list may be a number prefixed by `=` (e.g. `=404`), which as a fallback, will emit an error with that code; the error can be caught and handled with [`handle_errors`](handle_errors). - **policy** is the policy for choosing the file among the list of files. Default: `first_exist` ## Expanded form The `try_files` directive is basically a shortcut for: ```caddy-d @try_files file rewrite @try_files {file_match.relative} ``` Note that this directive does not accept a matcher token. If you need more complex matching logic, then use the expanded form above as a basis. See the [`file` matcher](/docs/caddyfile/matchers#file) for more details. ## Examples If the request does not match any static files, rewrite to your PHP index/router entrypoint: ```caddy-d try_files {path} /index.php ``` Same, but adding the original path to the query string (required by some legacy PHP apps): ```caddy-d try_files {path} /index.php?{query}&p={path} ``` Same, but also match directories: ```caddy-d try_files {path} {path}/ /index.php?{query}&p={path} ``` Attempt to rewrite to a file or directory if it exists, otherwise emit a 404 error (which can be caught and handled with [`handle_errors`](handle_errors)): ```caddy-d try_files {path} {path}/ =404 ``` Choose the most recently deployed version of a static file (e.g. serve `index.be331df.html` when `index.html` is requested): ```caddy-d try_files {file.base}.*.{file.ext} { policy most_recently_modified } ``` --- --- title: uri (Caddyfile directive) --- # uri Manipulates a request's URI. It can strip path prefix/suffix or replace substrings on the whole URI. This directive is distinct from [`rewrite`](rewrite) in that `uri` _differentiably_ changes the URI, rather than resetting it to something completely different as `rewrite` does. While `rewrite` is treated specially as an internal redirect, `uri` is just another middleware. ## Syntax Multiple different operations are supported: ```caddy-d uri [] strip_prefix uri [] strip_suffix uri [] replace [] uri [] path_regexp uri [] query [-|+] [] uri [] query { [] [] ... } ``` The first (non-matcher) argument specifies the operation: - **strip_prefix** strips the prefix from the path. - **strip_suffix** strips the suffix from the path. - **replace** performs a substring replacement across the whole URI. - **<target>** is the prefix, suffix, or search string/regular expression. If a prefix, the leading forward slash may be omitted, since paths always start with a forward slash. - **<replacement>** is the replacement string. Supports using capture groups with `$name` or `${name}` syntax, or with a number for the index, such as `$1`. See the [Go documentation](https://golang.org/pkg/regexp/#Regexp.Expand) for details. If the replacement value is `""`, then the matching text is removed from the value. - **<limit>** is an optional limit to the maximum number of replacements. - **path_regexp** performs a regular expression replacement on the path portion of the URI. - **<target>** is the prefix, suffix, or search string/regular expression. If a prefix, the leading forward slash may be omitted, since paths always start with a forward slash. - **<replacement>** is the replacement string. Supports using capture groups with `$name` or `${name}` syntax, or with a number for the index, such as `$1`. See the [Go documentation](https://golang.org/pkg/regexp/#Regexp.Expand) for details. If the replacement value is `""`, then the matching text is removed from the value. - **query** performs manipulations on the URI query, with the mode depending on the prefix to the parameter name or the count of arguments. A block can be used to specify multiple operations at once, grouped and performed in this order: rename 🡒 set 🡒 append 🡒 replace 🡒 delete. - With no prefix, the parameter is set with the given value in the query. For example, `uri query foo bar` will set the value of the `foo` param to `bar`. - Prefix with `-` to remove the parameter from the query. For example, `uri query -foo` will delete the `foo` parameter from the query. - Prefix with `+` to append a parameter to the query, with the given value. This will _not_ overwrite an existing parameter with the same name (omit the `+` to overwrite). For example, `uri query +foo bar` will append `foo=bar` to the query. - A param with `>` as an infix will rename the parameter to the value after the `>`. For example, `uri query foo>bar` will rename the `foo` parameter to `bar`. - With three arguments, query value regular expression replacement is performed, where the first arg is the query param name, the second is the search value, and the third is the replacement. The first arg (param name) may be `*` to perform the replacement on all query params. Supports using capture groups with `$name` or `${name}` syntax, or with a number for the index, such as `$1`. See the [Go documentation](https://golang.org/pkg/regexp/#Regexp.Expand) for details. If the replacement value is `""`, then the matching text is removed from the value. For example, `uri query foo ^(ba)r $1z` would replace the value of the `foo` param, where the value began with `bar` resulting in the value becoming `baz`. URI mutations occur on the normalized or unescaped form of the URI. However, escape sequences can be used in the prefix or suffix patterns to match only those literal escapes at those positions in the request path. For example, `uri strip_prefix /a/b` will rewrite both `/a/b/c` and `/a%2Fb/c` to `/c`; and `uri strip_prefix /a%2Fb` will rewrite `/a%2Fb/c` to `/c`, but won't match `/a/b/c`. The URI path is cleaned of directory traversal dots before modifications. Additionally, multiple slashes (such as `//`) are merged unless the `` contains multiple slashes too. ## Similar directives Some other directives can also manipulate the request URI. - [`rewrite`](rewrite) changes the entire path and query to a new value instead of partially changing the value. - [`handle_path`](handle_path) does the same as [`handle`](handle), but it strips a prefix from the request before running its handlers. Can be used instead of `uri strip_prefix` to eliminate one extra line of configuration in many cases. ## Examples Strip `/api` from the beginning of all request paths: ```caddy-d uri strip_prefix /api ``` Strip `.php` from the end of all request paths: ```caddy-d uri strip_suffix .php ``` Replace "/docs/" with "/v1/docs/" in any request URI: ```caddy-d uri replace /docs/ /v1/docs/ ``` Collapse all repeated slashes in the request path (but not the request query) to a single slash: ```caddy-d uri path_regexp /{2,} / ``` Set the value of the `foo` query parameter to `bar`: ```caddy-d uri query foo bar ``` Remove the `foo` parameter from the query: ```caddy-d uri query -foo ``` Rename the `foo` query parameter to `bar`: ```caddy-d uri query foo>bar ``` Append the `bar` parameter to the query: ```caddy-d uri query +foo bar ``` Replace the value of the `foo` query parameter where the value begins with `bar` with `baz`: ```caddy-d uri query foo ^(ba)r $1z ``` Perform multiple query operations at once: ```caddy-d uri query { +foo bar -baz qux test renamethis>renamed } ``` --- --- title: vars (Caddyfile directive) --- # vars Sets one or more variables to a particular value, to be used later in the request handling chain. The primary way to access variables is with placeholders, which have the form `{vars.variable_name}`, or with the [`vars`](/docs/caddyfile/matchers#vars) and [`vars_regexp`](/docs/caddyfile/matchers#vars_regexp) request matchers. You may use variables with the [`templates`](templates) directive using the `placeholder` function, for example: `{{ "{{placeholder \"http.vars.variable_name\"}}" }}` As a special case, it's possible to override the variable named `http.auth.user.id`, which is stored in the replacer, to update the `user_id` field in [access logs](log). ## Syntax ```caddy-d vars [] [ ] { ... } ``` - **<name>** is the variable name to set. - **<value>** is the value of the variable. The value will be type converted if possible; `true` and `false` will be converted to boolean types, and numeric values will be converted to integer or float accordingly. To avoid this conversion and keep them as strings, you may wrap them with [quotes](/docs/caddyfile/concepts#tokens-and-quotes). ## Examples To set a single variable, the value being conditional based on the request path, then responding with the value: ```caddy example.com { vars /foo* isFoo "yep" vars isFoo "nope" respond {vars.isFoo} } ``` To set multiple variables, each converted to the appropriate scalar type: ```caddy-d vars { # boolean abc true # integer def 1 # float ghi 2.3 # string jkl "example" } ``` --- --- title: Caddyfile Directives --- # Caddyfile Directives Directives are functional keywords that appear within site [blocks](/docs/caddyfile/concepts#blocks). Sometimes, they may open blocks of their own which can contain _subdirectives_, but directives **cannot** be used within other directives unless noted. For example, you can't use `basic_auth` inside a `file_server` block, because `file_server` does not know how to do authentication. However, you _may_ use some directives within special directive blocks like `handle` and `route` because they are specifically designed to group HTTP handler directives. - [Syntax](#syntax) - [Directive Order](#directive-order) - [Sorting Algorithm](#sorting-algorithm) The following directives come standard with Caddy, and can be used in the HTTP Caddyfile:
Directive | Description ----------|------------ **[abort](/docs/caddyfile/directives/abort)** | Aborts the HTTP request **[acme_server](/docs/caddyfile/directives/acme_server)** | An embedded ACME server **[basic_auth](/docs/caddyfile/directives/basic_auth)** | Enforces HTTP Basic Authentication **[bind](/docs/caddyfile/directives/bind)** | Customize the server's socket address **[encode](/docs/caddyfile/directives/encode)** | Encodes (usually compresses) responses **[error](/docs/caddyfile/directives/error)** | Trigger an error **[file_server](/docs/caddyfile/directives/file_server)** | Serve files from disk **[forward_auth](/docs/caddyfile/directives/forward_auth)** | Delegate authentication to an external service **[fs](/docs/caddyfile/directives/fs)** | Set the file system to use for file I/O **[handle](/docs/caddyfile/directives/handle)** | A mutually-exclusive group of directives **[handle_errors](/docs/caddyfile/directives/handle_errors)** | Defines routes for handling errors **[handle_path](/docs/caddyfile/directives/handle_path)** | Like handle, but strips path prefix **[header](/docs/caddyfile/directives/header)** | Sets or removes response headers **[import](/docs/caddyfile/directives/import)** | Include snippets or files **[intercept](/docs/caddyfile/directives/intercept)** | Intercept responses written by other handlers **[invoke](/docs/caddyfile/directives/invoke)** | Invoke a named route **[log](/docs/caddyfile/directives/log)** | Enables access/request logging **[log_append](/docs/caddyfile/directives/log_append)** | Append a field to the access log **[log_skip](/docs/caddyfile/directives/log_skip)** | Skip access logging for matched requests **[log_name](/docs/caddyfile/directives/log_name)** | Override the logger name(s) to write to **[map](/docs/caddyfile/directives/map)** | Maps an input value to one or more outputs **[method](/docs/caddyfile/directives/method)** | Change the HTTP method internally **[metrics](/docs/caddyfile/directives/metrics)** | Configures the Prometheus metrics exposition endpoint **[php_fastcgi](/docs/caddyfile/directives/php_fastcgi)** | Serve PHP sites over FastCGI **[push](/docs/caddyfile/directives/push)** | Push content to the client using HTTP/2 server push **[redir](/docs/caddyfile/directives/redir)** | Issues an HTTP redirect to the client **[request_body](/docs/caddyfile/directives/request_body)** | Manipulates request body **[request_header](/docs/caddyfile/directives/request_header)** | Manipulates request headers **[respond](/docs/caddyfile/directives/respond)** | Writes a hard-coded response to the client **[reverse_proxy](/docs/caddyfile/directives/reverse_proxy)** | A powerful and extensible reverse proxy **[rewrite](/docs/caddyfile/directives/rewrite)** | Rewrites the request internally **[root](/docs/caddyfile/directives/root)** | Set the path to the site root **[route](/docs/caddyfile/directives/route)** | A group of directives treated literally as single unit **[templates](/docs/caddyfile/directives/templates)** | Execute templates on the response **[tls](/docs/caddyfile/directives/tls)** | Customize TLS settings **[tracing](/docs/caddyfile/directives/tracing)** | Integration with OpenTelemetry tracing **[try_files](/docs/caddyfile/directives/try_files)** | Rewrite that depends on file existence **[uri](/docs/caddyfile/directives/uri)** | Manipulate the URI **[vars](/docs/caddyfile/directives/vars)** | Set arbitrary variables
## Syntax The syntax of each directive will look something like this: ```caddy-d directive [] { subdirective [] } ``` The `` indicate tokens to be substituted by actual values. The`[brackets]` indicate optional parameters. The ellipses `...` indicates a continuation, i.e. one or more parameters or lines. Subdirectives are typically optional unless documented otherwise, even though they don't appear in `[brackets]`. ### Matchers Most—but not all—directives accept [matcher tokens](/docs/caddyfile/matchers#syntax), which let you filter requests. Matcher tokens are usually optional. Directives support matchers if you see this in a directive's syntax: ```caddy-d [] ``` Because matcher tokens all work the same, the various possibilities for the matcher token will not be described on every page, to reduce duplication. Instead, refer to the [matcher documentation](/docs/caddyfile/matchers) for a detailed explanation of the syntax. ## Directive order Many directives manipulate the HTTP handler chain. The order in which those directives are evaluated matters, so a default ordering is hard-coded into Caddy. You can override/customize this ordering by using the [`order` global option](/docs/caddyfile/options#order) or the [`route` directive](/docs/caddyfile/directives/route). ```caddy-d tracing map vars fs root log_append log_skip log_name header copy_response_headers # only in reverse_proxy's handle_response block request_body redir # incoming request manipulation method rewrite uri try_files # middleware handlers; some wrap responses basic_auth forward_auth request_header encode push intercept templates # special routing & dispatching directives invoke handle handle_path route # handlers that typically respond to requests abort error copy_response # only in reverse_proxy's handle_response block respond metrics reverse_proxy php_fastcgi file_server acme_server ``` ## Sorting algorithm For ease of use, the Caddyfile adapter sorts directives according to the following rules: - Differently named directives are sorted by their position in the [default order](#directive-order). The default order can be overridden with the [`order` global option](/docs/caddyfile/options). Directives from plugins _do not_ have an order, so the [`order`](/docs/caddyfile/options) global option or the [`route`](/docs/caddyfile/directives/route) directive should be used to set one. - Same-named directives are sorted according to their [matchers](/docs/caddyfile/matchers#syntax). - The highest priority is a directive with a single [path matcher](/docs/caddyfile/matchers#path-matchers). Path matchers are sorted by specificity, from most specific to least specific. In general, this is performed by sorting by the length of the path matcher. There is one exception where if the path ends in a `*` and the paths of the two matchers are otherwise the same, the matcher with no `*` is considered more specific and sorted higher. For example: - `/foobar` is more specific than `/foo` - `/foo` is more specific than `/foo*` - `/foo/*` is more specific than `/foo*` - A directive with any other matcher is sorted next, in the order it appears in the Caddyfile. This includes path matchers with multiple values, and [named matchers](/docs/caddyfile/matchers#named-matchers). - A directive with no matcher (i.e. matching all requests) is sorted last. - The [`vars`](/docs/caddyfile/directives/vars) directive has its ordering by matcher reversed, because it involves setting values which can overwrite each other, so the most specific matcher should be evaluated last. - The contents of the [`route`](/docs/caddyfile/directives/route) directive ignores all the above rules, and preserves the order the directives appear within. --- --- title: Request matchers (Caddyfile) --- # Request Matchers **Request matchers** can be used to filter (or classify) requests by various criteria. - [Syntax](#syntax) - [Examples](#examples) - [Wildcard matchers](#wildcard-matchers) - [Path matchers](#path-matchers) - [Named matchers](#named-matchers) - [Standard matchers](#standard-matchers) - [client_ip](#client-ip) - [expression](#expression) - [file](#file) - [header](#header) - [header_regexp](#header-regexp) - [host](#host) - [method](#method) - [not](#not) - [path](#path) - [path_regexp](#path-regexp) - [protocol](#protocol) - [query](#query) - [remote_ip](#remote-ip) - [vars](#vars) - [vars_regexp](#vars-regexp) ## Syntax In the Caddyfile, a **matcher token** immediately following the directive can limit that directive's scope. The matcher token can be one of these forms: 1. [**`*`**](#wildcard-matchers) to match all requests (wildcard; default). 2. [**`/path`**](#path-matchers) start with a forward slash to match a request path. 3. [**`@name`**](#named-matchers) to specify a _named matcher_. If a directive supports matchers, it will appear as `[]` in its syntax documentation. Matcher tokens are [usually optional](/docs/caddyfile/directives#syntax), denoted by `[ ]`. If the matcher token is omitted, it is the same as a wildcard matcher (`*`). #### Examples This directive applies to [all](#wildcard-matchers) HTTP requests: ```caddy-d reverse_proxy localhost:9000 ``` And this is the same (`*` is unnecessary here): ```caddy-d reverse_proxy * localhost:9000 ``` But this directive applies only to requests having a [path](#path-matchers) starting with `/api/`: ```caddy-d reverse_proxy /api/* localhost:9000 ``` To match on anything other than a path, define a [named matcher](#named-matchers) and refer to it using `@name`: ```caddy-d @postfoo { method POST path /foo/* } reverse_proxy @postfoo localhost:9000 ``` ### Wildcard matchers The wildcard (or "catch-all") matcher `*` matches all requests, and is only needed if a matcher token is required. For example, if the first argument you want to give a directive also happens to be a path, it would look exactly like a path matcher! So you can use a wildcard matcher to disambiguate, for example: ```caddy-d root * /home/www/mysite ``` Otherwise, this matcher is not often used. We generally recommend omitting it if syntax doesn't require it. ### Path matchers Matching by URI path is the most common way to match requests, so the matcher can be inlined, like this: ```caddy-d redir /old.html /new.html ``` Path matcher tokens must start with a forward slash `/`. **[Path matching](#path) is an exact match by default, not a prefix match.** You must append a `*` for a fast prefix match. Note that `/foo*` will match `/foo` and `/foo/` as well as `/foobar`; you might actually want `/foo/*` instead. ### Named matchers All matchers that are not path or wildcard matchers must be named matchers. This is a matcher that is defined outside of any particular directive, and can be reused. Defining a matcher with a unique name gives you more flexibility, allowing you to combine [any available matchers](#standard-matchers) into a set: ```caddy-d @name { ... } ``` or, if there is only one matcher in the set, you can put it on the same line: ```caddy-d @name ... ``` Then you can use the matcher like so, by specifying it as the first argument to a directive: ```caddy-d directive @name ``` For example, this proxies HTTP/1.1 websocket requests to `localhost:6001`, and other requests to `localhost:8080`. It matches requests that have a header field named `Connection` _containing_ `Upgrade`, **and** another field named `Upgrade` with exactly `websocket`: ```caddy example.com { @websockets { header Connection *Upgrade* header Upgrade websocket } reverse_proxy @websockets localhost:6001 reverse_proxy localhost:8080 } ``` If the matcher set consists of only one matcher, a one-liner syntax also works: ```caddy-d @post method POST reverse_proxy @post localhost:6001 ``` As a special case, the [`expression` matcher](#expression) may be used without specifying its name as long as one [quoted](/docs/caddyfile/concepts#tokens-and-quotes) argument (the CEL expression itself) follows the matcher name: ```caddy-d @not-found `{err.status_code} == 404` ``` Like directives, named matcher definitions must go inside the [site blocks](/docs/caddyfile/concepts#structure) that use them. A named matcher definition constitutes a _matcher set_. Matchers in a set are AND'ed together; i.e. all must match. For example, if you have both a [`header`](#header) and [`path`](#path) matcher in the set, both must match. Multiple matchers of the same type may be merged (e.g. multiple [`path`](#path) matchers in the same set) using boolean algebra (AND/OR), as described in their respective sections below. For more complex boolean matching logic, it's recommended to the [`expression` matcher](#expression) to write a CEL expression, which supports **and** `&&`, **or** `||`, and **parentheses** `( )`. ## Standard matchers Full matcher documentation can be found [in each respective matcher module's docs](/docs/json/apps/http/servers/routes/match/). Requests can be matched the following ways: ### client_ip ```caddy-d client_ip expression client_ip('') ``` By the client IP address. Accepts exact IPs or CIDR ranges. IPv6 zones are supported. This matcher is best used when the [`trusted_proxies`](/docs/caddyfile/options#trusted-proxies) global option is configured, otherwise it acts identically to the [`remote_ip`](#remote-ip) matcher. Only requests from trusted proxies will have their client IP parsed at the start of the request; untrusted requests will use the remote IP address of the immediate peer. As a shortcut, `private_ranges` can be used to match all private IPv4 and IPv6 ranges. It's the same as specifying all of these ranges: `192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8 fd00::/8 ::1` There can be multiple `client_ip` matchers per named matcher, and their ranges will be merged and OR'ed together. #### Example: Match requests from private IPv4 addresses: ```caddy-d @private-ipv4 client_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8 ``` This matcher is commonly paired with the [`not`](#not) matcher to invert the match. For example, to abort all connections from _public_ IPv4 and IPv6 addresses (which is the inverse of all private ranges): ```caddy example.com { @denied not client_ip private_ranges abort @denied respond "Hello, you must be from a private network!" } ``` In a [CEL expression](#expression), it would look like this: ```caddy-d @my-friends `client_ip('12.23.34.45', '23.34.45.56')` ``` ### expression ```caddy-d expression ``` By any [CEL (Common Expression Language)](https://github.com/google/cel-spec) expression that returns `true` or `false`. Caddy [placeholders](/docs/conventions#placeholders) (or [Caddyfile shorthands](/docs/caddyfile/concepts#placeholders)) may be used in these CEL expressions, as they are preprocessed and converted to regular CEL function calls before being interpreted by the CEL environment. Most other request matchers can also be used in expressions as functions, which allows for more flexibility for boolean logic than outside expressions. See the documentation for each matcher for the supported syntax within CEL expressions. For convenience, the matcher name may be omitted if defining a named matcher that consists solely of a CEL expression. The CEL expression must be [quoted](/docs/caddyfile/concepts#tokens-and-quotes) (backticks or heredocs recommended). This reads quite nicely: ```caddy-d @mutable `{method}.startsWith("P")` ``` In this case the CEL matcher is assumed. #### Examples: Match requests whose methods start with `P`, e.g. `PUT` or `POST`: ```caddy-d @methods expression {method}.startsWith("P") ``` Match requests where handler returned error status code `404`, would be used in conjunction with the [`handle_errors` directive](/docs/caddyfile/directives/handle_errors): ```caddy-d @404 expression {err.status_code} == 404 ``` Match requests where the path matches one of two different regular expressions; this is only possible to write using an expression, because the [`path_regexp`](#path-regexp) matcher can normally only exist once per named matcher: ```caddy-d @user expression path_regexp('^/user/(\w*)') || path_regexp('^/(\w*)') ``` Or the same, omitting the matcher name, and wrapping in [backticks](/docs/caddyfile/concepts#tokens-and-quotes) so it's parsed as a single token: ```caddy-d @user `path_regexp('^/user/(\w*)') || path_regexp('^/(\w*)')` ``` You may use [heredoc syntax](/docs/caddyfile/concepts#heredocs) to write multi-line CEL expressions: ```caddy-d @api < try_files try_policy first_exist|first_exist_fallback|smallest_size|largest_size|most_recently_modified split_path } file expression `file({ 'root': '', 'try_files': [''], 'try_policy': 'first_exist|first_exist_fallback|smallest_size|largest_size|most_recently_modified', 'split_path': [''] })` expression file('') ``` By files. - `root` defines the directory in which to look for files. Default is the current working directory, or the `root` [variable](/docs/modules/http.handlers.vars) (`{http.vars.root}`) if set (can be set via the [`root` directive](/docs/caddyfile/directives/root)). - `try_files` checks files in its list that match the try_policy. To match directories, append a trailing forward slash `/` to the path. All file paths are relative to the site [root](/docs/caddyfile/directives/root), and [glob patterns](https://pkg.go.dev/path/filepath#Match) will be expanded. If the `try_policy` is `first_exist` (the default), then the last item in the list may be a number prefixed by `=` (e.g. `=404`), which as a fallback, will emit an error with that code; the error can be caught and handled with [`handle_errors`](/docs/caddyfile/directives/handle_errors). - `try_policy` specifies how to choose a file. Default is `first_exist`. - `first_exist` checks for file existence. The first file that exists is selected. - `first_exist_fallback` is similar to `first_exist`, but assumes that the last element in the list always exists to prevent a disk access. - `smallest_size` chooses the file with the smallest size. - `largest_size` chooses the file with the largest size. - `most_recently_modified` chooses the file that was most recently modified. - `split_path` will cause the path to be split at the first delimiter in the list that is found in each filepath to try. For each split value, the left-hand side of the split including the delimiter itself will be the filepath that is tried. For example, `/remote.php/dav/` using a delimiter of `.php` would try the file `/remote.php`. Each delimiter must appear at the end of a URI path component in order to be used as a split delimiter. This is a niche setting and is mostly used when serving PHP sites. Because `try_files` with a policy of `first_exist` is so common, there is a one-line shortcut for that: ```caddy-d file ``` An empty `file` matcher (one with no files listed after it) will see if the requested file—verbatim from the URI, relative to the [site root](/docs/caddyfile/directives/root)—exists. This is effectively the same as `file {path}`. Upon matching, four new placeholders will be made available: - `{file_match.relative}` The root-relative path of the file. This is often useful when rewriting requests. - `{file_match.absolute}` The absolute path of the matched file, including the root. - `{file_match.type}` The type of file, `file` or `directory`. - `{file_match.remainder}` The portion remaining after splitting the file path (if `split_path` is configured) #### Examples: Match requests where the path is a file that exists: ```caddy-d @file file ``` Match requests where the path followed by `.html` is a file that exists, or if not, where the path is a file that exists: ```caddy-d @html file { try_files {path}.html {path} } ``` Same as above, except using the one-line shortcut, and falling back to emitting a 404 error if a file is not found: ```caddy-d @html-or-error file {path}.html {path} =404 ``` Some more examples using [CEL expressions](#expression). Keep in mind that placeholders are preprocessed and converted to regular CEL function calls before being interpreted by the CEL environment, so concatenation is used here. Additionally, the long-form must be used if concatenating with placeholders due to current parsing limitations: ```caddy-d @file `file()` @first `file({'try_files': [{path}, {path} + '/', 'index.html']})` @smallest `file({'try_policy': 'smallest_size', 'try_files': ['a.txt', 'b.txt']})` ``` --- ### header ```caddy-d header [ ...] expression header({'': ''}) ``` By request header fields. - `` is the name of the HTTP header field to check. - If prefixed with `!`, the field must not exist to match (omit value arg). - `` is the value the field must have to match. One or more may be specified. - If prefixed with `*`, it performs a fast suffix match (appears at the end). - If suffixed with `*`, it performs a fast prefix match (appears at the start). - If enclosed by `*`, it performs a fast substring match (appears anywhere). - Otherwise, it is a fast exact match. Different header fields within the same set are AND-ed. Multiple values per field are OR'ed. Note that header fields may be repeated and have different values. Backend applications MUST consider that header field values are arrays, not singular values, and Caddy does not interpret meaning in such quandaries. #### Example: Match requests with the `Connection` header containing `Upgrade`: ```caddy-d @upgrade header Connection *Upgrade* ``` Match requests with the `Foo` header containing `bar` OR `baz`: ```caddy-d @foo { header Foo bar header Foo baz } ``` Match requests that do not have the `Foo` header field at all: ```caddy-d @not_foo header !Foo ``` Using an [CEL expression](#expression), match WebSocket requests by checking for the `Connection` header containing `Upgrade` and the `Upgrade` header equalling `websocket` (HTTP/2 has the `:protocol` header for this): ```caddy-d @websockets `header({'Connection':'*Upgrade*','Upgrade':'websocket'}) || header({':protocol': 'websocket'})` ``` --- ### header_regexp ```caddy-d header_regexp [] expression header_regexp('', '', '') expression header_regexp('', '') ``` Like [`header`](#header), but supports regular expressions. 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). As of v2.8.0, if `name` is _not_ provided, the name will be taken from the named matcher's name. For example a named matcher `@foo` will cause this matcher to be named `foo`. The main advantage of specifying a name is if more than one regexp matcher (e.g. `header_regexp` and [`path_regexp`](#path-regexp), or multiple different header fields) is used in the same named matcher. Capture groups can be accessed via [placeholder](/docs/caddyfile/concepts#placeholders) in directives after matching: - `{re..}` where: - `` is the name of the regular expression, - `` is either the name or number of the capture group in the expression. - `{re.}` without a name, is also populated for convenience. The caveat is that if multiple regexp matchers are used in sequence, then the placeholder values will be overwritten by the next matcher. Capture group `0` is the full regexp match, `1` is the first capture group, `2` is the second capture group, and so on. So `{re.foo.1}` or `{re.1}` will both hold the value of the first capture group. Only one regular expression is supported per header field, since regexp patterns cannot be merged; if you need more, consider using an [`expression` matcher](#expression). Matches against multiple different header fields will be AND'ed. #### Example: Match requests where the Cookie header contains `login_` followed by a hex string, with a capture group that can be accessed with `{re.login.1}` or `{re.1}`. ```caddy-d @login header_regexp login Cookie login_([a-f0-9]+) ``` This can be simplified by omitting the name, which will be inferred from the named matcher: ```caddy-d @login header_regexp Cookie login_([a-f0-9]+) ``` Or the same, using a [CEL expression](#expression): ```caddy-d @login `header_regexp('login', 'Cookie', 'login_([a-f0-9]+)')` ``` --- ### host ```caddy-d host expression host('') ``` Matches request by the `Host` header field of the request. Since most site blocks already indicate hosts in the address of the site, this matcher is more commonly used in site blocks that use a wildcard hostname (see the [wildcard certificates pattern](/docs/caddyfile/patterns#wildcard-certificates)), but where hostname-specific logic is required. Multiple `host` matchers will be OR'ed together. #### Example: Matching one subdomain: ```caddy-d @sub host sub.example.com ``` Matching the apex domain and a subdomain: ```caddy-d @site host example.com www.example.com ``` Multiple subdomains using a [CEL expression](#expression): ```caddy-d @app `host('app1.example.com', 'app2.example.com')` ``` --- ### method ```caddy-d method expression method('') ``` By the method (verb) of the HTTP request. Verbs should be uppercase, like `POST`. Can match one or many methods. Multiple `method` matchers will be OR'ed together. #### Examples: Match requests with the `GET` method: ```caddy-d @get method GET ``` Match requests with the `PUT` or `DELETE` methods: ```caddy-d @put-delete method PUT DELETE ``` Match read-only methods using a [CEL expression](#expression): ```caddy-d @read `method('GET', 'HEAD', 'OPTIONS')` ``` --- ### not ```caddy-d not ``` or, to negate multiple matchers which get AND'ed, open a block: ```caddy-d not { } ``` The results of the enclosed matchers will be negated. #### Examples: Match requests with paths that do NOT begin with `/css/` OR `/js/`. ```caddy-d @not-assets { not path /css/* /js/* } ``` Match requests WITH NEITHER: - an `/api/` path prefix, NOR - the `POST` request method i.e. must have none of these to match: ```caddy-d @with-neither { not path /api/* not method POST } ``` Match requests WITHOUT BOTH: - an `/api/` path prefix, AND - the `POST` request method i.e. must have neither or either of these to match: ```caddy-d @without-both { not { path /api/* method POST } } ``` There's no [CEL expression](#expression) for this matcher, because you may use the `!` operator for negation instead. For example: ```caddy-d @without-both `!path('/api*') && !method('POST')` ``` Which is the same as this, using parentheses: ```caddy-d @without-both `!(path('/api*') || method('POST'))` ``` --- ### path ```caddy-d path expression path('') ``` By request path (the path component of the request URI). Path matches are exact but case-insensitive. Wildcards `*` may be used: - At the end only, for a prefix match (`/prefix/*`) - At the beginning only, for a suffix match (`*.suffix`) - On both sides only, for a substring match (`*/contains/*`) - In the middle only, for a globular match (`/accounts/*/info`) Slashes are significant. For example, `/foo*` will match `/foo`, `/foobar`, `/foo/`, and `/foo/bar`, but `/foo/*` will _not_ match `/foo` or `/foobar`. Request paths are cleaned to resolve directory traversal dots before matching. Additionally, multiple slashes are merged unless the match pattern has multiple slashes. In other words, `/foo` will match `/foo` and `//foo`, but `//foo` will only match `//foo`. Because there are multiple escaped forms of any given URI, the request path is normalized (URL-decoded, unescaped) except for those escape sequences at positions where escape sequences are also present in the match pattern. For example, `/foo/bar` matches both `/foo/bar` and `/foo%2Fbar`, but `/foo%2Fbar` will match only `/foo%2Fbar`, because the escape sequence is explicitly given in the configuration. The special wildcard escape `%*` can also be used instead of `*` to leave its matching span escaped. For example, `/bands/*/*` will not match `/bands/AC%2FDC/T.N.T` because the path will be compared in normalized space where it looks like `/bands/AC/DC/T.N.T`, which does not match the pattern; however, `/bands/%*/*` will match `/bands/AC%2FDC/T.N.T` because the span represented by `%*` will be compared without decoding escape sequences. Multiple paths will be OR'ed together. #### Examples: Match multiple directories and their contents: ```caddy-d @assets path /js/* /css/* /images/* ``` Match a specific file: ```caddy-d @favicon path /favicon.ico ``` Match file extensions: ```caddy-d @extensions path *.js *.css ``` With a [CEL expression](#expression): ```caddy-d @assets `path('/js/*', '/css/*', '/images/*')` ``` --- ### path_regexp ```caddy-d path_regexp [] expression path_regexp('', '') expression path_regexp('') ``` Like [`path`](#path), but supports regular expressions. Runs against the URI-decoded/unescaped path. 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). As of v2.8.0, if `name` is _not_ provided, the name will be taken from the named matcher's name. For example a named matcher `@foo` will cause this matcher to be named `foo`. The main advantage of specifying a name is if more than one regexp matcher (e.g. `path_regexp` and [`header_regexp`](#header-regexp)) is used in the same named matcher. Capture groups can be accessed via [placeholder](/docs/caddyfile/concepts#placeholders) in directives after matching: - `{re..}` where: - `` is the name of the regular expression, - `` is either the name or number of the capture group in the expression. - `{re.}` without a name, is also populated for convenience. The caveat is that if multiple regexp matchers are used in sequence, then the placeholder values will be overwritten by the next matcher. Capture group `0` is the full regexp match, `1` is the first capture group, `2` is the second capture group, and so on. So `{re.foo.1}` or `{re.1}` will both hold the value of the first capture group. There can only be one `path_regexp` pattern per named matcher, since this matcher cannot be merged with itself; if you need more, consider using an [`expression` matcher](#expression). #### Example: Match requests where the path ends a 6 character hex string followed by `.css` or `.js` as the file extension, with capture groups (parts enclosed in `( )`), that can be accessed with `{re.static.1}` and `{re.static.2}` (or `{re.1}` and `{re.2}`), respectively: ```caddy-d @static path_regexp static \.([a-f0-9]{6})\.(css|js)$ ``` This can be simplified by omitting the name, which will be inferred from the named matcher: ```caddy-d @static path_regexp \.([a-f0-9]{6})\.(css|js)$ ``` Or the same, using a [CEL expression](#expression), also validating that the [`file`](#file) exists on disk: ```caddy-d @static `path_regexp('\.([a-f0-9]{6})\.(css|js)$') && file()` ``` --- ### protocol ```caddy-d protocol http|https|grpc|http/[+] expression protocol('http|https|grpc|http/[+]') ``` By request protocol. A broad protocol name such as `http`, `https`, or `grpc` can be used; or specific or minimum HTTP versions such as `http/1.1` or `http/2+`. There can only be one `protocol` matcher per named matcher. #### Example: Match requests using HTTP/2: ```caddy-d @http2 protocol http/2+ ``` With a [CEL expression](#expression): ```caddy-d @http2 `protocol('http/2+')` ``` --- ### query ```caddy-d query =... query "" expression query({'': ''}) expression query({'': ['']}) ``` By query string parameters. Should be a sequence of `key=value` pairs, or an empty string "". Keys are matched exactly (case-sensitively) but also support `*` to match any value. Values can use placeholders. Empty string matches http requests with no query parameters. There can be multiple `query` matchers per named matcher, and pairs with the same keys will be OR'ed together. Different keys will be AND'ed together. So, all keys in the matcher must have at least one matching value. Illegal query strings (bad syntax, unescaped semicolons, etc.) will fail to parse and thus will not match. **NOTE:** Query string parameters are arrays, not singular values. This is because repeated keys are valid in query strings, and each one may have a different value. This matcher will match for a key if any one of its configured values is assigned in the query string. Backend applications using query strings MUST take into consideration that query string values are arrays and can have multiple values. #### Example: Match a `q` query parameter with any value: ```caddy-d @search query q=* ``` Match a `sort` query parameter with the value `asc` or `desc`: ```caddy-d @sorted query sort=asc sort=desc ``` Matching both `q` and `sort`, with a [CEL expression](#expression): ```caddy-d @search-sort `query({'sort': ['asc', 'desc'], 'q': '*'})` ``` --- ### remote_ip ```caddy-d remote_ip expression remote_ip('') ``` By remote IP address (i.e. the IP address of the immediate peer). Accepts exact IPs or CIDR ranges. IPv6 zones are supported. As a shortcut, `private_ranges` can be used to match all private IPv4 and IPv6 ranges. It's the same as specifying all of these ranges: `192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8 fd00::/8 ::1` if you wish to match the "real IP" of the client, as parsed from HTTP headers, use the [`client_ip`](#client-ip) matcher instead. There can be multiple `remote_ip` matchers per named matcher, and their ranges will be merged and OR'ed together. #### Example: Match requests from private IPv4 addresses: ```caddy-d @private-ipv4 remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8 ``` This matcher is commonly paired with the [`not`](#not) matcher to invert the match. For example, to abort all connections from _public_ IPv4 and IPv6 addresses (which is the inverse of all private ranges): ```caddy example.com { @denied not remote_ip private_ranges abort @denied respond "Hello, you must be from a private network!" } ``` In a [CEL expression](#expression), it would look like this: ```caddy-d @my-friends `remote_ip('12.23.34.45', '23.34.45.56')` ``` --- ### vars ```caddy-d vars ``` By the value of a variable in the request context, or the value of a placeholder. Multiple values may be specified to match any of those possible values (OR'ed). The **<variable>** argument may be either a variable name or a placeholder in curly braces `{ }`. (Placeholders are not expanded in the first parameter.) This matcher is most useful when paired with the [`map` directive](/docs/caddyfile/directives/map) which sets outputs, with the [`vars` directive](/docs/caddyfile/directives/vars) within your routes, or with plugins which set some information in the request context. #### Example: Match an output of the [`map` directive](/docs/caddyfile/directives/map) named `magic_number` for the values `3` or `5`: ```caddy-d vars {magic_number} 3 5 ``` Match an arbitrary placeholder's value, i.e. the authenticated user's ID, either `Bob` or `Alice`: ```caddy-d vars {http.auth.user.id} Bob Alice ``` A complete example using the [`vars` directive](/docs/caddyfile/directives/vars) to set a variable, and then matching on it with the [`vars` matcher](#vars). Here we combine two request headers into one variable, and match on that variable: ```caddy example.com { vars combined_header "{header.Foo}_{header.Bar}" @special vars {vars.combined_header} "123_456" handle @special { respond "You sent Foo=123 and Bar=456!" } handle { respond "Foo and Bar were not special." } } ``` --- ### vars_regexp ```caddy-d vars_regexp [] ``` Like [`vars`](#vars), but supports regular expressions. 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). As of v2.8.0, if `name` is _not_ provided, the name will be taken from the named matcher's name. For example a named matcher `@foo` will cause this matcher to be named `foo`. The main advantage of specifying a name is if more than one regexp matcher (e.g. `vars_regexp` and [`header_regexp`](#header-regexp)) is used in the same named matcher. Capture groups can be accessed via [placeholder](/docs/caddyfile/concepts#placeholders) in directives after matching: - `{re..}` where: - `` is the name of the regular expression, - `` is either the name or number of the capture group in the expression. - `{re.}` without a name, is also populated for convenience. The caveat is that if multiple regexp matchers are used in sequence, then the placeholder values will be overwritten by the next matcher. Capture group `0` is the full regexp match, `1` is the first capture group, `2` is the second capture group, and so on. So `{re.foo.1}` or `{re.1}` will both hold the value of the first capture group. Only one regular expression is supported per variable name, since regexp patterns cannot be merged; if you need more, consider using an [`expression` matcher](#expression). Matches against multiple different variables will be AND'ed. #### Example: Match an output of the [`map` directive](/docs/caddyfile/directives/map) named `magic_number` for a value starting with `4`, capturing the value in a capture group that can be accessed with `{re.magic.1}` or `{re.1}`: ```caddy-d @magic vars_regexp magic {magic_number} ^(4.*) ``` This can be simplified by omitting the name, which will be inferred from the named matcher: ```caddy-d @magic vars_regexp {magic_number} ^(4.*) ``` --- --- title: Global options (Caddyfile) --- # Global options The Caddyfile has a way for you to specify options that apply globally. Some options act as default values; others customize HTTP servers and don't apply to just one particular site; while yet others customize the behavior of the Caddyfile [adapter](/docs/config-adapters). The very top of your Caddyfile can be a **global options block**. This is a block that has no keys: ```caddy { ... } ``` There can only be one at most, and it must be the first block of the Caddyfile. Possible options are (click on each option to jump to its documentation): ```caddy { # General Options debug http_port https_port default_bind order first|last|[before|after ] storage { } storage_clean_interval admin off| { origins enforce_origin } persist_config off log [name] { output ... format ... level include exclude } grace_period shutdown_delay metrics { per_host } # TLS Options auto_https off|disable_redirects|ignore_loaded_certs|disable_certs email default_sni fallback_sni local_certs skip_install_trust acme_ca acme_ca_root acme_eab { key_id mac_key } acme_dns ... dns ... ech { dns ... } on_demand_tls { ask permission } key_type ed25519|p256|p384|rsa2048|rsa4096 cert_issuer ... renew_interval cert_lifetime ocsp_interval ocsp_stapling off preferred_chains [smallest] { root_common_name any_common_name } # Server Options servers [] { name listener_wrappers { } timeouts { read_body read_header write idle } keepalive_interval trusted_proxies ... client_ip_headers trace max_header_size enable_full_duplex log_credentials protocols [h1|h2|h2c|h3] strict_sni_host [on|insecure_off] } # File Systems filesystem { } # PKI Options pki { ca [] { name root_cn intermediate_cn intermediate_lifetime root { format cert key } intermediate { format cert key } } } # Event options events { on } } ``` ## General Options ##### `debug` Enables debug mode, which sets the log level to `DEBUG` for the [default logger](#log). This reveals more details that can be useful when troubleshooting (and is very verbose in production). We ask that you enable this before asking for help on the [community forums](https://caddy.community). For example, at the top of your Caddyfile, if you have no other global options: ```caddy { debug } ``` ##### `http_port` The port for the server to use for HTTP. **For internal use only**; does not change the HTTP port for clients. This is typically used if within your internal network, you needed to port forward `80` to a different port (e.g. `8080`) before it reaches Caddy, for routing purposes. Default: `80` ##### `https_port` The port for the server to use for HTTPS. **For internal use only**; does not change the HTTPS port for clients. This is typically used if within your internal network, you needed to port forward `443` to a different port (e.g. `8443`) before it reaches Caddy, for routing purposes. Default: `443` ##### `default_bind` The default bind address(es) to be used for all sites, if the [`bind` directive](/docs/caddyfile/directives/bind) is not used in the site. Default: empty, which binds to all interfaces. ```caddy { default_bind 10.0.0.1 } ``` ##### `order` Assigns an order to HTTP handler directive(s). As HTTP handlers execute in a sequential chain, it is necessary for the handlers to be executed in the right order. Standard directives have [a pre-defined order](/docs/caddyfile/directives#directive-order), but if using third-party HTTP handler modules, you'll need to define the order explicitly by either using this option or placing the directive in a [`route` block](/docs/caddyfile/directives/route). Ordering can be described absolutely (`first` or `last`), or relatively (`before` or `after`) to another directive. For example, to use the [`replace-response` plugin](https://github.com/caddyserver/replace-response), you'd want to make sure its directive is ordered after `encode` so that it can perform replacements before the response is encoded (because responses flow up the handler chain, not down): ```caddy { order replace after encode } ``` ##### `storage` Configures Caddy's storage mechanism. The default is [`file_system`](/docs/json/storage/file_system/). There are many other available [storage modules](/docs/json/storage/) provided as plugins. For example, to change the file system's storage location: ```caddy { storage file_system /path/to/custom/location } ``` Customizing the storage module is typically needed when syncing Caddy's storage across multiple instances of Caddy to make sure they all use the same certificates and keys. See the [Automatic HTTPS section on storage](/docs/automatic-https#storage) for more details. ##### `storage_clean_interval` How often to scan storage units for old or expired assets and remove them. These scans exert lots of reads (and list operations) on the storage module, so choose a longer interval for large deployments. Accepts [duration values](/docs/conventions#durations). Storage will always be cleaned when the process first starts. Then, a new cleaning will be started this duration after the previous cleaning started if the previous cleaning finished in less than half the time of this interval (otherwise next start will be skipped). Default: `24h` ```caddy { storage_clean_interval 7d } ``` ##### `admin` Customizes the [admin API endpoint](/docs/api). Accepts placeholders. Takes [network addresses](/docs/conventions#network-addresses). Default: `localhost:2019`, unless the `CADDY_ADMIN` environment variable is set. If set to `off`, then the admin endpoint will be disabled. When disabled, **config changes will be impossible** without stopping and starting the server, since the [`caddy reload` command](/docs/command-line#caddy-reload) uses the admin API to push the new config to the running server. Remember to use the `--address` CLI flag with compatible [commands](/docs/command-line) to specify the current admin endpoint, if the address of the running server was changed from the default. Also supports these sub-options: - **origins** configures the list of origins that are allowed to connect to the endpoint. A default is intelligently chosen: - if the listen address is loopback (e.g. `localhost` or a loopback IP, or a unix socket) then the allowed origins are `localhost`, `::1` and `127.0.0.1`, joined with the listen address port (so `localhost:2019` is a valid origin). - if the listen address is not loopback, then the allowed origin is the same as the listen address. If the listen address host is not a wildcard interface (wildcards include: empty string, or `0.0.0.0`, or `[::]`), then `Host` header enforcement is performed. Effectively, this means that by default, the `Host` header is validated to be in `origins`, since the interface is `localhost`. But for an address like `:2020` which has a wildcard interface, `Host` header validation is not performed. - **enforce_origin** enables enforcement of the `Origin` request header. This is most useful when the listen address is a wildcard interface (since `Host` is not validated), and the admin API is exposed to the public internet. It enables CORS preflight checks and ensures that the `Origin` header is validated against the `origins` list. Only use this if you're running Caddy on your development machine and need to access the admin API from a web browser. For example, to expose the admin API on a different port, on all interfaces — ⚠️ this port **should not be exposed publicly**, otherwise anyone can control your server; consider enabling origin enforcement if you need it to be public: ```caddy { admin :2020 } ``` To turn off the admin API — ⚠️ this makes **config reloads impossible** without stopping and starting the server: ```caddy { admin off } ``` To use a [unix socket](/docs/conventions#network-addresses) for the admin API, allowing access control via file permissions: ```caddy { admin unix//run/caddy-admin.sock } ``` To only allow requests having a matching `Origin` header: ```caddy { admin :2019 { origins localhost enforce_origin } } ``` ##### `persist_config` Controls whether the current JSON config should be persisted to the [configuration directory](/docs/conventions#configuration-directory), to avoid losing config changes performed via the admin API. Currently, only the `off` option is supported. By default, the config is persisted. ```caddy { persist_config off } ``` ##### `log` Configures named loggers. The name can be passed to indicate a specific logger for which to customize the behavior. If no name is specified, the behavior of the `default` logger is modified. You can read more about the `default` logger and an explanation of [how logging works in Caddy](/docs/logging). Multiple loggers with different names can be configured by using the `log` multiple times. This differs from the [`log` directive](/docs/caddyfile/directives/log), which only configures HTTP request logging (also known as access logs). The `log` global option shares its configuration structure with the directive (except for `include` and `exclude`), and complete documentation can be found on the directive's page. - **output** configures where to write the logs. See the [`log` directive](/docs/caddyfile/directives/log#output-modules) for complete documentation. - **format** describes how to encode, or format, the logs. See the [`log` directive](/docs/caddyfile/directives/log#format-modules) for complete documentation. - **level** is the minimum entry level to log. Default: `INFO`. Possible values: `DEBUG`, `INFO`, `WARN`, `ERROR`, and very rarely, `PANIC`, `FATAL`. - **include** specifies the log names to be included in this logger. By default, this list is empty (i.e. all logs are included). For example, to include only logs emitted by the admin API, you would include `admin.api`. - **exclude** specifies the log names to be excluded from this logger. By default, this list is empty (i.e. no logs are excluded). For example, to exclude only HTTP access logs, you would exclude `http.log.access`. The logger names that `include` and `exclude` accept depend on the modules used, and easiest way to discover them is from prior logs. Here is an example logging as json all http access logs and admin logs to stdout: ```caddy { log default { output stdout format json include http.log.access admin.api } } ``` ##### `grace_period` Defines the grace period for shutting down HTTP servers (i.e. during config changes or when Caddy is stopping). During the grace period, no new connections are accepted, idle connections are closed, and active connections are impatiently waited upon to finish their requests. If clients do not finish their requests within the grace period, the server will be forcefully terminated to allow the reload to complete and free up resources. Accepts [duration values](/docs/conventions#durations). By default, the grace period is eternal, which means connections are never forcefully closed. ```caddy { grace_period 10s } ``` ##### `shutdown_delay` Defines a [duration](/docs/conventions#durations) _before_ the [grace period](#grace_period) during which a server that is going to be stopped continues to operate normally, except the `{http.shutting_down}` placeholder evaluates to `true` and `{http.time_until_shutdown}` gives the time until the grace period begins. This causes a delay if any server is being shut down as part of a config change, and effectively schedules the change for a later time. It is useful for announcing to health checkers of this server's impending doom and to give time for a load balancer to move it out of the rotation; for example: ```caddy { shutdown_delay 30s } example.com { handle /health-check { @goingDown vars {http.shutting_down} true respond @goingDown "Bye-bye in {http.time_until_shutdown}" 503 respond 200 } handle { respond "Hello, world!" } } ``` ## TLS Options ##### `auto_https` Configures [Automatic HTTPS](/docs/automatic-https), which is the feature that enables Caddy to automate certificate management and HTTP-to-HTTPS redirects for your sites. There are a few modes to choose from: - `off`: Disables both certificate automation and HTTP-to-HTTPS redirects. - `disable_redirects`: Disable only HTTP-to-HTTPS redirects. - `disable_certs`: Disable only certificate automation. - `ignore_loaded_certs`: Automate certificates even for names which appear on manually-loaded certificates. Useful if you specified a certificate using the [`tls` directive](/docs/caddyfile/directives/tls) which contains names (or wildcards) that you instead want to be managed automatically. ```caddy { auto_https disable_redirects } ``` ##### `email` Your email address. Mainly used when creating an ACME account with your CA, and is highly recommended in case there are problems with your certificates. ```caddy { email admin@example.com } ``` ##### `default_sni` Sets a default TLS ServerName for when clients do not use SNI in their ClientHello. ```caddy { default_sni example.com } ``` ##### `fallback_sni` ⚠️ Experimental If configured, the fallback becomes the TLS ServerName in the ClientHello if the original ServerName doesn't match any certificates in the cache. The uses for this are very niche; typically if a client is a CDN and passes through the ServerName of the downstream handshake but can accept a certificate with the origin's hostname instead, then you would set this as your origin's hostname. Note that Caddy must be managing a certificate for this name. ```caddy { fallback_sni example.com } ``` ##### `local_certs` Causes **all** certificates to be issued internally by default, rather than through a (public) ACME CA such as Let's Encrypt. This is useful as a quick toggle in development environments. ```caddy { local_certs } ``` ##### `skip_install_trust` Skips the attempts to install the local CA's root into the system trust store, as well as into Java and Mozilla Firefox trust stores. ```caddy { skip_install_trust } ``` ##### `acme_ca` Specifies the URL to the ACME CA's directory. It is strongly recommended to set this to Let's Encrypt's [staging endpoint ](https://letsencrypt.org/docs/staging-environment/) for testing or development. Default: ZeroSSL and Let's Encrypt's production endpoints. Note that a globally-configured ACME CA may not apply to all sites; see the [hostname requirements](/docs/automatic-https#hostname-requirements) for using the default ACME issuer(s). ```caddy { acme_ca https://acme-staging-v02.api.letsencrypt.org/directory } ``` ##### `acme_ca_root` Specifies a PEM file that contains a trusted root certificate for ACME CA endpoints, if not in the system trust store. ```caddy { acme_ca_root /path/to/ca/root.pem } ``` ##### `acme_eab` Specifies an External Account Binding to use for all ACME transactions. For example, with mock ZeroSSL credentials: ```caddy { acme_eab { key_id GD-VvWydSVFuss_GhBwYQQ mac_key MjXU3MH-Z0WQ7piMAnVsCpD1shgMiWx6ggPWiTmydgUaj7dWWWfQfA } } ``` ##### `acme_dns` Configures the [ACME DNS challenge](/docs/automatic-https#dns-challenge) provider to use for all ACME transactions. Requires a custom build of Caddy with a plugin for your DNS provider. The tokens following the name of the provider set up the provider the same as if specified in the [`tls` directive's `acme` issuer](/docs/caddyfile/directives/tls#acme). ```caddy { acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN} } ``` ##### `dns` Configures a default DNS provider to use when none other is specified locally in a relevant context. For example, if the ACME DNS challenge is enabled but does not have a DNS provider configured, this global default will be used. It is also applied for publishing Encrypted ClientHello (ECH) configs. Your Caddy binary must be compiled with the specified DNS provider module for this to work. Example, using credentials from an environment variable: ```caddy { dns cloudflare {env.CLOUDFLARE_API_TOKEN} } ``` (Requires Caddy 2.10 beta 1 or newer.) ##### `ech` Enables Encrypted ClientHello (ECH) by using the specified public domain name(s) as the plaintext server name (SNI) in TLS handshakes. Given the right conditions, ECH can help protect the domain names of your sites on the wire during connections. Caddy will generate and publish one ECH config for each public name specified. Publication is how compatible clients (such as properly-configured modern browsers) know to use ECH to access your sites. In order to work properly, the ECH config(s) must be published in a way that clients expect. Most browsers (with DNS-over-HTTPS or DNS-over-TLS enabled) expect ECH configs to be published to HTTPS-type DNS records. Caddy does this kind of publication automatically, but you have to specify a DNS provider either with the `dns` sub-option, or globally with the [`dns` global option](#dns), and your Caddy binary must be built with the specified DNS provider module. (Custom builds are available on our [download page](/download).) **Privacy notices:** - It is generally advisable to **maximize the size of your [_anonymity set_](https://www.ietf.org/archive/id/draft-ietf-tls-esni-23.html#name-introduction)**. As such, we typically recommend that most users configure _only one_ public domain name to protect all your sites. - **Your server should be authoritative for the public domain name(s) you specify** (i.e. they should point to your server) because Caddy will obtain a certificate for them. These certificates are vital to help spec-conforming clients connect reliably and safely with ECH in some cases. They are only used to faciliate a proper ECH handshake, not used for application data (your sites -- unless you define a site that is the same as your public domain name). - Every circumstance may be different. We recommend consulting experts to **review your threat model** if the stakes are high, as ECH is not a one-size-fits-all solution. Example using credentials from an environment variable for publication to nameservers parked at Cloudflare: ```caddy { dns cloudflare {env.CLOUDFLARE_API_TOKEN} ech ech.example.net } ``` This should cause compatible clients to load all your sites with `ech.example.net`, rather than the individual site names exposed in plaintext. Successful publication requires that your site's domains are parked at the configured DNS provider and the records can be modified with the given credentials / provider configuration. (Requires Caddy 2.10 beta 1 or newer.) ##### `on_demand_tls` Configures [On-Demand TLS](/docs/automatic-https#on-demand-tls) where it is enabled, but does not enable it (to enable it, use the [`on_demand` subdirective of the `tls` directive](/docs/caddyfile/directives/tls#syntax)). Required for use in production environments, to prevent abuse. - **ask** will cause Caddy to make an HTTP request to the given URL, asking whether a domain is allowed to have a certificate issued. The request has a query string of `?domain=` containing the value of the domain name. If the endpoint returns a `2xx` status code, Caddy will be authorized to obtain a certificate for that name. Any other status code will result in cancelling issuance of the certificate and erroring the TLS handshake. - **permission** allows custom modules to be used to determine whether a certificate should be issued for a particular name. The module must implement the [`caddytls.OnDemandPermission` interface](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#OnDemandPermission). An `http` permission module is included, which is what the `ask` option uses, and remains as a shortcut for backwards compatibility. - ⚠️ **interval** and **burst** rate limiting options were available, but are NOT recommended. Remove them from your config if you still have them. ```caddy { on_demand_tls { ask http://localhost:9123/ask } } https:// { tls { on_demand } } ``` ##### `key_type` Specifies the type of key to generate for TLS certificates; only change this if you have a specific need to customize it. The possible values are: `ed25519`, `p256`, `p384`, `rsa2048`, `rsa4096`. ```caddy { key_type ed25519 } ``` ##### `cert_issuer` Defines the issuer (or source) of TLS certificates. This allows configuring issuers globally, instead of per-site as you would with the [`tls` directive's `issuer` subdirective](/docs/caddyfile/directives/tls#issuer). May be repeated if you wish to configure more than one issuer to try. They will be tried in the order they are defined. ```caddy { cert_issuer acme { ... } cert_issuer zerossl { ... } } ``` ##### `renew_interval` How often to scan all loaded, managed certificates for expiration, and trigger renewal if expired. Default: `10m` ```caddy { renew_interval 30m } ``` ##### `cert_lifetime` The validity period to ask the CA to issue a certificate for. This value is used to compute the `notAfter` field of the ACME order; therefore the system must have a reasonably synchronized clock. NOTE: Not all CAs support this. Check with your CA's ACME documentation to see if this is allowed and what values may be used. Default: `0` (CA chooses lifetime, usually 90 days) ⚠️ This is an experimental feature. Subject to change or removal. ```caddy { cert_lifetime 30d } ``` ##### `ocsp_interval` How often to check if [OCSP staples ](https://en.wikipedia.org/wiki/OCSP_stapling) need updating. Default: `1h` ```caddy { ocsp_interval 2h } ``` ##### `ocsp_stapling` Can be set to `off` to disable OCSP stapling. Useful in environments where responders are not reachable due to firewalls. ```caddy { ocsp_stapling off } ``` ##### `preferred_chains` If your CA provides multiple certificate chains, you can use this option to specify which chain Caddy should prefer. Set one of the following options: - **smallest** will tell Caddy to prefer chains with the fewest amount of bytes. - **root_common_name** is a list of one or more common names; Caddy will choose the first chain that has a root that matches with at least one of the specified common names. - **any_common_name** is a list of one or more common names; Caddy will choose the first chain that has an issuer that matches with at least one of the specified common names. Note that specifying `preferred_chains` as a global option will affect all issuers if there isn't any [overriding issuer level config](/docs/caddyfile/directives/tls#acme). ```caddy { preferred_chains smallest } ``` ```caddy { preferred_chains { root_common_name "ISRG Root X2" } } ``` ## Server Options Customizes [HTTP servers](/docs/json/apps/http/servers/) with settings that potentially span multiple sites, and thus can't be rightly configured in site blocks. These options affect the listener/socket or other facilities beneath the HTTP layer. Can be specified more than once with different `listener_address` values to configure different options per server. For example, `servers :443` will only apply to the server that is bound to the listener address `:443`. Omitting the listener address will apply the options to any remaining server. For example, to configure different options for the servers on ports `:80` and `:443`, you would specify two `servers` blocks: ```caddy { servers :443 { listener_wrappers { http_redirect tls } } servers :80 { protocols h1 h2c } } ``` When using `servers`, it will **only** apply to servers that **actually appear** in your Caddyfile (i.e. are produced by a site block). Remember, [Automatic HTTPS](/docs/automatic-https) will create a server listening on port `80` (or the [`http_port` option](#http_port)), to serve HTTP->HTTPS redirects and to solve the ACME HTTP challenge; this happens at runtime, i.e. _after_ the Caddyfile adapter applies `servers`. So in other words, this means that `servers` **will not** apply to `:80` unless you explicitly declare a site block like `http://` or `:80`. ##### `name` A custom name to assign to this server. Usually helpful to identify a server by its name in logs and metrics. If not set, Caddy will define it dynamically using a `srvX` pattern, where `X` starts with `0` and increments based on the number of servers in the config. Keep in mind that only servers produced by site blocks in your config will have settings applied. [Automatic HTTPS](/docs/automatic-https) creates an `:80` server (or [`http_port`](#http_port)) at runtime, so if you want rename it, you'll need at least an empty `http://` site block. For example: ```caddy { servers :443 { name https } servers :80 { name http } } example.com { } http:// { } ``` ##### `listener_wrappers` Allows configuring [listener wrappers](/docs/json/apps/http/servers/listener_wrappers/), which can modify the behaviour of the socket listener. They are applied in the given order. ###### `tls` The `tls` listener wrapper is a no-op listener wrapper that marks where the TLS listener should be in a chain of listener wrappers. It should only be used if another listener wrapper must be placed in front of the TLS handshake. ###### `http_redirect` The [`http_redirect`](/docs/json/apps/http/servers/listener_wrappers/http_redirect/) provides HTTP->HTTPS redirects for connections that come on the TLS port as an HTTP request, by detecting using the first few bytes that it's not a TLS handshake, but instead an HTTP request. This is most useful when serving HTTPS on a non-standard port (other than `443`), since browsers will try HTTP unless the scheme is specified. It must be placed _before_ the `tls` listener wrapper. Here's an example: ```caddy { servers { listener_wrappers { http_redirect tls } } } ``` ###### `proxy_protocol` The [`proxy_protocol`](/docs/json/apps/http/servers/listener_wrappers/proxy_protocol/) listener wrapper (prior to v2.7.0 it was only available via a plugin) enables [PROXY protocol](https://github.com/haproxy/haproxy/blob/master/doc/proxy-protocol.txt) parsing (popularized by HAProxy). This must be used _before_ the `tls` listener wrapper since it parses plaintext data at the start of the connection: ```caddy-d proxy_protocol { timeout allow deny fallback_policy } ``` - **timeout** specifies the maximum duration to wait for the PROXY header. Defaults to `5s`. - **allow** is a list of CIDR ranges of trusted sources to receive PROXY headers. Unix sockets are trusted by default and not part of this option. - **deny** is a list of CIDR ranges of trusted sources to reject PROXY headers from. - **fallback_policy** is the action to take if the PROXY header comes from an address that not in either list of allow/deny. The default fallback policy is `ignore`. Accepted values of `fallback_policy` are: - `ignore`: address from PROXY header, but accept connection - `use`: address from PROXY header - `reject`: connection when PROXY header is sent - `require`: connection to send PROXY header, reject if not present - `skip`: accepts a connection without requiring the PROXY header. For example, for an HTTPS server (needing the `tls` listener wrapper) that accepts PROXY headers from a specific range of IP addresses, and rejects PROXY headers from a different range, with a timeout of 2 seconds: ```caddy { servers { listener_wrappers { proxy_protocol { timeout 2s allow 192.168.86.1/24 192.168.86.1/24 deny 10.0.0.0/8 fallback_policy reject } tls } } } ``` ##### `timeouts` - **read_body** is a [duration value](/docs/conventions#durations) that sets how long to allow a read from a client's upload. Setting this to a short, non-zero value can mitigate slowloris attacks, but may also affect legitimately slow clients. Defaults to no timeout. - **read_header** is a [duration value](/docs/conventions#durations) that sets how long to allow a read from a client's request headers. Defaults to no timeout. - **write** is a [duration value](/docs/conventions#durations) that sets how long to allow a write to a client. Note that setting this to a small value when serving large files may negatively affect legitimately slow clients. Defaults to no timeout. - **idle** is a [duration value](/docs/conventions#durations) that sets the maximum time to wait for the next request when keep-alives are enabled. Defaults to 5 minutes to help avoid resource exhaustion. ```caddy { servers { timeouts { read_body 10s read_header 5s write 30s idle 10m } } } ``` ##### `keepalive_interval` The interval at which TCP keepalive packets are sent to keep the connection alive at the TCP layer when no other data is being transmitted. Defaults to `15s`. ```caddy { servers { keepalive_interval 30s } } ``` ##### `trusted_proxies` Allows configuring IP ranges (CIDRs) of proxy servers from which requests should be trusted. By default, no proxies are trusted. Enabling this causes trusted requests to have the _real_ client IP parsed from HTTP headers (by default, `X-Forwarded-For`; see [`client_ip_headers`](#client-ip-headers) to configure other headers). If trusted, the client IP is added to [access logs](/docs/caddyfile/directives/log), is available as a `{client_ip}` [placeholder](/docs/caddyfile/concepts#placeholders), and allows the use of the [`client_ip` matcher](/docs/caddyfile/matchers#client-ip). If the request is not from a trusted proxy, then the client IP is set to the remote IP address of the direct incoming connection. By default, the IPs in headers are parsed left-to-right. See [`trusted_proxies_strict`](#trusted-proxies-strict) to alter this behaviour. Some matchers or handlers may use the trust status of the request to make decisions. For example, if trusted, the [`reverse_proxy`](/docs/caddyfile/directives/reverse_proxy#defaults) handler will proxy and augment the sensitive `X-Forwarded-*` request headers. Currently, only the `static` [IP source module](/docs/json/apps/http/servers/trusted_proxies/) is included with the standard distribution of Caddy, but this can be [extended](/docs/extending-caddy) with plugins to maintain a dynamic list of IP ranges. ###### `static` Takes a static (unchanging) list of IP ranges (CIDRs) to trust. As a shortcut, `private_ranges` can be used to match all private IPv4 and IPv6 ranges. It's the same as specifying all of these ranges: `192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8 fd00::/8 ::1`. The syntax is as follows: ```caddy-d trusted_proxies static [private_ranges] ``` Here's a complete example, trusting an example IPv4 range and an IPv6 range: ```caddy { servers { trusted_proxies static 12.34.56.0/24 1200:ab00::/32 } } ``` ##### `trusted_proxies_strict` When [`trusted_proxies`](#trusted-proxies) is enabled, the IPs in the headers (configured by [`client_ip_headers`](#client-ip-headers)) are parsed from left-to-right by default. The first untrusted IP address found becomes the real client address. Since v2.8, you can opt-in to right-to-left parsing of these headers with `trusted_proxies_strict`. By default, this option is disabled for backwards compatibility. Upstream proxies such as HAProxy, CloudFlare, AWS ALB, CloudFront, etc. will append each new connecting remote address to the right of `X-Forwarded-For`. It is recommended to enable `trusted_proxies_strict` when working with these, as the left-most IP address may be spoofed by the client. ```caddy { servers { trusted_proxies static private_ranges trusted_proxies_strict } } ``` ##### `client_ip_headers` Pairing with [`trusted_proxies`](#trusted-proxies), allows configuring which headers to use to determine the client's IP address. By default, only `X-Forwarded-For` is considered. Multiple header fields can be specified, in which case the first non-empty header value is used. ```caddy { servers { trusted_proxies static private_ranges client_ip_headers X-Forwarded-For X-Real-IP } } ``` ##### `metrics` Enables Prometheus metrics collection; necessary before scraping metrics. Note that metrics reduce performance on really busy servers. (Our community is working on improving this. Please get involved!) ```caddy { metrics } ``` You can add the `per_host` option to label metrics with the host name of the metric. ```caddy { metrics { per_host } } ``` ##### `trace` Log each individual handler that is invoked. Requires that the log emit at `DEBUG` level ( You may do so with the [`debug` global option](#debug)). NOTE: This may log the configuration of your HTTP handler modules; do not enable this in insecure contexts when there is sensitive data in the configuration. ⚠️ This is an experimental feature. Subject to change or removal. ```caddy { servers { trace } } ``` ##### `max_header_size` The maximum size to parse from a client's HTTP request headers. If the limit is exceeded, the server will respond with HTTP status `431 Request Header Fields Too Large`. It accepts all formats supported by [go-humanize](https://github.com/dustin/go-humanize/blob/master/bytes.go). By default, the limit is `1MB`. ```caddy { servers { max_header_size 5MB } } ``` ##### `enable_full_duplex` Enable full-duplex communication for HTTP/1 requests. Only has an effect if Caddy was built with Go 1.21 or later. For HTTP/1 requests, the Go HTTP server by default consumes any unread portion of the request body before beginning to write the response, preventing handlers from concurrently reading from the request and writing the response. Enabling this option disables this behavior and permits handlers to continue to read from the request while concurrently writing the response. For HTTP/2+ requests, the Go HTTP server always permits concurrent reads and responses, so this option has no effect. Test thoroughly with your HTTP clients, as some older clients may not support full-duplex HTTP/1 which can cause them to deadlock. See [golang/go#57786](https://github.com/golang/go/issues/57786) for more info. ⚠️ This is an experimental feature. Subject to change or removal. ```caddy { servers { enable_full_duplex } } ``` ##### `log_credentials` By default, access logs (enabled with the [`log` directive](/docs/caddyfile/directives/log)) with headers that contain potentially sensitive information (`Cookie`, `Set-Cookie`, `Authorization` and `Proxy-Authorization`) will be logged as `REDACTED`. If you wish to _not_ have these headers redacted, you may enable the `log_credentials` option. ```caddy { servers { log_credentials } } ``` ##### `protocols` The space-separated list of HTTP protocols to support. Default: `h1 h2 h3` Accepted values are: - `h1` for HTTP/1.1 - `h2` For HTTP/2 - `h2c` for HTTP/2 over cleartext - `h3` for HTTP/3 Currently, enabling HTTP/2 (including H2C) necessarily implies enabling HTTP/1.1 because the Go standard library does not let us disable HTTP/1.1 when using its HTTP server. However, either HTTP/1.1 or HTTP/3 can be enabled independently. Note that H2C ("Cleartext HTTP/2" or "H2 over TCP") and HTTP/3 are not implemented by the Go standard library, so some functionality or features may be limited. We recommend against enabling H2C unless it is absolutely necessary for your application. ```caddy { servers :80 { protocols h1 h2c } } ``` ##### `strict_sni_host` Enabling this requires that a request's `Host` header matches the value of the `ServerName` sent by the client's TLS ClientHello, a necessary safeguard when using TLS client authentication. If there's a mismatch, HTTP status `421 Misdirected Request` response is written to the client. This option will automatically be turned on if [client authentication](/docs/caddyfile/directives/tls#client_auth) is configured. This disallows TLS client auth bypass (domain fronting) which could otherwise be exploited by sending an unprotected SNI value during a TLS handshake, then putting a protected domain in the Host header after establishing connection. This behavior is a safe default, but you may explicitly turn it off with `insecure_off`; for example in the case of running a proxy where domain fronting is desired and access is not restricted based on hostname. ```caddy { servers { strict_sni_host on } } ``` ## File Systems The `filesystem` global option allows declaring one or more file systems that can be used for 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. File systems are declared with a name to identify them. This means you can connect to more than one file system of the same type, if you need to. By default, Caddy doesn't have any file system modules, so you'll need to build Caddy with a plugin for the file system you want to use. #### Example Using an imaginary `custom` file system module, you could declare two file systems: ```caddy { filesystem foo custom { ... } filesystem bar custom { ... } } foo.example.com { fs foo file_server } foo.example.com { fs bar file_server } ``` ## PKI Options The PKI (Public Key Infrastructure) app is the foundation for Caddy's [Local HTTPS](/docs/automatic-https#local-https) and [ACME server](/docs/caddyfile/directives/acme_server) features. The app defines certificate authorities (CAs) which are capable of signing certificates. The default CA ID is `local`. If the ID is omitted when configuring the `ca`, then `local` is assumed. ##### `name` The user-facing name of the certificate authority. Default: `Caddy Local Authority` ```caddy { pki { ca local { name "My Local CA" } } } ``` ##### `root_cn` The name to put in the CommonName field of the root certificate. Default: `{pki.ca.name} - {time.now.year} ECC Root` ```caddy { pki { ca local { root_cn "My Local CA - 2024 ECC Root" } } } ``` ##### `intermediate_cn` The name to put in the CommonName field of the intermediate certificates. Default: `{pki.ca.name} - ECC Intermediate` ```caddy { pki { ca local { intermediate_cn "My Local CA - ECC Intermediate" } } } ``` ##### `intermediate_lifetime` The [duration](/docs/conventions#durations) for which intermediate certificates are valid. This value **must** be less than the lifetime of the root cert (`3600d` or 10 years). Default: `7d`. It is _not recommended_ to change this, unless absolutely necessary. ```caddy { pki { ca local { intermediate_lifetime 30d } } } ``` ##### `root` A key pair (certificate and private key) to use as the root for the CA. If not specified, one will be generated and managed automatically. - **format** is the format in which the certificate and private key are provided. Currently, only `pem_file` is supported, which is the default, so this field is optional. - **cert** is the certificate. This should be the path to a PEM file, when using `pem_file` format. - **key** is the private key. This should be the path to a PEM file, when using `pem_file` format. ##### `intermediate` A key pair (certificate and private key) to use as the intermediate for the CA. If not specified, one will be generated and managed automatically. - **format** is the format in which the certificate and private key are provided. Currently, only `pem_file` is supported, which is the default, so this field is optional. - **cert** is the certificate. This should be the path to a PEM file, when using `pem_file` format. - **key** is the private key. This should be the path to a PEM file, when using `pem_file` format. ```caddy { pki { ca local { root { format pem_file cert /path/to/root.pem key /path/to/root.key } intermediate { format pem_file cert /path/to/intermediate.pem key /path/to/intermediate.key } } } } ``` ## Event Options Caddy modules emit events when interesting things happen (or are about to happen). Events typically include a metadata payload. The best way to learn about events and their payloads is from each module's documentation, but you may also see the events and their data payloads by enabling the [`debug` global option](#debug) and reading the logs. ##### `on` Binds an event handler to the named event. Specify the name of the event handler module, followed by its configuration. For example, to run a command after a certificate is obtained ([third-party plugin ](https://github.com/mholt/caddy-events-exec) required), with a part of the event payload being passed to the script using a placeholder: ```caddy { events { on cert_obtained exec ./my-script.sh {event.data.certificate_path} } } ``` ### Events These standard events are emitted by Caddy: - [`tls` events ](https://github.com/caddyserver/certmagic#events) - [`reverse_proxy` events](/docs/caddyfile/directives/reverse_proxy#events) Plugins may also emit events, so check their documentation for details. --- --- title: Common Caddyfile Patterns --- # Common Caddyfile Patterns This page demonstrates a few complete and minimal Caddyfile configurations for common use cases. These can be helpful starting points for your own Caddyfile documents. These are not drop-in solutions; you will have to customize your domain name, ports/sockets, directory paths, etc. They are intended to illustrate some of the most common configuration patterns. - [Static file server](#static-file-server) - [Reverse proxy](#reverse-proxy) - [PHP](#php) - [Redirect `www.` subdomain](#redirect-www-subdomain) - [Trailing slashes](#trailing-slashes) - [Wildcard certificates](#wildcard-certificates) - [Single-page apps (SPAs)](#single-page-apps-spas) - [Caddy proxying to another Caddy](#caddy-proxying-to-another-caddy) ## Static file server ```caddy example.com { root * /var/www file_server } ``` As usual, the first line is the site address. The [`root` directive](/docs/caddyfile/directives/root) specifies the path to the root of the site (the `*` means to match all requests, so as to disambiguate from a [path matcher](/docs/caddyfile/matchers#path-matchers))—change the path to your site if it isn't the current working directory. Finally, we enable the [static file server](/docs/caddyfile/directives/file_server). ## Reverse proxy Proxy all requests: ```caddy example.com { reverse_proxy localhost:5000 } ``` Only proxy requests having a path starting with `/api/` and serve static files for everything else: ```caddy example.com { root * /var/www reverse_proxy /api/* localhost:5000 file_server } ``` This uses a [request matcher](/docs/caddyfile/matchers#syntax) to match only requests that start with `/api/` and proxy them to the backend. All other requests will be served from the site [`root`](/docs/caddyfile/directives/root) with the [static file server](/docs/caddyfile/directives/file_server). This also depends on the fact that `reverse_proxy` is higher on the [directive order](/docs/caddyfile/directives#directive-order) than `file_server`. There are many more [`reverse_proxy` examples here](/docs/caddyfile/directives/reverse_proxy#examples). ## PHP ### PHP-FPM With a PHP FastCGI service running, something like this works for most modern PHP apps: ```caddy example.com { root * /srv/public encode php_fastcgi localhost:9000 file_server } ``` Customize the site root accordingly; this example assumes that your PHP app's webroot is within a `public` directory—requests for files that exist on disk will be served with [`file_server`](/docs/caddyfile/directives/file_server), and anything else will be routed to `index.php` for handling by the PHP app. You may sometimes use a unix socket to connect to PHP-FPM: ```caddy-d php_fastcgi unix//run/php/php8.2-fpm.sock ``` The [`php_fastcgi` directive](/docs/caddyfile/directives/php_fastcgi) is actually just a shortcut for [several pieces of configuration](/docs/caddyfile/directives/php_fastcgi#expanded-form). ### FrankenPHP Alternatively, you may use [FrankenPHP](https://frankenphp.dev/), which is a distribution of Caddy which calls PHP directly using CGO (Go to C bindings). This can be up to 4x faster than with PHP-FPM, and even better if you can use the worker mode. ```caddy { frankenphp order php_server before file_server } example.com { root * /srv/public encode zstd br gzip php_server } ``` ## Redirect `www.` subdomain To **add** the `www.` subdomain with an HTTP redirect: ```caddy example.com { redir https://www.{host}{uri} } www.example.com { } ``` To **remove** it: ```caddy www.example.com { redir https://example.com{uri} } example.com { } ``` To remove it for **multiple domains** at once; this uses the `{labels.*}` placeholders which are the parts of the hostname, `0`-indexed from the right (e.g. `0`=`com`, `1`=`example-one`, `2`=`www`): ```caddy www.example-one.com, www.example-two.com { redir https://{labels.1}.{labels.0}{uri} } example-one.com, example-two.com { } ``` ## Trailing slashes You will not usually need to configure this yourself; the [`file_server` directive](/docs/caddyfile/directives/file_server) will automatically add or remove trailing slashes from requests by way of HTTP redirects, depending on whether the requested resource is a directory or file, respectively. However, if you need to, you can still enforce trailing slashes with your config. There are two ways to do it: internally or externally. ### Internal enforcement This uses the [`rewrite`](/docs/caddyfile/directives/rewrite) directive. Caddy will rewrite the URI internally to add or remove the trailing slash: ```caddy example.com { rewrite /add /add/ rewrite /remove/ /remove } ``` Using a rewrite, requests with and without the trailing slash will be the same. ### External enforcement This uses the [`redir`](/docs/caddyfile/directives/redir) directive. Caddy will ask the browser to change the URI to add or remove the trailing slash: ```caddy example.com { redir /add /add/ redir /remove/ /remove } ``` Using a redirect, the client will have to re-issue the request, enforcing a single acceptable URI for a resource. ## Wildcard certificates For most issuers including Let's Encrypt, you must enable the [ACME DNS challenge](/docs/automatic-https#dns-challenge) to have Caddy automate wildcard certificates. With the DNS challenge enabled, as of Caddy 2.10, Caddy will prefer an applicable wildcard certificate that is already configured or managed before managing a separate certificate for a subdomain. If you need to serve multiple subdomains with the same wildcard certificate, the best way to handle them is with a Caddyfile like this, making use of the [`handle` directive](/docs/caddyfile/directives/handle) and [`host` matchers](/docs/caddyfile/matchers#host): ```caddy *.example.com { tls { dns [] } @foo host foo.example.com handle @foo { respond "Foo!" } @bar host bar.example.com handle @bar { respond "Bar!" } # Fallback for otherwise unhandled domains handle { abort } } ``` You must enable the [ACME DNS challenge](/docs/automatic-https#dns-challenge) to have Caddy automatically manage wildcard certificates. ## Single-page apps (SPAs) When a web page does its own routing, servers may receive lots of requests for pages that don't exist server-side, but which are renderable client-side as long as the singular index file is served instead. Web applications architected like this are known as SPAs, or single-page apps. The main idea is to have the server "try files" to see if the requested file exists server-side, and if not, fall back to an index file where the client does the routing (usually with client-side JavaScript). A typical SPA config usually looks something like this: ```caddy example.com { root * /srv encode try_files {path} /index.html file_server } ``` If your SPA is coupled with an API or other server-side-only endpoints, you will want to use `handle` blocks to treat them exclusively: ```caddy example.com { encode handle /api/* { reverse_proxy backend:8000 } handle { root * /srv try_files {path} /index.html file_server } } ``` If your `index.html` contains references to your JS/CSS assets with hashed filenames, you may want to consider adding a `Cache-Control` header to instruct clients to _not_ cache it (so that if the assets change, browsers fetch the new ones). Since the `try_files` rewrite is used to serve your `index.html` from any path that doesn't match another file on disk, you can wrap the `try_files` with a `route` so that the `header` handler runs _after_ the rewrite (it normally would run before due to the [directive order](/docs/caddyfile/directives#directive-order)): ```caddy-d route { try_files {path} /index.html header /index.html Cache-Control "public, max-age=0, must-revalidate" } ``` ## Caddy proxying to another Caddy If you have one Caddy instance publicly accessible (let's call it "front"), and another Caddy instance in your private network (let's call it "back") serving your actual app, you can use the [`reverse_proxy` directive](/docs/caddyfile/directives/reverse_proxy) to pass requests through. Front instance: ```caddy foo.example.com, bar.example.com { reverse_proxy 10.0.0.1:80 } ``` Back instance: ```caddy { servers { trusted_proxies static private_ranges } } http://foo.example.com { reverse_proxy foo-app:8080 } http://bar.example.com { reverse_proxy bar-app:9000 } ``` - This example serves two different domains, proxying both to the same back Caddy instance, on port `80`. Your back instance is serving the two domains different ways, so it's configured with two separate site blocks. - On the back, [`http://`](/docs/caddyfile/concepts#addresses) is used to accept HTTP on port `80`. The front instance terminates TLS, and the traffic between front and back are on a private network, so there's no need to re-encrypt it. - You may use a different port like `8080` on the back instance if you need to; just append `:8080` to each site address on the back's config, OR set the [`http_port` global option](/docs/caddyfile/options#http_port) to `8080`. - On the back, the [`trusted_proxies` global option](/docs/caddyfile/options#trusted_proxies) is used to tell Caddy to trust the front instance as a proxy. This ensures the real client IP is preserved. - Going further, you could have more than one back instance that you [load balance](/docs/caddyfile/directives/reverse_proxy#load-balancing) between. You could set up mTLS (mutual TLS) using the [`acme_server`](/docs/caddyfile/directives/acme_server) on the front instance such that it acts like the CA for the back instance (useful if the traffic between front and back cross untrusted networks). --- --- title: Response matchers (Caddyfile) --- # Response Matchers **Response matchers** can be used to filter (or classify) responses by specific criteria. These typically only appear as config inside of certain other directives, to make decisions on the response as it's being written out to the client. - [Syntax](#syntax) - [Matchers](#matchers) - [status](#status) - [header](#header) ## Syntax If a directive accepts response matchers, the usage is represented as either `[]` or `[]` in the syntax documentation. - The **** token can be the name of a previously declared named response matcher. For example: `@name`. - The **** token can be the response criteria itself, without requiring prior declaration. For example: `status 200`. ### Named ```caddy-d @name { status header [] } ``` If only one aspect of the response is relevant to the directive, you can put the name and the criteria on the same line: ```caddy-d @name status ``` ### Inline ```caddy-d ... { status header [] } ``` ```caddy-d ... status ``` ```caddy-d ... header [] ``` ## Matchers ### status ```caddy-d status ``` By HTTP status code. - **<code...>** is a list of HTTP status codes. Special cases are strings like `2xx` and `3xx`, which match against all status codes in the range of `200`-`299` and `300`-`399`, respectively. #### Example: ```caddy-d @success status 2xx ``` ### header ```caddy-d header [] ``` By response header fields. - `` is the name of the HTTP header field to check. - If prefixed with `!`, the field must not exist to match (omit value arg). - `` is the value the field must have to match. - If prefixed with `*`, it performs a fast suffix match (appears at the end). - If suffixed with `*`, it performs a fast prefix match (appears at the start). - If enclosed by `*`, it performs a fast substring match (appears anywhere). - Otherwise, it is a fast exact match. Different header fields within the same set are AND-ed. Multiple values per field are OR'ed. Note that header fields may be repeated and have different values. Backend applications MUST consider that header field values are arrays, not singular values, and Caddy does not interpret meaning in such quandaries. #### Example: Match responses with the `Foo` header containing the value `bar`: ```caddy-d @upgrade header Foo *bar* ``` Match responses with the `Foo` header having the value `bar` OR `baz`: ```caddy-d @foo { header Foo bar header Foo baz } ``` Match responses that do not have the `Foo` header field at all: ```caddy-d @not_foo header !Foo ``` --- --- title: Caddyfile Tutorial --- # Caddyfile Tutorial This tutorial will teach you the basics of the [HTTP Caddyfile](/docs/caddyfile) so that you can quickly and easily produce good-looking, functional site configs. **Objectives:** - 🔲 First site - 🔲 Static file server - 🔲 Templates - 🔲 Compression - 🔲 Multiple sites - 🔲 Matchers - 🔲 Environment variables - 🔲 Comments **Prerequisites:** - Basic terminal / command line skills - Basic text editor skills - `caddy` in your PATH --- Create a new text file named `Caddyfile` (no extension). The first thing you should type is your site's [address](/docs/caddyfile/concepts#addresses): ```caddy localhost ``` Then hit enter and type what you want it to do. For this tutorial, make your Caddyfile look like this: ```caddy localhost respond "Hello, world!" ``` Save that and run Caddy (since this is a training tutorial, we'll use the `--watch` flag so changes to our Caddyfile are applied automatically):
caddy run --watch
The first time, you'll be asked for your password. This is so Caddy can serve your site over HTTPS. Open [localhost](https://localhost) in your browser and see your web server working, complete with HTTPS! That's not particularly exciting, so let's change our static response to a [file server](/docs/caddyfile/directives/file_server) with directory listings enabled: ```caddy localhost file_server browse ``` Save your Caddyfile, then refresh your browser tab. You should either see a list of files or an HTML page if there is an index file in the current directory. ## Adding functionality Let's do something interesting with our file server: serve a templated page. Create a new file and paste this into it: ```html Caddy tutorial Page loaded at: {{`{{`}}now | date "Mon Jan 2 15:04:05 MST 2006"{{`}}`}} ``` Save this as `caddy.html` in the current directory and load it in your browser: [https://localhost/caddy.html](https://localhost/caddy.html) The output is: ``` Page loaded at: {{`{{`}}now | date "Mon Jan 2 15:04:05 MST 2006"{{`}}`}} ``` Wait a minute. We should see today's date. Why didn't it work? It's because the server hasn't yet been configured to evaluate templates! Easy to fix, just add a line to the Caddyfile so it looks like this: ```caddy localhost templates file_server browse ``` Save that, then reload the browser tab. You should see: ``` Page loaded at: {{now | date "Mon Jan 2 15:04:05 MST 2006"}} ``` With Caddy's [templates module](/docs/modules/http.handlers.templates), you can do a lot of useful things with static files, such as including other HTML files, making sub-requests, setting response headers, working with data structures, and more! It's good practice to compress responses with a quick and modern compression algorithm. Let's enable Gzip and Zstandard support using the [`encode`](/docs/caddyfile/directives/encode) directive: ```caddy localhost encode templates file_server browse ``` That's the basic process for getting a semi-advanced, production-ready site up and running! When you're ready to turn on [automatic HTTPS](/docs/automatic-https), just replace your site's address (`localhost` in our tutorial) with your domain name. See our [HTTPS quick-start guide](/docs/quick-starts/https) for more information. ## Multiple sites With our current Caddyfile, we can only have the one site definition! Only the first line can be the address(es) of the site, and then all the rest of the file has to be directives for that site. But it is easy to make it so we can add more sites! Our Caddyfile so far: ```caddy localhost encode templates file_server browse ``` is equivalent to this one: ```caddy localhost { encode templates file_server browse } ``` except the second one allows us to add more sites. By wrapping our site block in curly braces `{ }` we are able to define multiple, different sites in the same Caddyfile. For example: ```caddy :8080 { respond "I am 8080" } :8081 { respond "I am 8081" } ``` When wrapping site blocks in curly braces, only [addresses](/docs/caddyfile/concepts#addresses) appear outside the curly braces and only [directives](/docs/caddyfile/directives) appear inside them. For multiple sites which share the same configuration, you can add more addresses, for example: ```caddy :8080, :8081 { ... } ``` You can then define as many different sites as you want, as long as each address is unique. ## Matchers We may want to apply some directives only to certain requests. For example, let's suppose we want to have both a file server and a reverse proxy, but we obviously can't do both on every request! Either the file server will write a response with a static file, or the reverse proxy will pass the request to a backend and write back its response. This config will not work like we want (`reverse_proxy` will take precedence due to the [directive order](/docs/caddyfile/directives#directive-order)): ```caddy localhost file_server reverse_proxy 127.0.0.1:9005 ``` In practice, we may want to use the reverse proxy only for API requests, i.e. requests with a base path of `/api/`. This is easy to do by adding a [matcher token](/docs/caddyfile/matchers#syntax): ```caddy localhost reverse_proxy /api/* 127.0.0.1:9005 file_server ``` There; now the reverse proxy will be prioritized for all requests starting with `/api/`. The `/api/*` part we just added is called a **matcher token**. You can tell it's a matcher token because it starts with a forward slash `/` and it appears right after the directive (but you can always look it up in the [directive's docs](/docs/caddyfile/directives) to be sure). Matchers are really powerful. You can declare named matchers and use them like `@name` to match on more than just the request path! Take a moment to [learn more about matchers](/docs/caddyfile/matchers) before continuing! ## Environment variables The Caddyfile adapter allows substituting [environment variables](/docs/caddyfile/concepts#environment-variables) before the Caddyfile is parsed. First, set an environment variable (in the same shell that runs Caddy):
export SITE_ADDRESS=localhost:9055
Then you can use it like this in the Caddyfile: ```caddy {$SITE_ADDRESS} file_server ``` Before the Caddyfile is parsed, it will be expanded to: ```caddy localhost:9055 file_server ``` You can use environment variables anywhere in the Caddyfile, for any number of tokens. ## Comments One last thing that you will find most helpful: if you want to remark or note anything in your Caddyfile, you can use comments, starting with `#`: ```caddy # this starts a comment ``` ## Further reading - [Caddyfile concepts](/docs/caddyfile/concepts) - [Directives](/docs/caddyfile/directives) - [Common patterns](/docs/caddyfile/patterns) --- --- title: The Caddyfile --- # The Caddyfile The **Caddyfile** is a convenient Caddy configuration format for humans. It is most people's favorite way to use Caddy because it is easy to write, easy to understand, and expressive enough for most use cases. It looks like this: ```caddy example.com { root * /var/www/wordpress encode php_fastcgi unix//run/php/php-version-fpm.sock file_server } ``` (That's a real, production-ready Caddyfile that serves WordPress with fully-managed HTTPS.) The basic idea is that you first type the address of your site, then the features or functionality you need your site to have. [View more common patterns.](/docs/caddyfile/patterns) ## Menu - #### [Quick start guide](/docs/quick-starts/caddyfile) A good place to begin getting familiar with the Caddyfile. - #### [Full Caddyfile tutorial](/docs/caddyfile-tutorial) Learn to do a variety of common things with the Caddyfile. - #### [Caddyfile concepts](/docs/caddyfile/concepts) Required reading! Structure, site addresses, matchers, placeholders, and more. - #### [Directives](/docs/caddyfile/directives) Keywords at the beginning of lines that enable features for your sites. - #### [Request matchers](/docs/caddyfile/matchers) Filter requests by using matchers with your directives. - #### [Global options](/docs/caddyfile/options) Settings that apply to the whole server rather than individual sites. - #### [Common patterns](/docs/caddyfile/patterns) Simple ways to do common things. ## Note The Caddyfile is just a [config adapter](/docs/config-adapters) for Caddy. It is usually preferred when manually crafting configurations by hand, but is not as expressive, flexible, or programmable as Caddy's [native JSON structure](/docs/json/). If you are automating your Caddy configurations/deployments, you may wish to use JSON with [Caddy's API](/docs/api). (You can actually use the Caddyfile with the API too, just to a limited extent.) --- --- title: "Command Line" --- # Command Line Caddy has a standard unix-like command line interface. Basic usage is: ``` caddy [] ``` The `` indicate parameters that get replaced by your input. The`[brackets]` indicate optional parameters. The `(brackets)` indicate required parameters. The ellipses `...` indicates a continuation, i.e. one or more parameters. The `--flags` may have a single-letter shortcut like `-f`. **Quick start: `caddy`, `caddy help`, or `man caddy` (if installed)** --- - **[caddy adapt](#caddy-adapt)** Adapts a config document to native JSON - **[caddy build-info](#caddy-build-info)** Prints build information - **[caddy completion](#caddy-completion)** Generate shell completion script - **[caddy environ](#caddy-environ)** Prints the environment - **[caddy file-server](#caddy-file-server)** A simple but production-ready file server - **[caddy file-server export-template](#caddy-file-server-export-template)** Auxiliary command for the file server to export the default file browser template - **[caddy fmt](#caddy-fmt)** Formats a Caddyfile - **[caddy hash-password](#caddy-hash-password)** Hashes a password and outputs base64 - **[caddy help](#caddy-help)** View help for caddy commands - **[caddy list-modules](#caddy-list-modules)** Lists the installed Caddy modules - **[caddy manpage](#caddy-manpage)** Generate manpages - **[caddy reload](#caddy-reload)** Changes the config of the running Caddy process - **[caddy respond](#caddy-respond)** A quick-and-clean, hard-coded HTTP server for development and testing - **[caddy reverse-proxy](#caddy-reverse-proxy)** A simple but production-ready HTTP(S) reverse proxy - **[caddy run](#caddy-run)** Starts the Caddy process in the foreground - **[caddy start](#caddy-start)** Starts the Caddy process in the background - **[caddy stop](#caddy-stop)** Stops the running Caddy process - **[caddy storage export](#caddy-storage)** Exports the contents of the configured storage to a tarball - **[caddy storage import](#caddy-storage)** Imports a previously exported tarball to the configured storage - **[caddy trust](#caddy-trust)** Installs a certificate into local trust store(s) - **[caddy untrust](#caddy-untrust)** Untrusts a certificate from local trust store(s) - **[caddy upgrade](#caddy-upgrade)** Upgrades Caddy to the latest release - **[caddy add-package](#caddy-add-package)** Upgrades Caddy to the latest release, with additional plugins added - **[caddy remove-package](#caddy-remove-package)** Upgrades Caddy to the latest release, with some plugins removed - **[caddy validate](#caddy-validate)** Tests whether a config file is valid - **[caddy version](#caddy-version)** Prints the version - **[Signals](#signals)** How Caddy handles signals - **[Exit codes](#exit-codes)** Emitted when the Caddy process exits ## Subcommands ### `caddy adapt`
caddy adapt
	[-c, --config <path>]
	[-a, --adapter <name>]
	[-p, --pretty]
	[--validate]
Adapts a configuration to Caddy's native JSON config structure and writes the output to stdout, along with any warnings to stderr, then exits. `--config` is the path to the config file. If omitted, assumes `Caddyfile` in current directory if it exists; otherwise, this flag is required. `--adapter` specifies the config adapter to use; default is `caddyfile`. `--pretty` will format the output with indentation for human readability. `--validate` will load and provision the adapted configuration to check for validity (but it will not actually start running the config). Note that a config which is successfully adapted may still fail validation. For an example of this, use this Caddyfile: ```caddy localhost tls cert_notexist.pem key_notexist.pem ``` Try adapting it:
caddy adapt --config Caddyfile
It succeeds without error. Then try:
caddy adapt --config Caddyfile --validate
adapt: validation: loading app modules: module name 'tls': provision tls: loading certificates: open cert_notexist.pem: no such file or directory
Even though that Caddyfile can be adapted to JSON without errors, the actual certificate and/or key files do not exist, so validation fails because that error arises during the provisioning phase. Thus, validation is a stronger error check than adaptation is. #### Example To adapt a Caddyfile to JSON that you can easily read and tweak manually:
caddy adapt --config /path/to/Caddyfile --pretty
### `caddy build-info`
caddy build-info
Prints information provided by Go about the build (main module path, package versions, module replacements). ### `caddy completion`
caddy completion [bash|zsh|fish|powershell]
Generates shell completion scripts. This allows you to get tab-complete or auto-complete (or similar, depending on your shell) when typing `caddy` commands. To get instructions for installing this script into your specific shell, run `caddy help completion` or `caddy completion -h`. ### `caddy environ`
caddy environ
Prints the environment as seen by caddy, then exits. Can be useful when debugging init systems or process manager units like systemd. ### `caddy file-server`
caddy file-server
	[-r, --root <path>]
	[--listen <addr>]
	[-d, --domain <example.com>]
	[-b, --browse]
	[--reveal-symlinks]
	[-t, --templates]
	[--access-log]
	[-v, --debug]
	[--no-compress]
	[-p, --precompressed]
Spins up a simple but production-ready static file server. `--root` specifies the root file path. Default is the current working directory. `--listen` accepts a listener address. Default is `:80`, unless `--domain` is used, then `:443` will be the default. `--domain` will only serve files through that hostname, and Caddy will attempt to serve it over HTTPS, so make sure any public DNS is configured properly first if it's a public domain name. The default port will be changed to 443. `--browse` will enable directory listings if a directory without an index file is requested. `--reveal-symlinks` will show the target of symbolic links in directory listings, when `--browse` is enabled. `--templates` will enable template rendering. `--access-log` enables the request/access log. `--debug` enables verbose logging. `--no-compress` disables compression. By default, Zstandard and Gzip compression are enabled. `--precompressed` specifies encoding formats to search for precompressed sidecar files. Can be repeated for multiple formats. See the [file_server directive](/docs/caddyfile/directives/file_server#precompressed) for more information. This command disables the admin API, making it easier to run multiple instances on a local development machine. #### `caddy file-server export-template`
caddy file-server export-template
Exports the default file browsing template to stdout ### `caddy fmt`
caddy fmt [<path>]
	[-w, --overwrite]
	[-d, --diff]
Formats or prettifies a Caddyfile, then exits. The result is printed to stdout unless `--overwrite` is used, and will exit with code `1` if there are any differences. `` specifies the path to the Caddyfile. If `-`, the input is read from stdin. If omitted, a file named Caddyfile in the current directory is assumed instead. `--overwrite` causes the result to be written to the input file instead of being printed to the terminal. If the input is not a regular file, this flag has no effect. `--diff` causes the output to be compared against the input, and lines will be prefixed with `-` and `+` where they differ. Note that unchanges lines are prefixed with two spaces for alignment, and that this is not a valid patch format; it's just meant as a visual tool. ### `caddy hash-password`
caddy hash-password
	[-p, --plaintext <password>]
	[-a, --algorithm <name>]
Convenient way to hash a plaintext password. The resulting hash is written to stdout as a format usable directly in your Caddy config. `--plaintext` is the plaintext form of the password. If omitted, interactive mode will be assumed and the user will be shown a prompt to enter the password manually. `--algorithm` may be `bcrypt` or any installed hash algorithm. Default is `bcrypt`. ### `caddy help`
caddy help [<command>]
Prints CLI help text, optionally for a specific subcommand, then exits. ### `caddy list-modules`
caddy list-modules
	[--packages]
	[--versions]
	[-s, --skip-standard]
Prints the Caddy modules that are installed, optionally with package and/or version information from their associated Go modules, then exits. In some scripted situations, it may be redundant to print all of the standard modules as well, so you may use `--skip-standard` to omit those from the output. NOTE: Due to [a bug in Go](https://github.com/golang/go/issues/29228), version information is only available if Caddy is built as a dependency and not as the main module. Use [xcaddy](/docs/build#xcaddy) to make this easier. ### `caddy manpage`
caddy manpage
	(-o, --directory <path>)
Generates manual/documentation pages for Caddy commands and writes them to the directory at the specified path. The output of this command can be read by the `man` command. `--directory` (required) is the path to the directory into which to write the man pages. It will be created if it does not exist. Once generated, the manual pages generally need to be installed. This procedure varies by platform, but on typical Linux systems, it's something like this:
$ caddy manpage --directory man
$ gzip -r man/
$ sudo cp man/* /usr/share/man/man8/
$ sudo mandb
Then you can run `man caddy` (or `man caddy-*` for subcommands) to read documentation in your terminal. Manual pages are separate documentation from what is on our website. Our website has more comprehensive documentation that is updated often. ### `caddy reload`
caddy reload
	[-c, --config <path>]
	[-a, --adapter <name>]
	[--address <interface>]
	[-f, --force]
Gives the running Caddy instance a new configuration. This has the same effect as POSTing a document to the [/load endpoint](/docs/api#post-load), but this command is convenient for simple workflows revolving around config files. Compared to the `stop`, `start`, and `run` commands, this single command is the correct, semantic way to change/reload the running configuration. Because this command uses the API, the admin endpoint must not be disabled. `--config` is the config file to apply. If `-`, the config is read from stdin. If not specified, it will try a file called `Caddyfile` in the current working directory and, if it exists, it will adapt it using the `caddyfile` config adapter; otherwise, it is an error if there is no config file to load. `--adapter` specifies a config adapter to use, if any. This flag is not necessary if the `--config` filename starts with `Caddyfile` or ends with `.caddyfile` which assumes the `caddyfile` adapter. Otherwise, this flag is required if the provided config file is not in Caddy's native JSON format. `--address` needs to be used if the admin endpoint is not listening on the default address and if it is different from the address in the provided config file. `--force` will cause a reload to happen even if the specified config is the same as what Caddy is already running. Can be useful to force Caddy to reprovision its modules, which can have side-effects, for example: reloading manually-loaded TLS certificates. ### `caddy respond`
caddy respond
	[-s, --status <code>]
	[-H, --header "<Field>: <value>"]
	[-b, --body <content>]
	[-l, --listen <addr>]
	[-v, --debug]
	[--access-log]
	[<status|body>]
Starts one or more simple, hard-coded HTTP servers that are useful for development, staging, and some production use cases. It can be useful for verifying or debugging HTTP clients, scripts, or even load balancers. `--status` is the HTTP status code to return. `--header` adds an HTTP header; `Field: value` format is expected. This flag can be used multiple times. `--body` specifies the response body. Alternatively, the body can be piped from stdin. `--listen` is the listener address, which can be any [network address](/docs/conventions#network-addresses) recognized by Caddy, and may include a port range to start multiple servers. `--debug` enables verbose debug logging. `--access-log` enables access/request logging. With no options specified, this command listens on a random available port and answers HTTP requests with an empty 200 response. The listen address can be customized with the `--listen` flag and will always be printed to stdout. If the listen address includes a port range, multiple servers will be started. If a final, unnamed argument is given, it will be treated as a status code (same as the `--status` flag) if it is a 3-digit number. Otherwise, it is used as the response body (same as the `--body` flag). The `--status` and `--body` flags will always override this argument. A body may be given in 3 ways: a flag, a final (and unnamed) argument to the command, or piped to stdin (if flag and argument are unset). Limited [template evaluation](https://pkg.go.dev/text/template) is supported on the body, with the following variables: Variable | Description ---------|------------- `.N` | Server number `.Port` | Listener port `.Address` | Listener address #### Examples Empty 200 response on a random port:
caddy respond
HTTP response with a body:
caddy respond "Hello, world!"
Multiple servers and templates:
$ caddy respond --listen :2000-2004 "{{printf "I'm server {{.N}} on port {{.Port}}"}}"

Server address: [::]:2000
Server address: [::]:2001
Server address: [::]:2002
Server address: [::]:2003
Server address: [::]:2004

$ curl 127.0.0.1:2002
I'm server 2 on port 2002
Pipe in a maintenance page:
cat maintenance.html | caddy respond \
	--listen :80 \
	--status 503 \
	--header "Content-Type: text/html"
### `caddy reverse-proxy`
caddy reverse-proxy
	[-f, --from <addr>]
	(-t, --to <addr>)
	[-H, --header-up "<Field>: <value>"]
	[-d, --header-down "<Field>: <value>"]
	[-c, --change-host-header]
	[-r, --disable-redirects]
	[-i, --internal-certs]
	[-v, --debug]
	[--access-log]
	[--insecure]
A simple but production-ready reverse proxy. Useful for quick deployments, demos, and development. Simply shuttles HTTP(S) traffic from the `--from` address to the `--to` address. Multiple `--to` addresses may be specified by repeating the flag. At least one `--to` address is required. The `--to` address may have a port range as a shortcut to expand to multiple upstreams. Unless otherwise specified in the addresses, the `--from` address will be assumed to be HTTPS if a hostname is given, and the `--to` address will be assumed to be HTTP. If the `--from` address has a host or IP, Caddy will attempt to serve the proxy over HTTPS with a certificate (unless overridden by the HTTP scheme or port). If serving HTTPS: - `--disable-redirects` can be used to avoid binding to the HTTP port. - `--internal-certs` can be used to force issuance certs using the internal CA instead of attempting to issue a public certificate. For proxying: - `--header-up` can be used to set a request header to send to the upstream. - `--header-down` can be used to set a response header to send back to the client. - `--change-host-header` sets the Host header on the request to the address of the upstream, instead of defaulting to the incoming Host header. This is a shortcut for `--header-up "Host: {http.reverse_proxy.upstream.hostport}"` - `--insecure` disables TLS verification with the upstream. WARNING: THIS DISABLES SECURITY BY NOT VERIFYING THE UPSTREAM'S CERTIFICATE. - `--debug` enables verbose logging. This command disables the admin API so it is easier to run multiple instances on a local development machine. ### `caddy run`
caddy run
	[-c, --config <path>]
	[-a, --adapter <name>]
	[--pidfile <file>]
	[-e, --environ]
	[--envfile <file>]
	[-r, --resume]
	[-w, --watch]
Runs Caddy and blocks indefinitely; i.e. "daemon" mode. `--config` specifies an initial config file to immediately load and use. If `-`, the config is read from stdin. If no config is specified, Caddy will run with a blank configuration and use default settings for the [admin API endpoints](/docs/api), which can be used to feed it new configuration. As a special case, if the current working directory has a file called "Caddyfile" and the `caddyfile` config adapter is plugged in (default), then that file will be loaded and used to configure Caddy, even without any command line flags. `--adapter` is the name of the config adapter to use when loading the initial config, if any. This flag is not necessary if the `--config` filename starts with `Caddyfile` or ends with `.caddyfile` which assumes the `caddyfile` adapter. Otherwise, this flag is required if the provided config file is not in Caddy's native JSON format. Any warnings will be printed to the log, but beware that any adaptation without errors will immediately be used, even if there are warnings. If you want to review the results of the adaptation first, use the [`caddy adapt`](#caddy-adapt) subcommand. `--pidfile` writes the PID to the specified file. `--environ` prints out the environment before starting. This is the same as the `caddy environ` command, but does not exit after printing. `--envfile` loads environment variables from the specified file, in `KEY=VALUE` format. Comments starting with `#` are supported; keys may be prefixed with `export`; values may be double-quoted (double-quotes within can be escaped); multi-line values are supported. `--resume` uses the last loaded configuration that was autosaved, overriding the `--config` flag (if present). Using this flag guarantees config durability through machine reboots or process restarts. It is most useful in [API](/docs/api)-centric deployments. `--watch` will watch the config file and automatically reload it after it changes. ⚠️ This feature is intended for use only in local development environments! ### `caddy start`
caddy start
	[-c, --config <path>]
	[-a, --adapter <name>]
	[--envfile <file>]
	[--pidfile <file>]
	[-w, --watch]
Same as [`caddy run`](#caddy-run), but in the background. This command only blocks until the background process is running successfully (or fails to run), then returns. Note: the flag `--config` does _not_ support `-` to read the config from stdin. Use of this command is discouraged with system services or on Windows. On Windows, the child process will remain attached to the terminal, so closing the window will forcefully stop Caddy, which is not obvious. Consider running Caddy [as a service](/docs/running) instead. Once started, you can use [`caddy stop`](#caddy-stop) or the [`POST /stop`](/docs/api#post-stop) API endpoint to exit the background process. ### `caddy stop`
caddy stop
	[--address <interface>]
	[-c, --config <path> [-a, --adapter <name>]]
Gracefully stops the running Caddy process (other than the process of the stop command) and causes it to exit. It uses the [`POST /stop`](/docs/api#post-stop) endpoint of the admin API to perform a graceful shutdown. The address of this request can be customized using the `--address` flag, or from the given `--config`, if the running instance's admin API is not using the default listen address. If you want to stop the current configuration but do not want to exit the process, use [`caddy reload`](#caddy-reload) with a blank config, or the [`DELETE /config/`](/docs/api#delete-configpath) endpoint. ### `caddy storage` ⚠️ Experimental Allows export and import of the contents of Caddy's configured data storage. This is useful when needing to transition from one [storage module](/docs/json/storage/) to another, by exporting from your old one, updating your config, then importing into the new one. The following command can be used to copy the storage between different modules in one shot, using old and new configs, piping the export command's output into the import command. ``` $ caddy storage export -c Caddyfile.old -o- | caddy storage import -c Caddyfile.new -i- ``` #### `caddy storage export`
caddy storage export
	-c, --config <path>
	[-o, --output <path>]
`--config` is the config file load. This is required, so that the correct storage module is connected to. `--output` is the filename to write the tarball. If `-`, the output is written to stdout. #### `caddy storage import`
caddy storage import
	-c, --config <path>
	-i, --input <path>
`--config` is the config file load. This is required, so that the correct storage module is connected to. `--input` is the filename of the tarball to read from. If `-`, the input is read from stdin. ### `caddy trust`
caddy trust
	[--ca <id>]
	[--address <interface>]
	[-c, --config <path> [-a, --adapter <name>]]
Installs a root certificate for a CA managed by Caddy's [PKI app](/docs/json/apps/pki/) into local trust stores. Caddy will attempt to install its root certificates into the local trust stores automatically when they are first generated, but it might fail if Caddy doesn't have the appropriate permissions to write to the trust store. This command is necessary to pre-install the certificates before using them, if the server process runs as an unprivileged user (such as via systemd). You may need to run this command with `sudo` to unix systems. By default, this command installs the root certificate for Caddy's default CA (i.e. "local"). You may specify the ID of another CA with the `--ca` flag. This command will attempt to connect to Caddy's [admin API](/docs/api) to fetch the root certificate, using the [`GET /pki/ca//certificates`](/docs/api#get-pkicaltidgtcertificates) endpoint. You may explicitly specify the `--address`, or use the `--config` flag to load the admin address from your config, if the running instance's admin API is not using the default listen address. You may also use the `caddy` binary with this command to install certificates on other machines in your network, if the admin API is made accessible to other machines -- be careful if doing this, to not expose the admin API to untrusted clients. ### `caddy untrust`
caddy untrust
	[-p, --cert <path>]
	[--ca <id>]
	[--address <interface>]
	[-c, --config <path> [-a, --adapter <name>]]
Untrusts a root certificate from the local trust store(s). This command uninstalls trust; it does not necessarily delete the root certificate from trust stores entirely. Thus, repeatedly trusting and untrusting new certificates can fill up trust databases. This command does not delete or modify certificate files from Caddy's configured storage. This command can be used in one of two ways: - By specifying a direct path to the root certificate to untrust with the `--cert` flag. - By fetching the root certificate from the [admin API](/docs/api) using the [`GET /pki/ca//certificates`](/docs/api#get-pkicaidcertificates) endpoint. This is the default behaviour if no flags are given. If the admin API is used, then the CA ID defaults to "local". You may specify the ID of another CA with the `--ca` flag. You may explicitly specify the `--address`, or use the `--config` flag to load the admin address from your config, if the running instance's admin API is not using the default listen address. ### `caddy upgrade` ⚠️ Experimental
caddy upgrade
	[-k, --keep-backup]
Replaces the current Caddy binary with the latest version from [our download page](/download) with the same modules installed, including all third-party plugins that are registered on the Caddy website. Upgrades do not interrupt running servers; currently, the command only replaces the binary on disk. This might change in the future if we can figure out a good way to do it. The upgrade process is fault tolerant; the current binary is backed up first (copied beside the current one) and automatically restored if anything goes wrong. If you wish to keep the backup after the upgrade process is complete, you may use the `--keep-backup` option. This command may require elevated privileges if your user does not have permission to write to the executable file. ### `caddy add-package` ⚠️ Experimental
caddy add-package <packages...>
	[-k, --keep-backup]
Similarly to `caddy upgrade`, replaces the current Caddy binary with the latest version with the same modules installed, _plus_ the packages listed as arguments included in the new binary. Find the list of packages you can install from [our download page](/download). Each argument should be the full package name. For example:
caddy add-package github.com/caddy-dns/cloudflare
### `caddy remove-package` ⚠️ Experimental
caddy remove-package <packages...>
	[-k, --keep-backup]
Similarly to `caddy upgrade`, replaces the current Caddy binary with the latest version with the same modules installed, but _without_ the packages listed as arguments, if they existed in the current binary. Run `caddy list-modules --packages` to see the list of package names of non-standard modules included in the current binary. ### `caddy validate`
caddy validate
	[-c, --config <path>]
	[-a, --adapter <name>]
	[--envfile <file>]
Validates a configuration file, then exits. This command deserializes the config, then loads and provisions all of its modules as if to start the config, but the config is not actually started. This exposes errors in a configuration that arise during loading or provisioning phases and is a stronger error check than merely serializing a config as JSON. `--config` is the config file to validate. If `-`, the config is read from stdin. Default is the `Caddyfile` in the current directory, if any. `--adapter` is the name of the config adapter to use. This flag is not necessary if the `--config` filename starts with `Caddyfile` or ends with `.caddyfile` which assumes the `caddyfile` adapter. Otherwise, this flag is required if the provided config file is not in Caddy's native JSON format. `--envfile` loads environment variables from the specified file, in `KEY=VALUE` format. Comments starting with `#` are supported; keys may be prefixed with `export`; values may be double-quoted (double-quotes within can be escaped); multi-line values are supported. ### `caddy version`
caddy version
Prints the version and exits. ## Signals Caddy traps certain signals and ignores others. Signals can initiate specific process behavior. Signal | Behavior -------|---------- `SIGINT` | Graceful exit. Send signal again to force exit immediately. `SIGQUIT` | Quits Caddy immediately, but still cleans up locks in storage because it is important. `SIGTERM` | Graceful exit. `SIGUSR1` | Ignored. For config updates, use the `caddy reload` command or [the API](/docs/api). `SIGUSR2` | Ignored. `SIGHUP` | Ignored. A graceful exit means that new connections are no longer accepted, and existing connections will be drained before the socket is closed. A grace period may apply (and is configurable). Once the grace period is up, connections will be forcefully terminated. Locks in storage and other resources that individual modules need to release are cleaned up during a graceful shutdown. ## Exit codes Caddy returns a code when the process exits: Code | Meaning -----|--------- `0` | Normal exit. `1` | Failed startup. **Do not automatically restart the process; it will likely error again unless changes are made.** `2` | Forced quit. Caddy was forced to exit without cleaning up resources. `3` | Failed quit. Caddy exited with some errors during cleanup. In bash, you can get the exit code of the last command with `echo $?`. --- --- title: Config Adapters --- # Config Adapters Caddy's native config language is [JSON](https://www.json.org/json-en.html), but writing JSON by hand can be tedious and error-prone. That's why Caddy supports being configured with other languages through **config adapters**. They are Caddy plugins which make it possible to use config in your preferred format by outputting [Caddy JSON](/docs/json/) for you. For example, a config adapter could [turn your NGINX config into Caddy JSON](https://github.com/caddyserver/nginx-adapter). ## Known config adapters The following config adapters are currently available (some are third-party projects): - [**caddyfile**](/docs/caddyfile) (standard) - [**nginx**](https://github.com/caddyserver/nginx-adapter) - [**jsonc**](https://github.com/caddyserver/jsonc-adapter) - [**json5**](https://github.com/caddyserver/json5-adapter) - [**yaml**](https://github.com/abiosoft/caddy-yaml) - [**cue**](https://github.com/caddyserver/cue-adapter) - [**toml**](https://github.com/awoodbeck/caddy-toml-adapter) - [**hcl**](https://github.com/francislavoie/caddy-hcl) - [**dhall**](https://github.com/mholt/dhall-adapter) - [**mysql**](https://github.com/zhangjiayin/caddy-mysql-adapter) ## Using config adapters You can use a config adapter by specifying it on the command line by using the `--adapter` flag on most subcommands that take a config:
caddy run --config caddy.yaml --adapter yaml
Or via the API at the [`/load` endpoint](/docs/api#post-load):
curl localhost:2019/load \
	-H "Content-Type: application/yaml" \
	--data-binary @caddy.yaml
If you only want to get the output JSON without running it, you can use the [`caddy adapt`](/docs/command-line#caddy-adapt) command:
caddy adapt --config caddy.yaml --adapter yaml
## Caveats Not all config languages are 100% compatible with Caddy; some features or behaviors simply don't translate well or are not yet programmed into the adapter or Caddy itself. Some adapters do a 1-1 translation, like YAML->JSON or TOML->JSON. Others are designed specifically for Caddy, like the Caddyfile. Generally, these adapters will always work. However, not all adapters work all of the time. Config adapters do their best to translate your input to Caddy JSON with the highest fidelity and correctness. Because this conversion process is not guaranteed to be complete and correct all the time, we don't call them "converters" or "translators". They are "adapters" since they will at least give you a good starting point to finish crafting your final JSON config. Config adapters can output the resulting JSON, warnings, and errors. JSON results if no errors occur. Errors occur when something is wrong with the input (for example, syntax errors). Warnings are emitted when something is wrong with the adaptation but which is not necessarily fatal (for example, feature not supported). Caution is advised if using configs that were adapted with warnings. --- --- title: Conventions --- # Conventions The Caddy ecosystem adheres to a few conventions to make things consistent and intuitive across the platform. - [Network addresses](#network-addresses) - [Placeholders](#placeholders) - [File locations](#file-locations) - [Data directory](#data-directory) - [Configuration directory](#configuration-directory) - [Durations](#durations) ## Network addresses When specifying a network address to dial or bind, Caddy accepts a string in the following format: ``` network/address ``` The network part is optional (defaulting to `tcp`), and is anything that [Go's `net.Dial` function](https://pkg.go.dev/net#Dial) recognizes. If a network is specified, a single forward slash `/` must separate the network and address portions. The network can be any of the following; ones suffixed with `4` or `6` are IPv4 or IPv6 only, respectively: - TCP: `tcp`, `tcp4`, `tcp6` - UDP: `udp`, `udp4`, `udp6` - IP: `ip`, `ip4`, `ip6` - Unix: `unix`, `unixgram`, `unixpacket` The address part may be any of these forms: - `host` - `host:port` - `:port` - `[ipv6%zone]:port` - `/path/to/unix/socket` - `/path/to/unix/socket|0200` The host may be any hostname, resolvable domain name, or IP address. In the case of IPv6 addresses, the address must be enclosed in square brackets `[]`. The zone identifier (starting with `%`) is optional (often used for link-local addresses). The port may be a single value (`:8080`) or an inclusive range (`:8080-8085`). A port range will be multiplied into singular addresses. Not all config fields accept port ranges. The special port `:0` means any available port. A unix socket path is only acceptable when using a `unix*` network type. The forward slash that separates the network and address is not considered part of the path. When a unix socket is used as a bind address, you may optionally specify a file permission mode after the path, separated by a pipe `|`. The default is `0200` (octal), i.e. `u=w,g=,o=` (symbolic). The leading `0` is optional. Valid examples: ``` :8080 127.0.0.1:8080 localhost:8080 localhost:8080-8085 tcp/localhost:8080 tcp/localhost:8080-8085 udp/localhost:9005 [::1]:8080 tcp6/[fe80::1%eth0]:8080 unix//path/to/socket unix//path/to/socket|0200 ``` ## Placeholders Caddy's configuration supports the use of _placeholders_. Using placeholders is a simple way to inject dynamic values into a static configuration. 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. The following placeholders are always available (global): Placeholder | Description ------------|------------- `{env.*}` | Environment variable; example: `{env.HOME}` `{file.*}` | Contents from a file; example: `{file./path/to/secret.txt}` `{system.hostname}` | The system's local hostname `{system.slash}` | The system's filepath separator `{system.os}` | The system's OS `{system.arch}` | The system's architecture `{system.wd}` | The current working directory `{time.now}` | The current time as a Go Time struct `{time.now.http}` | The current time in the format used in [HTTP headers ](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified) `{time.now.unix}` | The current time as a unix timestamp in seconds `{time.now.unix_ms}` | The current time as a unix timestamp in milliseconds `{time.now.common_log}` | The current time in Common Log Format `{time.now.year}` | The current year in YYYY format 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. ## File locations This section contains information about where to find various files. File and directory paths described here are defaults at best; some can be overridden. ### Your config files There is no single, conventional place for you to put your config files. Put them wherever makes the most sense to you. Distributions that ship with a default config file should document where this config file is at, even if it might be obvious to the package/distro maintainers. For most Linux installations, the Caddyfile will be found at `/etc/caddy/Caddyfile`. ### Data directory Caddy stores TLS certificates and other important assets in a data directory, which is backed by [the configured storage module](/docs/json/storage/) (default: local file system). If the `XDG_DATA_HOME` environment variable is set, it is `$XDG_DATA_HOME/caddy`. Otherwise, its path varies by platform, adhering to OS conventions: OS | Data directory path ---|--------------------- **Linux, BSD** | `$HOME/.local/share/caddy` **Windows** | `%AppData%\Caddy` **macOS** | `$HOME/Library/Application Support/Caddy` **Plan 9** | `$HOME/lib/caddy` **Android** | `$HOME/caddy` (or `/sdcard/caddy`) All other OSes use the Linux/BSD directory path. **The data directory must not be treated as a cache.** Its contents are **not** ephemeral or merely for the sake of performance. Caddy stores TLS certificates, private keys, OCSP staples, and other necessary information to the data directory. It should not be purged without understanding the implications. It is crucial that this directory is persistent and writeable by Caddy. ### Configuration directory This is where Caddy may store certain configuration to disk. Most notably, it persists the last active configuration (by default) to this folder for easy resumption later using [`caddy run --resume`](/docs/command-line#caddy-run). If the `XDG_CONFIG_HOME` environment variable is set, it is `$XDG_CONFIG_HOME/caddy`. Otherwise, its path varies by platform, adhering to OS conventions: OS | Config directory path ---|--------------------- **Linux, BSD** | `$HOME/.config/caddy` **Windows** | `%AppData%\Caddy` **macOS** | `$HOME/Library/Application Support/Caddy` **Plan 9** | `$HOME/lib/caddy` All other OSes use the Linux/BSD directory path. It is crucial that this directory is persistent and writeable by Caddy. ## Durations Duration strings are commonly used throughout Caddy's configuration. They take on the same format as [Go's `time.ParseDuration` syntax](https://golang.org/pkg/time/#ParseDuration) except you can also use `d` for day (we assume 1 day = 24 hours for simplicity). Valid units are: - `ns` (nanosecond) - `us`/`µs` (microsecond) - `ms` (millisecond) - `s` (second) - `m` (minute) - `h` (hour) - `d` (day) Examples: - `250ms` - `5s` - `1.5h` - `2h45m` - `90d` In the [JSON config](/docs/json/), duration values can also be integers which represent nanoseconds. --- --- title: "Caddyfile Support" --- # Caddyfile Support Caddy modules are automatically added to the [native JSON config](/docs/json/) by virtue of their namespace when they are [registered](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#RegisterModule), making them both usable and documented. This makes Caddyfile support purely optional, but it is often requested by users who prefer the Caddyfile. ## Unmarshaler To add Caddyfile support for your module, simply implement the [`caddyfile.Unmarshaler`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/caddyfile?tab=doc#Unmarshaler) interface. You get to choose the Caddyfile syntax your module has by how you parse the tokens. An unmarshaler's job is simply to set up your module's type, e.g. by populating its fields, using the [`caddyfile.Dispenser`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/caddyfile?tab=doc#Dispenser) passed to it. For example, a module type named `Gizmo` might have this method: ```go // UnmarshalCaddyfile implements caddyfile.Unmarshaler. Syntax: // // gizmo [