diff --git a/Home.md b/Home.md index b0e2718..e82298f 100644 --- a/Home.md +++ b/Home.md @@ -1,31 +1,2066 @@ -Caddy Developer Wiki -==================== +This page describes how to use Caddy 2, which is a work-in-progress on the [v2 branch](https://github.com/caddyserver/caddy/tree/v2). See its [README](https://github.com/caddyserver/caddy/tree/v2#readme) to know how to download and build Caddy 2 for now. -[Caddy](https://caddyserver.com) is a general-purpose web server that's easy to use. +Features which are available in Caddy Enterprise are indicated with   ๐Ÿข _Enterprise_. -This wiki is for developers. If you are trying to learn how to use Caddy, please see the [documentation](https://caddyserver.com/docs) on the Caddy website. +# Contents -## Menu +- [Command-line interface](#command-line-interface) + - [help](#help) + - [start](#start) + - [run](#run) + - [reload](#reload) + - [stop](#stop) + - [version](#version) + - [list-modules](#list-modules) + - [environ](#environ) + - [adapt](#adapt) + - [validate](#validate) +- [Admin endpoint](#admin-endpoint) + - [POST /load](#post-load) + - [POST /stop](#post-stop) + - [GET /config/[scope]](#get-configscope) + - [POST /config/[scope]](#post-configscope) + - [PUT /config/[scope]](#put-configscope) + - [PATCH /config/[scope]](#patch-configscope) + - [DELETE /config/[scope]](#delete-configscope) + - [Using IDs in JSON](#using-ids-in-json) +- [Caddy modules](#caddy-modules) +- [Config intro](#config-intro) + - [Making one from scratch](#making-one-from-scratch) + - [Duration values](#duration-values) + - [Placeholders (variables)](#placeholders-variables) + - [Config adapters](#config-adapters) + - [Caddyfile adapter](#caddyfile-adapter) + - [Matcher tokens](#matcher-tokens) + - [Shorthand placeholders](#shorthand-placeholders) + - [Global config](#global-config) + - [Caddyfile directives](#caddyfile-directives) + - [root](#root) + - [matcher](#matcher) + - [file_server](#file_server) + - [encode](#encode) + - [headers](#headers) + - [request_header](#request_header) + - [php_fastcgi](#php_fastcgi) + - [reverse_proxy](#reverse_proxy) + - [redir](#redir) + - [rewrite](#rewrite) + - [templates](#templates) + - [respond](#respond) + - [tls](#tls-directive) + - [try_files](#try_files) +- [Config structure](#config-structure) + - [admin](#admin) + - [storage](#storage) + - [caddy.storage.file_system](#caddystoragefile_system) + - [apps](#apps) + - [http](#http) + - [http/servers](#httpservers) + - [http/servers/listen](#httpserverslisten) + - [http/servers/routes](#httpserversroutes) + - [HTTP Placeholders](#http-placeholders) + - [http/servers/routes/match](#httpserversroutesmatch) + - [http.matchers.host](#httpmatchershost) + - [http.matchers.path](#httpmatcherspath) + - [http.matchers.path_regexp](#httpmatcherspath_regexp) + - [http.matchers.method](#httpmatchersmethod) + - [http.matchers.query](#httpmatchersquery) + - [http.matchers.header](#httpmatchersheader) + - [http.matchers.header_regexp](#httpmatchersheader_regexp) + - [http.matchers.protocol](#httpmatchersprotocol) + - [http.matchers.not](#httpmatchersnot) + - [http.matchers.remote_ip](#httpmatchersremote_ip) + - [http.matchers.starlark_expr](#httpmatchersstarlark_expr) + - [http.matchers.file](#httpmatchersfile) + - [http/servers/handle](#httpservershandle) + - Middleware (non-terminating handlers): + - [http.handlers.headers](#httphandlersheaders) + - [http.handlers.rewrite](#httphandlersrewrite) + - [http.handlers.markdown](#httphandlersmarkdown) + - [http.handlers.request_body](#httphandlersrequest_body) + - [http.handlers.encode](#httphandlersencode) + - [http.handlers.templates](#httphandlerstemplates) + - Terminating handlers: + - [http.handlers.static_response](#httphandlersstatic_response) + - [http.handlers.file_server](#httphandlersfile_server) + - [http.handlers.reverse_proxy](#httphandlersreverse_proxy) + - [http.handlers.subroute](#httphandlerssubroute) + - [http.handlers.error](#httphandlerserror) + - [http.handlers.vars](#httphandlersvars) + - [http/servers/errors](#httpserverserrors) + - [http/servers/tls_connection_policies](#httpserverstls_connection_policies) + - [tls.certificate_selection.enterprise](#tlscertificate_selectionenterprise) + - [http/servers/automatic_https](#httpserversautomatic_https) + - [tls](#tls) + - [tls/certificates](#tlscertificates) + - [tls/automation](#tlsautomation) + - [tls/automation/policies](#tlsautomationpolicies) + - [tls.management.acme](#tlsmanagementacme) + - [tls/session_tickets](#tlssession_tickets) + - [tls.stek.standard](#tlsstekstandard) + - [tls.stek.distributed](#tlsstekdistributed) -### Version 2 -- [Caddy 2 Documentation](https://github.com/caddyserver/caddy/wiki/v2:-Documentation) -- [Example of building a config from scratch](https://github.com/caddyserver/caddy/wiki/v2:-Config-from-Scratch) -- [Writing a Caddy 2 Module](https://github.com/caddyserver/caddy/wiki/v2:-Writing-a-Module) -- [Caddyfile examples](https://github.com/caddyserver/caddy/wiki/v2:-Caddyfile-examples) +# Command-line interface -### Writing Caddy Plugins +## help -1. [How to write a Caddy plugin](https://github.com/caddyserver/caddy/wiki/Extending-Caddy) -2. [How to publish your plugin to the Caddy website](https://github.com/caddyserver/caddy/wiki/Publishing-a-Plugin-to-the-Download-Page) +``` +$ caddy help [] +``` -### Community +Prints CLI help text, optionally for a specific subcommand. -- [Forum](https://caddy.community) -- [Contributing Guide](https://github.com/caddyserver/caddy/blob/master/.github/CONTRIBUTING.md) -- [Slack](https://caddyserver.slack.com) (requires active [Sponsorship](https://caddyserver.com/pricing#sponsorship) or [Engineering Package](https://caddyserver.com/pricing#engineering-package) subscription) +## start -### Other Topics -- [Try QUIC](https://github.com/caddyserver/caddy/wiki/QUIC) -- [Troubleshoot PHP-FPM](https://github.com/caddyserver/caddy/wiki/Troubleshooting-PHP-FPM-and-FastCGI) -- [Running Caddy on Android](https://github.com/caddyserver/caddy/wiki/Running-Caddy-on-Android) \ No newline at end of file +``` +$ caddy start + [--config ] + [--adapter ] +``` + +Starts the Caddy process, optionally bootstrapped with an initial config file. Blocks until server is successfully running (or fails to run), then returns. On Windows, the child process will remain attached to the terminal, so closing the window will forcefully stop Caddy. See [`run`](#run) for more details. + +## run + +``` +$ caddy run + [--config ] + [--adapter ] + [--environ] +``` + +Same as `start`, but blocks indefinitely; i.e. runs Caddy in "daemon" mode. On Windows, this is recommended over `caddy start` when running Caddy manually since it will be more obvious that Caddy is still running and bound to the terminal window. + +If a config file is specified, it will be applied immediately after the process is running. If the config file is not in Caddy's native JSON format, you can specify an adapter with `--adapter` to adapt the given config file to Caddy's native format. The config adapter must be a registered module. Any warnings will be printed to the log, but beware that any adaptation without errors will immediately be used. If you want to review the results of the adaptation first, use the [`adapt`](#adapt) subcommand. + +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. + +If `--environ` is specified, the environment as seen by the Caddy process will be printed before starting. This is the same as the `environ` command but does not quit after printing. + +## stop + +``` +$ caddy stop +``` + +Gracefully stops the running Caddy process. (Note: this will stop any process named the same as the executable.) On Windows, this stop is forceful and Caddy will not have an opportunity to clean up any active locks; for a graceful shutdown on Windows, use Ctrl+C or the [/stop endpoint](#post-stop). + +## reload + +``` +$ caddy reload + --config + [--adapter ] + [--address ] +``` + +Gives the running Caddy instance a new configuration. This has the same effect as POSTing a document to the [/load](#post-load) endpoint, but is convenient for simple workflows revolving around config files. Since the admin endpoint is configurable, the endpoint configuration is loaded from the `--address` flag if specified; otherwise it is loaded from the given config file; otherwise the default is assumed. + +## version + +``` +$ caddy version +``` + +Prints the version. + +## list-modules + +``` +$ caddy list-modules + [--versions] +``` + +Prints the Caddy modules that are installed, optionally with version information from their associated Go modules. NOTE: Due to [a bug in the Go standard library](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. See our README for build instructions that preserve module version info. + +## environ + +``` +$ caddy environ +``` + +Prints the environment as seen by caddy. Can be useful when debugging init systems or process manager units like systemd. + +## adapt + +``` +$ caddy adapt + --config + --adapter + [--pretty] + [--validate] +``` + +Adapts a configuration to Caddy's native JSON config structure and writes the output to stdout, along with any warnings to stderr. If `--pretty` is specified, the output will be formatted with indentation for human readability. If `--validate` is specified, the adapted configuration will be loaded and provisioned 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. + +### Example + +To adapt a Caddyfile that you can easily read and tweak manually later: + +```bash +$ caddy adapt --config /path/to/Caddyfile --adapter caddyfile --pretty +``` + +## validate + +``` +$ caddy validate + --config + [--adapter ] +``` + +Validates a configuration file. 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. + + +# Admin endpoint + +Caddy is configured through an administration endpoint which is an HTTP listener with a REST API. + +Default address: `localhost:2019` + +## POST /load + +Sets Caddy's configuration. The response blocks until the reload completes or fails. Configuration changes are lightweight, efficient, and incur zero downtime. + +The request's Content-Type header indiciates the config format provided 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 ending in `/caddyfile`; or for JSON 5, specify a value ending in `/json5`; etc. + +_Note for Enterprise users_: If you use the `/config/` endpoint to modify configuration, you MUST NOT mix its use with `/load` because `/load` lacks the capabilities for partial configuration changes, and mixing their use could lead to inconsistent configuration states. + +### Example + +```bash +$ curl -X POST "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: + +```bash +$ curl -X POST "http://localhost:2019/load" \ + -H "Content-Type: text/caddyfile" \ + --data-binary @Caddyfile +``` + +## POST /stop + +Gracefully shuts down the server. + +### Example + +```bash +$ curl -X POST "http://localhost:2019/stop" +``` + +## GET /config/[scope] + +๐Ÿข _Enterprise_ + +Exports Caddy's current configuration. Returns a JSON body. Any path appended to this endpoint will traverse the configuration and return only that scope. + +### Examples + +```bash +$ curl "http://localhost:2019/config/" +{"apps":{"http":{"servers":{"myserver":{"listen":[":443"],"routes":[{"match":[{"host":["example.com"]}],"handle":[{"handler":"file_server"}]}]}}}}} +``` + +```bash +$ curl "http://localhost:2019/config/apps/http/servers/myserver/listen" +[":443"] +``` + + +## POST /config/[scope] + +๐Ÿข _Enterprise_ + +Changes Caddy's configuration at the named scope to the JSON body of the request. If the named scope is an array, POST appends; if an object, it creates or replaces. + +### Example + +```bash +$ curl -X POST \ + -H "Content-Type: application/json" \ + -d '":2080"' \ + "http://localhost:2019/config/apps/http/servers/myserver/listen" +``` + + +## PUT /config/[scope] + +๐Ÿข _Enterprise_ + +Changes Caddy's configuration at the named scope to the JSON body of the request. If the named scope is an array, PUT inserts; if an object, it strictly creates a new value. + +### Example + +```bash +$ curl -X PUT \ + -H "Content-Type: application/json" \ + -d '":2080"' \ + "http://localhost:2019/config/apps/http/servers/myserver/listen" +``` + + +## PATCH /config/[scope] + +๐Ÿข _Enterprise_ + +Changes Caddy's configuration at the named scope to the JSON body of the request. PATCH strictly replaces an existing value or array element. + +### Example + +```bash +$ curl -X PUT \ + -H "Content-Type: application/json" \ + -d '":2080"' \ + "http://localhost:2019/config/apps/http/servers/myserver/listen" +``` + + +## DELETE /config/[scope] + +๐Ÿข _Enterprise_ + +Changes Caddy's configuration at the named scope to the JSON body of the request. DELETE deletes the value at the named scope. + +### Example + +```bash +$ curl -X DELETE "http://localhost:2019/config/apps/http/servers/myserver" +``` + + +## Using IDs in JSON + +๐Ÿข _Enterprise_ + +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: + +```javascript +{ + "@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 `/config/` endpoint, but the ID takes the request directly into that scope of the config. For example, this request adds an upstream to a reverse proxy: + +``` +POST /config/apps/http/servers/myserver/routes/1/handle/0/upstreams "10.0.0.5:7000" +``` + +It would be shortened to: + +``` +POST /id/my_proxy/upstreams "10.0.0.5:7000" +``` + +which is much easier to remember and write by hand. + + +# Caddy modules + +Caddy modules are distinct from [Go modules](https://github.com/golang/go/wiki/Modules), although technically speaking, Caddy modules can be implemented by Go modules. When we talk about modules here, we mean Caddy modules. + +Caddy modules are the evolution of "plugins" in Caddy 1, and although modules are much-improved over plugins, the idea is similar. Various parts of Caddy's functionality can be swapped out or extended by use of modules which are statically compiled in at build-time. + +Each Caddy module has a name, scope/namespace, and ID: + +- A name looks like `a.b.c.module_id` +- The namespace would be `a.b.c` +- The module ID is `module_id` which must be unique in its namespace + +When the context makes it obvious what the namespace is, we generally refer to the module ID as the name, or use the terms interchangably; and in conversation the namespace can be omitted. + +_Host modules_ (or _parent modules_) are modules which load/initialize other modules. + +_Guest modules_ (or _child modules_) are modules which get loaded or initialized. All modules are at least guest modules. + + +# Config intro + +At its core, Caddy 2 is configured with JSON via an API. There are no config files, but Caddy's command line interface can wrap the task of loading a config from a file and feeding it to the API for you. There are also alternatives to writing JSON by hand; see [Config Adapters](#config-adapters) for more on that. + +This section covers just a few things you will find helpful as you craft a Caddy config. + +## Making one from scratch + +We have a [separate page](https://github.com/caddyserver/caddy/wiki/v2:-Config-from-Scratch) which walks you through an example of building up a config of a working static file server with automatic HTTPS from scratch. + + +## Duration values + +Any properties which expect a duration value are given as a string in the same format as [Go's time.Duration type](https://golang.org/pkg/time/#ParseDuration): + +> A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "ยตs"), "ms", "s", "m", "h". + +Valid examples: `"30s"`, `"15m"`, `"1h25m30s"`, and `"30us"`. + +## Placeholders (variables) + +You can inject dynamic values into Caddy 2's configuration by using placeholders (aka variables). Placeholders can be used in properties which accept placeholder values; not all properties execute placeholders. Only string values can use placeholders. + +Placeholders are enclosed in `{ }` and namespaced to avoid collisions. Different placeholders are available in different contexts. + +All contexts support the global placeholders: + +``` +{system.hostname} + The system's hostname +{system.os} + The system's OS +{system.arch} + The system's architecture +{system.slash} + The system's filepath separator +{env.variable_name} + Replace variable_name with any env variable (lowercase) +``` + +## Config adapters + +Caddy's native config language is JSON, but writing JSON by hand can be tedious and error-prone. That's why Caddy supports being configured with other means through _config adapters_. They make it possible to use config in a more comfortable or convenient format by outputting Caddy JSON for you. + +Config adapters do their best to translate your input to Caddy JSON with the highest fidelity and correctness, but some features/behaviors don't translate well or are not yet programmed. 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. + +That said, the Caddyfile format is specifically designed for Caddy, so any and all of the Caddyfile's features should translate to Caddy JSON correctly and completely. The Caddyfile adapter is a special case in this sense. + +Generally, config adapters will output warnings when something cannot be adapted in a way that would be expected. if the behavior cannot be replicated perfectly in Caddy, or if the feature doesn't make sense in Caddy, or if the adapter simply doesn't cover all the possible use cases yet. However, warnings are not errors, and for this reason we advise against simply throwing your NGINX config into the NGINX config adapter using `caddy run` to immediately start your server without first verifying the adapter's output. You can use the [`adapt`](#adapt) command for this. + +Config adapters are an exciting and unique feature. We hope you will help contribute to the config adapters! + +### Caddyfile adapter + +The Caddyfile is the #1 choice for most users to configure Caddy because it is extremely quick to learn, easy to comprehend, and very productive for developers. [The Caddyfile syntax is documented on the Caddy website.](https://caddyserver.com/docs/caddyfile) + +The Caddyfile is not as expressive as Caddy's native JSON, so it cannot describe all possible Caddy configurations. However, it is designed to be suitable for upwards of ~95% of use cases. Some odd web apps or other edge cases may necessitate using the Caddy JSON directly, but you can still use a config adapter such as the Caddyfile as a starting point. Caddy's config is the most flexible on the planet, even if the Caddyfile can't express all of the possibilities. + +The Caddyfile is geared toward _everyone_, not just beginners. Rather than skill level, the decision about whether to use the Caddyfile should be more about what your technical and business requirements are, or if nothing else, what you personally prefer. + +The Caddyfile can support different "server types" or syntaxes. The default is the HTTP server type, which we describe here. You may be familiar with others, such as the Corefile from [CoreDNS](https://coredns.io), which is a different flavor of the same Caddyfile syntax. + +The v2 Caddyfile is not backwards-compatible with v1; as such, you cannot bring your v1 Caddyfile and expect it to work in v2 without changes (although some will be compatible by chance). + +At a high level, major changes from v1 include: + +- Some directives have changed, along with their syntax/structure. +- The static file server is no longer enabled by default. (There was no way to turn it off in v1, which could be problematic in some situations.) There is a `file_server` directive to enable the static file server. +- Request matching is much more powerful and not limited to just path prefixes anymore. +- Directives need not be centrally registered on a list in the Caddy code base anymore. This means you can write a handler module for Caddy and publish it and use it right away, without needing to submit a PR to the Caddy code base to add your directive to the list and wait for the next Caddy release. + + +#### Matcher tokens + +Request matching is one of the most important parts of configuring a web server. For directives that inject handlers into the middleware chain, you can specify which requests that handler applies to with _matchers_. + +A matcher token comes immediately after the directive token and can be one of three forms: + +1. `*` to match all requests. +2. `/path` starting with a forward slash to match path prefix. +3. `match:name` to specify a matcher by name. + +If a matcher token is omitted, it is the same as a wildcard matcher. The wildcard matcher is only needed if a matcher token is required; for example, if the argument you want to give a directive is itself a path, it would be ambiguous with a path matcher, so you use a wildcard token: `root * /home/www/mysite` + +Path matching is the most common, so it is inlined for convenience: `redir /old-article.html /new-article.html` + +Specifying a matcher by name gives you more flexibility. To define a matcher by name, use the `matcher` directive: + +```plain +matcher name { + ... +} +``` + +See the [Caddyfile directives](#caddyfile-directives) below for more details about matchers. + +#### Shorthand placeholders + +The Caddyfile is supposed to be easy to write, so it also provides shorter placeholder variable names for you to use. Instead of `{http.request.uri.path}` for example, you could just write `{path}`. Again, this loses some of the flexibility of the full JSON config because these shorthand variables are not properly namespaced. They are optional, however, so you are welcome to use the full name. + +| Shorthand | Replaces | +|------------|---------------------------------| +| {uri} | {http.request.uri} | +| {path} | {http.request.uri.path} | +| {host} | {http.request.host} | +| {hostport} | {http.request.hostport} | +| {method} | {http.request.method} | +| {scheme} | {http.request.scheme} | +| {file} | {http.request.uri.path.file} | +| {dir} | {http.request.uri.path.dir} | +| {query} | {http.request.uri.query_string} | + + +#### Global config + +The Caddyfile has a special place for specific settings that apply to the whole process rather than specifically per-site. This allows us to replace an arbitrary and growing number of CLI flags and environment variables, which constituted a large portion of hidden configuration in version 1. + +Simply create a server block with no keys to specify global options. The following options are allowed: + +``` +{ + http_port + https_port + handler_order appearance| { + + } + storage { + + } + experimental_http3 +} +``` + +Note that this feature is not a "defaults" block which all other sites inherit. The Caddyfile does not have inheritence. To share repeated directives across many site definitions, use snippets. + + +#### Caddyfile directives + +##### root + +Specifies the site root, usable by other directives that need a root path. + +``` +root [] +``` + +##### matcher + +Creates a matcher. Multiple matchers are AND'ed. + +``` +matcher { + host + path + path_regexp + method + query + header + header_regexp + protocol http|https|grpc + remote_ip + file { + root + try_files + try_policy first_exist|smallest_size|largest_size|most_recent_modified + } +} +``` + +##### file_server + +Enables static file server, optionally with directory browsing. + +``` +file_server [] [browse] { + hide + index + browse [] + root +} +``` + +##### encode + +Encodes HTTP responses. Used for compression. + +``` +encode [] { + gzip [] + zstd + brotli [] +} +``` + +##### headers + +Manipulates HTTP response headers. Can add, remove, set, or do substring replacements. + +``` +headers [] [[+|-] [] []] { + [+] [ []] + - +} +``` + +##### request_header + +Manipulates an HTTP request header. + +``` +request_header [] [[+|-] [] [replacement]] +``` + +##### php_fastcgi + +Proxies requests to one or more FastCGI backends specially configured for PHP apps. It has the same syntax and options as [reverse_proxy](#reverse_proxy), but is shorthand for a configuration equivalent to this example: + +``` +# canonicalize URI trailing slash when index file present +matcher indexFiles { + not { + path */ + } + file { + try_files {path}/index.php + } +} +redir match:indexFiles {path}/ + +# internally rewrite directory URIs to index.php files +try_files {path} {path}/index.php index.php + +# proxy any requests for PHP files to backend via FastCGI +matcher phpFiles { + path *.php +} +reverse_proxy match:phpFiles php-fpm:9000 { + transport fastcgi { + split .php + } +} +``` + +Thus, the following single line is sufficient and works for _most_ PHP scripts (replace `php-fpm:9000` with your php-fpm listener): + +``` +php_fastcgi php-fpm:9000 +``` + + +##### reverse_proxy + +Proxies requests to one or more backends with configurable transport options. + +``` +reverse_proxy [] [] { + # backends + to + + # load balancing + lb_policy [] + lb_try_duration + lb_try_interval + + # active health checking + health_path + health_port + health_interval + health_timeout + health_status + health_body + + # passive health checking + max_fails + fail_duration + max_conns + unhealthy_status + unhealthy_latency + + # header manipulation + header_up [+|-] [ []] + header_down [+|-] [ []] + + # round trip + transport { + ... + } +``` + +HTTP transport options: + +``` +transport http { + read_buffer + write_buffer + dial_timeout + tls # use HTTPS instead of HTTP + tls_client_auth + tls_insecure_skip_verify + tls_timeout + keepalive [off|] + keepalive_idle_conns +} +``` + +FastCGI transport options: + +``` +transport fastcgi { + root + split + env +} +``` + +For proxying to PHP via FastCGI (php-fpm) specifically, see the [php_fastcgi](#php_fastcgi) directive. + +##### redir + +Issue an HTTP redirect. + +``` +redir [] to [|permanent|temporary] +``` + +##### rewrite + +Rewrite the request URI. + +``` +rewrite [] to +``` + +##### templates + +Execute the HTTP response body as a [template](https://github.com/caddyserver/caddy/wiki/v2:-Templates). + +``` +templates [] { + mime + between + root +} +``` + +##### respond + +Respond with a hard-coded HTTP response. + +``` +respond [] { + body + close +} +``` + +##### tls (directive) + +Configure TLS for the site in which the directive appears. (Still WIP) + +``` +tls [off| ] { + load + protocols [] + ciphers + curves + alpn +} +``` + +##### try_files + +Rewrites a request based on the existence of the listed files on disk. Because this is a fairly common rewrite, this directive simply combines a `rewrite` handler with a `file` matcher for convenience in writing. + +``` +try_files +``` + + +# Config structure + +Caddy configuration is a JSON document. + +Generally, object keys are optional, as most of the "required" settings have sane defaults. If something is actually explicitly required for a successful config load, the docs will say so. + +At the top level, you'll find properties needed for Caddy's basic functionality: + +```json +{ + "admin": {}, + "storage": {}, + "apps": {} +} +``` + +## admin + +Configures the administration endpoint. + +```json +{ + "listen": "localhost:2019" +} +``` + +- `listen`: the address to which the admin endpoint's listener should bind itself. Can be [any single network address](#httpserverslisten) that can be parsed by Caddy. + +## storage + +Configures Caddy's default storage module. A storage module defines how and where Caddy stores assets (such as TLS certificates). + +Default: The local file system (`caddy.storage.file_system`). If `XDG_DATA_HOME` is set, then `$XDG_DATA_HOME/caddy` is the folder. Otherwise, `$HOME/.local/share/caddy` is the folder. + +### caddy.storage.file_system + +```json +{ + "module": "file_system", + "root": "/var/caddy_storage" +} +``` + +- `module`: The ID of the storage module. +- `root`: The base path in which things should be stored. + + +## apps + +Each app to initialize should be keyed by its name (i.e. module ID). + +Example which initializes the `tls` and `http` apps: + +```json +{ + "apps": { + "tls": { ... }, + "http": { ... } + } +} +``` + +### http + +The `http` app implements an HTTP server with automatic HTTPS: + +```json +{ + "http_port": 80, + "https_port": 443, + "grace_period": "", + "servers": {} +} +``` + +- `http_port`: The port to use for HTTP (optional; used for automatic HTTPS). +- `https_port`: The port to use for HTTPS (optional; used for automatic HTTPS). +- `grace_period`: How long to allow servers to shut down gracefully before forcing them to stop. Duration values follow Go's `time.Duration` format, e.g. `"10s"` or `"1m30s"`. +- `servers`: Server configurations, keyed by unique names you choose. A server is a set of listeners and routes which make sense to group together. At this time, servers cannot have overlapping listeners. + + +#### http/servers + +A map of server ID (of your choice) to its configuration object. Each server is an object with the following structure: + +```json +{ + "listen": [], + "read_timeout": "", + "read_header_timeout": "", + "write_timeout": "", + "idle_timeout": "", + "max_header_bytes": 0, + "routes": [], + "errors": {}, + "tls_connection_policies": [], + "automatic_https": {}, + "max_rehandles": 3, + "strict_sni_host": false, + "experimental_http3": false +} +``` + +- `listen`: The list of listener addresses to bind to. +- `read_timeout`: How long to allow reading an HTTP request. +- `read_header_timeout`: How long to allow reading an HTTP request header. +- `write_timeout`: How long to allow writing an HTTP response. +- `idle_timeout`: How long to keep idle connections open. +- `max_header_bytes`: How many bytes to allow an HTTP request header to be read. +- `routes`: The list of routes. +- `errors`: Configures how to handle errors during HTTP requests. +- `tls_connection_policies`: List of TLS connection policies. +- `automatic_https`: Customize or disable automatic HTTPS. +- `max_rehandles`: How many rehandles to allow; prevents infinite looping. +- `strict_sni_host`: If true, enforce that an HTTP Host header matches the connection's ServerName (SNI) value from the TLS handshake. Important when using TLS client authentication. +- `experimental_http3`: If true, IETF-draft-compliant HTTP/3 ("QUIC") will be enabled. It creates a UDP listener. This feature is experimental and is not subject to compatibility promises. + +#### http/servers/listen + +Listener addresses take on the following form: + + network/host:port-range + +For example: + + :8080 + 127.0.0.1:8080 + localhost:8080 + localhost:8080-8085 + tcp/localhost:8080 + udp/localhost:9005 + unix//path/to/socket + +#### http/servers/routes + +Routes are not your typical notion of routes (i.e. "GET /foo/bar" -> someFunc). Routes in Caddy 2 are much more powerful and dynamic. They are given in an ordered list, and each route has two parts: + +1. match (optional) +2. handle + +You can think of "match-handle" like "if-then" if that helps (but Caddy config is not really imperative or procedural). + +For each request, route matchers are evaluated in the order they are given in the list. Each route that matches the request is compiled into the _composite route_, which ends up becoming the HTTP handler for the request. All matchers in the routes list are evaluated before the request is sent down the handler chain. + +In Caddy 1, HTTP handlers were chained in a hard-coded order you had no control over. Unlike Caddy 1, handlers in Caddy 2 are chained in the order you specify in the routes. So, the order of routes matters. + +In some cases, a request can be _rehandled_, meaning that it is virtually processed through the top-level handler again. This is useful if changes were made to a request (such as rewriting part of it) and you want to process it as if it originally came in like that. Other web servers call this an internal redirect. Upon rehandling, a new composite route will be compiled and the request will be processed again. + +The structure for a route is: + +```json +{ + "group": "", + "match": [], + "handle": [], + "terminal": false +} +``` + +- `group`: If this route belongs to a group, specify its name here. Groups are used for mutual exclusion. Only the first matching route from a group is compiled into the composite route. (Think like radio buttons on an HTML form.) Groups can be used to implement "else" and "else-if" logic with matchers. +- `match`: The "if" statement of the route. This specifies an ordered list of matcher sets. +- `handle`: The "then" statement of the route. This specifies an ordered list of handlers to chain together. Handlers are chained from first to last, so requests flow from the first handler to the last. +- `terminal`: If true, no additional routes will be considered. + + +##### HTTP Placeholders + +Within an HTTP route, you can use additional placeholders: + +``` +{http.request.hostport} + The Host header of the request (including port, if given) +{http.request.host} + Same as hostport but with port stripped +{http.request.host.labels.N} + N is the number; i.e. for "foo.example.com": + 0 = "com" + 1 = "example" + 2 = "foo" +{http.request.port} + The port of the request. +{http.request.scheme} + The scheme of the request (http/https usually) +{http.request.uri} + The full request URI +{http.request.uri.path} + The path component of the request URI +{http.request.uri.path.file} + The filename in the path, excluding directory +{http.request.uri.path.dir} + The directory, excluding leaf filename +{http.request.uri.query.PARAM} + A specific query string parameter given by PARAM +{http.request.header.FIELD-NAME} + Request header field name (lower-cased) +{http.request.cookie.COOKIE_NAME} + Request cookie name (lower-cased) +{http.response.header.FIELD-NAME} + Response header field name (lower-cased) +``` + +If you are using regexp matchers, capture groups (both named and numeric) are available as well: + + {http.matchers.PATH_REGEXP.PATTERN_NAME.CAPTURE_GROUP_NAME} + +Replacing: + +- PATH_REGEXP with the name of the matcher that has the regular expression. +- PATTERN_NAME with the lower-cased name you gave the pattern. +- CAPTURE_GROUP_NAME with the name or index number of the capture group in the regular expression. + +This will allow you to access capture groups from anywhere in your HTTP route. + + +#### http/servers/routes/match + +Each route takes a list of matcher sets. A matcher set is comprised of matchers of various types. A matcher may be comprised of multiple values. + +Example: + +```json +[ + { + "host": ["example.com"] + }, + { + "host": ["example.com", "sub.example.com"], + "path": ["/foo/bar"] + } +] +``` + +Here you can see there are 2 matcher sets. The first one has one matcher (`host`), and the second one has two (`host` and `path`). + +The boolean logic of request matching goes like this: + +- Matcher sets are OR'ed (first matching matcher set is sufficient). +- Matchers within a set are AND'ed (all matchers in the set must match). +- Values within a specific matcher are OR'ed. + +This design enables moderately complex logic such as: + +``` +IF (Host = "example.com") + OR (Host = "sub.example.com" AND Path = "/foo/bar") +``` + +The expressions in parentheses are matcher sets. This expression has 2 matcher sets. This expression uses two matchers: Host and Path, which are AND'ed together within their set. + +Even more advanced logic can be expressed through the Starlark expression matcher at virtually no loss in performance. + +A route may omit matchers entirely to match all requests. + +All matchers within the same route list are evaluated before the requst is handled and do not modify the request, so all the matchers will see the same version of the request. + +##### http.matchers.host + +Matches requests by Host header. + +```json +"host": [] +``` + +Host values can contain wildcards to substitute for one label of the domain name. + +##### http.matchers.path + +Matches requests by request path. + +```json +"path": [] +``` + +Paths may contain globular patterns. + +##### http.matchers.path_regexp + +Matches requests by request path using a regular expression. + +```json +"path_regexp": { + "name": "", + "pattern": "", +} +``` + +- `name`: A name for the regular expression so you can access its capture groups in placeholders. Optional. +- `pattern`: The regular expression in [Go's regexp syntax](https://golang.org/pkg/regexp/). + + +##### http.matchers.method + +Matches requests by the request method. + +```json +"method": [] +``` + +Methods should be upper-cased. + +##### http.matchers.query + +Matches requests by query string parameters. + +```json +"query": { + "param": ["values"] +} +``` + +The object keys should be query string parameters, with the values to match in an array, as strings. + + +##### http.matchers.header + +Matches requests by request headers. + +```json +"header": { + "Field": ["values"] +} +``` + +The object keys should be header field names, with the values to match in an array. If the values array is present but empty, it will accept any value and match merely on the presence of the header field. + +##### http.matchers.header_regexp + +Matches requests by request headers using a regular expression. + +```json +"header_regexp": { + "Field": { + "name": "", + "pattern": "" + } +} +``` + +The object keys should be header fields, then: + +- `name`: A name for the regular expression so you can access its capture groups in placeholders. Optional. +- `pattern`: The regular expression in [Go's regexp syntax](https://golang.org/pkg/regexp/). + +##### http.matchers.protocol + +Matches requests by the protocol being used. + +```json +"protocol": "" +``` + +Possible values are `http`, `https`, or `grpc`. + +##### http.matchers.not + +Matches requests by negating a matcher set. + +```json +"not": {} +``` + +Its value should be a matcher set. The result of the matcher set will be negated. + +##### http.matchers.remote_ip + +Matches requests by the client IP address. + +```json +"remote_ip": { + "ranges": [] +} +``` + +- `ranges`: A list of IP addresses or CIDR ranges to match. + +##### http.matchers.starlark_expr + +Matches requests by evaluating a Starlark expression. This provides a great deal of flexibility with regards to boolean logic, and is a fine fit for advanced matching needs. + +```json +"starlark_expr": "" +``` + +The value should be a Starlark expression. (TODO: examples and docs) + + +##### http.matchers.file + +Matches requests based on files on disk. + +```json +"file": { + "root": "", + "try_files": [], + "try_policy": [] +} +``` + +- `root`: The base path with which relative paths will be rooted. Default is `{http.vars.root}` if set, or current working directory. +- `try_files`: A list of root-relative file paths to "try" matching (similar to nginx's try_files). A file will be selected based on try_policy. Directory convention is observed: paths ending in a forward slash (`/`) _must_ be a directory in order to match; paths ending without a forward slash _must not_ be a directory for a match. For example: `/my/path/` matches only directories and `/my/path` matches only files. To match both, include both patterns. +- `try_policy`: When trying files listed in `try_files`, use this policy to choose one. + - `first_exist` (default): Choose the first file that exists. + - `smallest_size`: Choose the file with the smallest size. + - `largest_size`: Choose the file with the largest size. + - `most_recent_modified`: Choose the file that was most recently modified. + +If a file is matched, two new placeholders will be made available: + +- `{http.matchers.file.relative}`: The root-relative path of the file. This is often useful when rewriting requests. +- `{http.matchers.file.absolute}`: The absolute path of the file. + + + +#### http/servers/handle + +Handlers are modules which handle HTTP requests. They are chained together in a middleware fashion: requests flow from the first handler to the last (top of the config to the bottom), with the possibility that any handler could abort the chain and/or return an error. Responses flow back up the chain as they are written out to the client. + +Not all handlers call the next handler in the chain. For example, the `file_server` handler always serves a file from disk or returns an error. Thus, configuring handlers after `file_server` in your route is illogical, since they would never be executed. You will want to put handlers which originate the response at the very end of your route(s). The documentation for a module should state whether it invokes the next handler, but sometimes it is common sense. + +Some handlers manipulate the response. Remember that **requests flow down, and responses flow up**. For example, if you wanted to use both `templates` and `encode` handlers, you would need to put `templates` _after_ `encode` in your route, because responses flow up. Thus, `templates` will be able to parse and execute the plain-text response as a template, and then return it up to the `encode` handler which will then compress it into a binary format. + +If `templates` came before `encode`, then `encode` would write a compressed, binary-encoded response to `templates` which would not be able to parse the response properly. + +The correct order, then, is this: + +``` +[ + { + "handler": "encode" + }, + { + "handler": "templates" + }, + { + "handler": "file_server" + } +] +``` + +The request flows โฌ‡๏ธ **DOWN** (`encode` -> `templates` -> `file_server`). + +1. First, `encode` will choose how to encode the response and wrap the response. +2. Then, `templates` will wrap the response with a buffer. +3. Finally, `file_server` will originate the content from a file. + +The response flows โฌ†๏ธ **UP** (`file_server` -> `templates` -> `encode`): + +1. First, `file_server` will write the file to the response. +2. That write will be buffered and then executed by `templates`. +3. Lastly, the write from templates will flow into `encode` which will compress the stream. + +If you think of routes in this way, it will be easy and even fun to solve the puzzle of writing correct routes. + + +##### http.handlers.headers + +Modifies request or response headers. + +Changes to headers are applied immediately, except for the response headers when `deferred` is true or when `required` is set. In those cases, the changes are applied when the headers are written to the response. Note that deferred changes do not take effect if an error occurs later in the middleware chain. + +Properties in this module accept placeholders. + +Response header operations can be conditioned upon response status code and/or other header values. + +```json +{ + "handler": "headers", + "request": { + "set": { + "Field": ["overwrites"] + }, + "add": { + "Field": ["appends"] + }, + "delete": ["Goodbye-Field"], + "replace": { + "Field": [ + { + "search": "old", + "search_regexp": "old(e)?", + "replace": "new" + } + ] + } + }, + "response": { + "set": { + "Field": ["overwrites"] + }, + "add": { + "Field": ["appends"] + }, + "delete": ["Goodbye-Field"], + "replace": { + "Field": [ + { + "search": "old", + "search_regexp": "old(e)?", + "replace": "new" + } + ] + }, + "deferred": true, + "require": { + "status_code": [2, 301], + "headers": { + "Foo": ["bar"] + } + } + } +} +``` + +- `request`: Request headers to set, add, delete, or perform substring replacements. +- `response`: Response headers to set, add, delete, or perform substring replacements. +- `*.replace`: Performs replacements of header values. Either the `search` or `search_regexp` parameters can be specified, but not both. `search` is a simple and fast substring replacement, while `search_regexp` is a regular expression replacement. Matched values are replaced with the value of `replace`. If the field name is `*`, the replacements will happen on every accessible header field. +- `response.deferred`: If true, changes will be applied when the response headers are written instead of as the request is being processed (immediately). Not necessary if `require` is set. +- `response.require`: Defer response header operations until they are written, and only if these criteria are met. +- `response.require.status_code`: Apply response header changes if the status code matches one of these values. Can be a single digit to apply to all statuses in that class (i.e. `2` means all `2xx` status codes) or a full status code (e.g. `404`). +- `response.require.headers`: Apply response header changes if the given response headers have the given value(s). + + +##### http.handlers.rewrite + +Changes the request's URI or method. + +Properties in this module accept placeholders. + +```json +{ + "handler": "rewrite", + "method": "FOO", + "uri": "/new/path?param=val", + "rehandle": false +} +``` + +- `method`: Changes the request's HTTP verb. +- `uri`: Changes the request's URI. +- `rehandle`: If true, the request will sent for rehandling after rewriting. + + +##### http.handlers.markdown + +Currently, does almost nothing: very plain/simple markdown rendering using Blackfriday. Will be configurable. But unlike Caddy 1, this already allows rendering *any* response body as Markdown, whether it be from a proxied upstream or a static file server. This needs a lot more testing and development. + +```json +{ + "handler": "markdown" +} +``` + + +##### http.handlers.request_body + +Change or limit the request body. + + +```json +{ + "handler": "request_body", + "max_size": 0 +} +``` + +- `max_size`: The maximum number of bytes to allow reading from the body by a later handler. + + +##### http.handlers.encode + +Compresses responses on-the-fly. + +```json +{ + "handler": "encode", + "encodings": { + "gzip": {"level": 5}, + "zstd": {}, + "brotli": {"quality": 6} + }, + "prefer": "", + "minimum_length": 512 +} +``` + +- `encodings`: Selection of compression algorithms to choose from. The best one will be chosen based on the client's Accept-Encoding header. Available options are gzip, zstd, and brotli. Note that brotli is currently very slow and it is recommended to pre-compress static content instead. +- `prefer`: If the client has no strong preference, choose this encoding. TODO: Not yet implemented +- `minimum_length`: Only encode responses that are at least this many bytes long. + + +##### http.handlers.templates + +Interprets the response as a template body, then executes the template and writes the response to the client. [There are functions](https://github.com/caddyserver/caddy/wiki/v2:-Templates) to include other files, make sub-requests (virtual HTTP requests), render Markdown, manipulate headers, access the request fields, manipulate strings, do math, work with data structures, and more. + +```json +{ + "handler": "templates", + "include_root": "", + "mime_types": ["text/html", "text/plain", "text/markdown"], + "delimiters": ["{{", "}}"] +} +``` + +- `include_root`: The root path from which to load files. Required if template functions accessing the file system are used (such as .Include). Default is `{http.vars.root}` if set, or current working directory. +- `mime_types`: The MIME types for which to render templates. It is important to use this if the route matchers do not exclude images or other binary files. +- `delimiters`: The template action delimiters. + +See [all available template actions](https://github.com/caddyserver/caddy/wiki/v2:-Templates). + + +##### http.handlers.static_response + +Responds to the request with a static (hard-coded) response. Does not call the next handler in the chain. + +This is a great way to do HTTP redirects. + +```json +{ + "handler": "static_response", + "status_code": 307, + "headers": { + "Location": ["https://example.com/foo"] + }, + "body": "Response body", + "close": true +} +``` + +- `status_code`: The HTTP status code to respond with. Can be an integer or, if needing to use a placeholder, a string. +- `headers`: Header fields to set on the response. +- `body`: The response body. +- `close`: If true, the server will close the client's connection after writing the response. + + +##### http.handlers.file_server + +A powerful, flexible static file server. + +In general, the request URI's path will be joined to the file server's root to get the file path to serve. + +This module executes placeholders in most of its properties. It does not call the next handler in the chain. + +```json +{ + "handler": "file_server", + "root": "", + "hide": [], + "index_names": [], + "browse": { + "template_file": "" + }, + "canonical_uris": true +} +``` + +- `root`: The path to the root of the site. Default is `{http.vars.root}` if set, or current working directory. +- `hide`: A list of files or folders to hide; the file server will pretend as if they don't exist. Accepts globular patterns like "\*.hidden" or "/foo/*/bar". +- `index_names`: The names of files to try as index files if a folder is requested. +- `browse`: Enables browsing if a directory was requested. +- `browse.template_file`: Use this template file instead of the default browse template. +- `canonical_uris`: Use redirects to enforce trailing slash appended to URIs for directories, or to remove trailing slash from URIs for files. Default is true. + + + +##### http.handlers.reverse_proxy + +A highly flexible and configurable multi-host reverse proxy with load balancing, health checks, and pluggable transport and security options. Does not call the next handler in the chain. + +```javascript +{ + "handler": "reverse_proxy", + "transport": { + "protocol": "http", + "tls": { + "root_ca_pool": ["..."], + "client_certificate_file": "...", + "client_certificate_key_file": "...", + "insecure_skip_verify": false, + "handshake_timeout": "2s" + }, + "keep_alive": { + "enabled": true, + "max_idle_conns": 1500, + "max_idle_conns_per_host": 50, + "idle_timeout": "2m" + }, + "compression": true, + "max_conns_per_host": 100, + "dial_timeout": "2s", + "response_header_timeout": "10s", + "expect_continue_timeout": "5s", + "max_response_header_size": 1024, + "write_buffer_size": 4096, + "read_buffer_size": 4096 + }, + "circuit_breaker": {}, + "load_balancing": { + "selection_policy": { + "policy": "header", + "header_name": "Server-ID" + }, + "try_duration": "10s", + "try_interval": "250ms" + }, + "health_checks": { + "active": { + "path": "/ok", + "port": 1234, + "interval": "10s", + "timeout": "2s", + "max_size": 1024, + "expect_status": 200, + "expect_body": ".*foobar.*" + }, + "passive": { + "max_fails": 3, + "fail_duration": "1m", + "unhealthy_request_count": 100, + "unhealthy_status": [500, 502], + "unhealthy_latency": "5s" + } + }, + "headers": { + // same structure as 'http.handlers.headers' + } + "upstreams": [ + { + "dial": "localhost:7777" + }, + { + "dial": "localhost:7799", + "max_requests": 1000 + }, + ] +} +``` + +- `transport`: The module for round-tripping requests. See below for options. +- `circuit_breaker` (๐Ÿข _Enterprise_): The module for short-circuiting before backends become unhealthy. +- `load_balancing`: Configures load balancing among a group of hosts. +- `load_balancing.selection_policy`: The module to use for selecting a host from a pool. See below for options. +- `load_balancing.try_duration`: How long to try selecting available backends for each request if the next available host is down. By default, this retry is disabled. Clients may hang for this long while the load balancer tries to find an available upstream host. +- `load_balancing.try_interval`: 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 `try_duration` can cause the CPU to spin if all backends are down and latency is very low. +- `health_checks`: Configures active and passive health checks. Active health checks run in the background on a timer, whereas passive health checks monitor proxied requests. +- `health_checks.active.path`: The URI path to use for health checks. +- `health_checks.active.port`: The port to use (if different) for health checks. +- `health_checks.active.interval`: How frequently to perform active health checks. +- `health_checks.active.timeout`: How long to wait for a response from a backend before considering it unhealthy. +- `health_checks.active.max_size`: The maximum response body to download from the backend during a health check. +- `health_checks.active.expect_status`: The HTTP status code to expect from a healthy backend. +- `health_checks.active.expect_body`: A regular expression against which to match the response body of a healthy backend. +- `health_checks.passive.fail_duration`: How long to remember a failed request to a backend. A duration > 0 enables passive health checking. Default is 0. +- `health_checks.passive.max_fails`: The number of failed requests within the `fail_duration` window to consider a backend as "down". Must be >= 1; default is 1. Requires that `fail_duration` be > 0. +- `health_checks.passive.unhealthy_request_count`: Limits the number of simultaneous requests to a backend by marking the backend as "down" if it has this many concurrent requests or more. +- `health_checks.passive.unhealthy_status`: Count the request as failed if the response comes back with a status code. +- `health_checks.passive.unhealthy_latency`: Count the request as failed if the response takes at least this long to receive. +- `headers`: Mutates proxied request and response headers to and from the chosen upstream. Has the same structure as the [http.handlers.headers](#httphandlersheaders) module. Can use placeholders, including those set by this handler (see below). +- `upstreams`: A list of backends (upstream hosts) to add to the pool. If there are multiple backends, requests will be load-balanced between them using the configured load balancing policy. +- `upstreams.[n].dial`: The network address of the upstream in [Caddy's network address syntax](#httpserverslisten). Addresses which represent more than one host will be expanded to multiple upstreams. +- `upstreams.[n].max_requests`: The maximum number of simultaneous requests to allow to this host. If set, overrides the global `health_checks.passive.unhealthy_request_count`. + +During proxying, this module sets the following placeholders (which can be used both within and after this handler): + +``` +{http.handlers.reverse_proxy.upstream.address} + The full address to the upstream as given in the config +{http.handlers.reverse_proxy.upstream.hostport} + The host:port of the upstream +{http.handlers.reverse_proxy.upstream.host} + The host of the upstream +{http.handlers.reverse_proxy.upstream.port} + The port of the upstream +{http.handlers.reverse_proxy.upstream.requests} + The approximate current number of requests to the upstream +{http.handlers.reverse_proxy.upstream.max_requests} + The maximum approximate number of requests allowed to the upstream +{http.handlers.reverse_proxy.upstream.fails} + The number of recent failed requests to the upstream +``` + +Transport modules: + +**HTTP**: The HTTP transport module is the default, and sane parameters are used if not explicitly configured. + +```json +{ + "protocol": "http", + "tls": { + "root_ca_pool": ["..."], + "client_certificate_file": "cert.pem", + "client_certificate_key_file": "key.pem", + "insecure_skip_verify": false, + "handshake_timeout": "2s" + }, + "keep_alive": { + "enabled": true, + "probe_interval": "500ms", + "max_idle_conns": 16, + "max_idle_conns_per_host": 4, + "idle_timeout": "2m" + }, + "compression": true, + "max_conns_per_host": 0, + "dial_timeout": "5s", + "fallback_delay": "250ms", + "response_header_timeout": "5s", + "max_response_header_size": 1024, + "expect_continue_timeout": "5s", + "read_buffer_size": 4096, + "write_buffer_size": 4096, +} +``` + +- `tls`: Configures TLS between the proxy and the backend. +- `tls.root_ca_pool`: A list of base64-encoded DER certificates to use as the root CA pool; these are the CA certificates that will be accepted by the proxy when connecting to a backend with TLS. +- `tls.client_certificate_file`: Path to a PEM certificate file to present to the backend. For use with TLS client authentication (mTLS, i.e. for the proxy to authenticate with the backend). +- `tls.client_certificate_key_file`: Path to the PEM private key file associated with the client certificate in `client_certificate_file`. +- `tls.insecure_skip_verify`: If true, disables certificate verification. This is insecure, dangerous, and cancels the benefits of TLS. Use only for testing/development. +- `tls.handshake_timeout`: Maximum time to establish TLS connection with the backend. +- `keep_alive`: TCP Keep-Alive configuration between the proxy and the backends. +- `keep_alive.enabled`: If false, TCP Keep-Alive will be disabled. Each request will establish a new connection. Default is true (Keep-Alive enabled). +- `keep_alive.probe_interval`: How often to probe for idleness. +- `keep_alive.max_idle_conns`: Maximum number of idle connections to keep alive across any host. +- `keep_alive.max_idle_conns_per_host`: Maximum number of idle connections to keep alive for a single host. +- `keep_alive.idle_timeout`: How long an idle connection should be kept alive without activity. +- `compression`: If false, compression to the backend will be disabled. Default true (enabled). +- `max_conns_per_host`: Maximum number of connections to allow per backend from this proxy. If this limit is reached, connections will block until an opening becomes available. +- `dial_timeout`: Maximum time allowed to dial connection to a backend. +- `fallback_delay`: Duration between dual stack fallback attempts. Not used by default. +- `response_header_timeout`: Maximum time to wait for response headers to be downloaded. +- `max_response_header_size`: Maximum size to allow for response headers. +- `expect_continue_timeout`: Maximum time to allow for HTTP 100 Continue responses. +- `read_buffer_size`: Size of the read buffer in bytes. +- `write_buffer_size`: Size of the write buffer in bytes. + + +**FastCGI**: The FastCGI transport module is often used to proxy requests to PHP backends like php-fpm. + +```json +{ + "protocol": "fastcgi", + "root": "/var/www/mysite", + "split_path": ".php", + "env": [["key", "value"]] +} +``` + +- `root`: Path to the root of the site, which is necessary when creating the environment variables for the request's FastCGI environment. Default is `{http.vars.root}` or current working directory. +- `split_path`: To create the FastCGI environment, the URI path will be split into two parts, with the first part ending with the first occurrence of this substring. The first part will be used as the actual resource (CGI script) name in DOCUMENT_URI, and the second piece will be set to PATH_INFO for the CGI script to use. +- `env`: Key-value pairs to add to the FastCGI environment. + +Load balancing policies: + +**round_robin**: Host is selected in turn. + +```json +{ + "policy": "round_robin" +} +``` + +**random**: Host is selected randomly. + +```json +{ + "policy": "random" +} +``` + +**random_choose**: A number of hosts (N) are randomly selected, then the host with the fewest concurrent connections is chosen from among those. N defaults to 2 and is the recommended value. Also referred to as _the power of two random choices_. + +```json +{ + "policy": "random_choose", + "choose": 2 +} +``` + +**least_conn**: Host with the fewest number of concurrent requests is selected. If tied for fewest, host is chosen randomly from among them. + +```json +{ + "policy": "least_conn" +} +``` + +**first**: First available host is selected. + +```json +{ + "policy": "first" +} +``` + +**ip_hash**: Host is selected by hashing the remote IP of the request. Each client IP will always get the same host, but specifically which host is arbitrary. + +```json +{ + "policy": "ip_hash" +} +``` + +**header**: Host is selected by hashing the value of a request header. Each request with a certain header value will get the same host, but specifically which host is arbitrary. + +```json +{ + "policy": "header", + "field": "Field-Name" +} +``` + +**uri_hash**: Host is selected by hashing the request URI. Each request with a certain URI will get the same host, but specifically which host is arbitrary. + +```json +{ + "policy": "uri_hash" +} +``` + + +##### http.handlers.subroute + +Creates a group of routes that are compiled and executed when this handler is invoked. This is useful if the evaluation of matchers needs to be deferred, like if they depend on placeholders created by other matchers or handlers that need to be evaluated first. + +This handler does not invoke the next handler in the chain. Instead, it compiles and executes the chain defined in `routes`. + +```json +{ + "handler": "subroute", + "routes": [] +} +``` + +- `routes`: The list of routes to compile and execute. + + +##### http.handlers.error + +This handler returns an error value, but does not write a response. This is useful when you want the server to act as if an error occurred; for example, to invoke your custom error handling logic. + +Since this handler does not write a response, the error information is for use by the server or other handlers to know how to handle the error. + +```json +{ + "handler": "error", + "status_code": 404, + "error": "" +} +``` + +- `status_code`: The recommended HTTP status code. Can be either an integer or a string if placeholders are needed. Optional. Default is 500. +- `error`: The error message. Optional. Default is no error message. + + +##### http.handlers.vars + +Sets variables associated with the request that can be used later with placeholders: `{http.vars.variable_name}` + +```json +{ + "handler": "vars", + "variable_name": "value" +} +``` + + +#### http/servers/errors + +Specifies how to handle errors returned from the HTTP handlers. + +If a [handler](#httpservershandle) chain returns an error, the error along with its recommended status code are bubbled back to the HTTP server which executes a separate error route, specified using this property. The error routes work exactly like the normal routes. + +```json +{ + "routes": [] +} +``` +- `routes`: The routes to execute on error. Works just like normal HTTP routes. + +In an error route, extra placeholders are available: + +``` +{http.error.status_code} + The recommended HTTP status code +{http.error.status_text} + The status text associated with the recommended status code +{http.error.message} + The error message +{http.error.trace} + The origin of the error +{http.error.id} + A short, human-conveyable ID for the error +``` + + +#### http/servers/tls_connection_policies + +An ordered list of policies which specify how to complete TLS handshakes when establishing an HTTPS connection. The first matching policy will be used. + +Most users will not need this; it is an advanced configuration feature. + +```json +{ + "match": {}, + "alpn": [], + "cipher_suites": [], + "curves": [], + "alpn": [], + "protocol_min": [], + "protocol_max": [], + "certificate_selection": {}, + "client_authentication": { + "trusted_ca_certs": ["..."], + "trusted_leaf_certs": ["..."] + } +} +``` + +- `match`: Configures how to match this policy with a TLS ClientHello. If the policy matches, it will be used. +- `alpn`: The ALPN value(s) to set. +- `cipher_suites`: The list of cipher suites to support. +- `curves`: The list of elliptic curves to support. +- `alpn`: Protocols to use for Application-Layer Protocol Negotiation (ALPN) during the handshake. +- `protocol_min`: Minimum TLS protocol version to allow. Default is `tls1.2`. +- `protocol_max`: Maximum TLS protocol version to allow. Default is `tls1.3`. +- `certificate_selection`: Certificate selection module, which configures how to choose a certificate if more than one match the given ServerName (SNI) value. +- `client_authentication`: Enables and configures TLS client authentication. +- `client_authentication.trusted_ca_certs`: A list of base64 DER-encoded CA certificates against which to validate client certificates. Client certs which are not signed by any of these CAs will be rejected. +- `client_authentication.trusted_leaf_certs`: A list of base64 DER-encoded client leaf certs to accept. If this list is not empty, client certs which are not in this list will be rejected. + +##### tls.certificate_selection.enterprise + +๐Ÿข _Enterprise_ + +Provides advanced certificate selection capabilities, when multiple certificates may satisfy a ClientHello's SNI value. + +All fields which are specified will be used to match certificates in the cache. If any specified field does not match the certificate, the certificate will not be chosen to complete the handshake. + +```json +{ + "policy": "enterprise", + "serial_number": "", + "subject_organization": "", + "public_key_algorithm": "", + "tag": "" +} +``` + +- `serial_number`: The certificate's serial number. +- `subject_organization`: The X.509 Subject.Organization field. +- `public_key_algorithm`: The kind of public key the certificate has. +- `tag`: A tag added to the certificate when it was loaded into the cache. Tags may be added in the load_files or load_pem certificate loaders. + + +#### http/servers/automatic_https + +Configures automatic HTTPS for this server. Automatic HTTPS consists of: + +- Provisioning certificates +- Renewing certificates +- Configuring TLS listeners +- Redirecting HTTP to HTTPS + +Generally you will not need to change these settings; they are for advanced use. + +```json +{ + "disable": false, + "disable_redirects": false, + "skip": [], + "skip_certificates": [] +} +``` + +- `disable`: If true, automatic HTTPS will be completely disabled. +- `disable_redirects`: If true, automatic HTTP->HTTPS redirects will be disabled, but automated certificate management will still be enabled. +- `skip`: A list of hosts (domain names) to not include in automatic HTTPS. +- `skip_certificates`: A list of hosts (domain names) to still enable automatic HTTPS for, except for managing certificates. + + +### tls + +Caddy's TLS app is an immensely powerful way to configure your server's security and privacy policies with regards to network connections. It enables you to load TLS certificates into the cache so they can be used to complete TLS handshakes. You can customize how certificates are managed or automated, and you can even configure how TLS session tickets are handled. + +Most users will not need to configure the TLS app at all, since HTTPS is automatic and on by default. + +The app is structured like this: + +```json +{ + "certificates": {}, + "automation": {}, + "session_tickets": {} +} +``` + +- `certificates`: Caches certificates in memory for quick use during TLS handshakes. Each key is the name of a certificate loader module. +- `automation`: Configures certificate automation. +- `session_tickets`: Configures session ticket ephemeral keys (STEKs) + + +#### tls/certificates + +Configures how certificates are loaded into memory. + +All loaded certificates get pooled into the same cache and may be used to complete TLS handshakes for the relevant server names (SNI). Certificates loaded manually (anything other than "automate") are not automatically managed and will have to be refreshed manually before they expire. + +There are several loader modules available. + +##### tls.certificates.load_files + +Loads certificates and their keys from files. Is a list of objects which specify how to load each cert+key pair. + +```json +"load_files": [ + { + "certificate": "", + "key": "", + "format": "pem", + "tags": [] + } +] +``` + +- `certificate`: The certificate file. +- `key`: The private key file. +- `format`: The format of the certificate and key. Values: pem +- `tags`: Optionally associate this certificate with tags ( as strings) to keep track of them. Useful for advanced certificate selection. + + +##### tls.certificates.load_folders + +Load all certificates and keys that can be found in the specified folders. Certificates and key pairs should be bundled in the same `.pem` files. This is a quick way to load a bunch of certificates at once. + +```json +"load_folders": ["/folder1", "folder2"] +``` + +- Simply specify the folder paths in the array. + +##### tls.certificates.automate + +A special case, this instructs the TLS app to automate certificates for the specified host/domain names. Certificates will be automated according to their matching automation policy. + +```json +"automate": ["example.com", "foo.example.com"] +``` + +- Specify the names to automate certificates for in the array. + + +##### tls.certificates.pem_loader + +๐Ÿข _Enterprise_ + +Loads certificate and key pairs directly as presented in the config, without needing to access disk. This allows you to securely transmit private keys without having to persist them to storage; you can keep them entirely in memory. + +```json +[ + { + "certificate": "", + "key": "", + "tags": [] + } +] +``` +- `certificate`: The PEM encoding of the certificate. +- `key`: The PEM encoding of the private key. +- `tags`: Optionally associate this certificate with tags ( as strings) to keep track of them. Useful for advanced certificate selection. + + +#### tls/automation + +Configures TLS certificate automation. + +```json +{ + "policies": [], + "on_demand": { + "rate_limit": { + "interval": "", + "burst": 0 + }, + "ask": "" + }, + "renew_interval": "12h", + "ocsp_interval": "1h" +} +``` + +- `policies`: An ordered list of automation policies. +- `on_demand`: The configuration On-Demand TLS, when needed. On-Demand TLS defers certificate operations to the time they are needed, e.g. during a TLS handshake. Because it is possible to abuse this feature, usage controls are configurable. +- `on_demand.rate_limit`: Configures a rate limit for getting certificates with On-Demand TLS. +- `on_demand.rate_limit.interval`: A duration value. A certificate may be obtained `burst` times during this interval. +- `on_demand.rate_limit.burst`: How many times during an interval a certificate can be obtained. +- `on_demand.ask`: A URL which will be queried to check if Caddy should be allowed to try to get a certificate for a hostname. The name will be passed in a query string parameter like so: `?domain=example.com`. The endpoint must return a 200 OK if a certificate is allowed; anything else will cause it to be denied. Redirects are not followed. +- `renew_interval`: How often to scan loaded certificates for renewal. Default is 12 hours. Should be at least an order of magnitude shorter than certificate lifetimes. +- `ocsp_interval`: How often to scan active OCSP staples for staleness. Default is 1 hour. Should be at least an order of magnitude less than the lifetime of an OCSP response. + + +#### tls/automation/policies + +Automation policies are a very powerful way to describe how Caddy should manage certificates for certain names. + +The first matching policy will be used. Policies are matched by hostname (aka "domain name"). + +```json +{ + "hosts": [], + "management": {} +} +``` + +- `hosts`: The list of host names for which this policy should be applied. Omitting this field matches all hostnames. An empty list matches none. +- `management`: The module to use for management. + +##### tls.management.acme + +This module uses ACME to manage TLS certificates. + +```json +{ + "module": "acme", + "ca": "https://acme-staging-v02.api.letsencrypt.org/directory", + "email": "", + "renew_ahead": "30d", + "key_type": "", + "acme_timeout": "", + "must_staple": false, + "challenges": { + "http": { + "disabled": false, + "alternate_port": 0 + }, + "tls-alpn": { + "disabled": false, + "alternate_port": 0 + }, + "dns": {} + }, + "on_demand": false, + "storage": {}, + "trusted_roots_pem_files": [] +} +``` +- `ca`: The ACME CA's directory endpoint. +- `email`: Your email address, so the CA can contact you if necessary. Not required but strongly recommended to provide one so you can be reached if there is a problem. +- `renew_ahead`: How long before a certificate's expiration to try renewing it. Default is 30 days. Should usually be about 1/3 of certificate lifetime, but long enough to give yourself time to troubleshoot problems before expiration. +- `key_type`: The type of key to generate for the certificate. Supported values: `rsa2048`, `rsa4096`, `p256`, `p384` +- `acme_timeout`: Duration to wait before timing out an ACME operation. +- `must_staple`: If true, the certificate will have MustStaple set. +- `challenges`: Configures the various ACME challenge types. +- `challenges.http`: Configures the ACME HTTP challenge type. +- `challenges.http.disabled`: Disables the HTTP challenge. +- `challenges.http.alternate_port`: Use this port for the HTTP challenge. Since the ACME spec requires port 80, you must use port forwarding to this alternate port. +- `challenges.tls-alpn`: Configures the ACME TLS-ALPN challenge type. +- `challenges.tls-alpn.disabled`: Disables the TLS-ALPN challenge. +- `challenges.tls-alpn.alternate_port`: Use this port for the TLS-ALPN challenge. Since the ACME spec requires port 443, you must use port forwarding to this alternate port. +- `challenges.dns`: Configures the ACME DNS challenge. Doing so disables the other challenge types. This challenge type must be configured using a DNS challenge module. +- `on_demand`: If true, certificates will be managed "on demand", that is, during TLS handshakes or when needed, as opposed to at startup. +- `storage`: Optionally configure a separate storage module associated with this manager, instead of using Caddy's global/default-configured storage. +- `trusted_roots_pem_files`: An array of CA certificates to accept when connecting to the ACME CA. + + + +#### tls/session_tickets + +Configures Caddy's TLS session tickets. By default, Caddy generates several keys and rotates them automatically on a regular basis, preserving forward secrecy in TLS 1.2. (Caddy is the only web server to do this by default.) You can customize this behavior. + +```json +{ + "disabled": false, + "max_keys": 4, + "key_source": {}, + "disable_rotation": false, + "rotation_interval": "12h" +} +``` + +- `disabled`: Disables session ticket keys. +- `max_keys`: How many keys to keep in memory. +- `key_source`: A STEK provider module that produces keys. +- `disable_rotation`: Disables STEK rotation. This is discouraged. +- `rotation_interval`: How often to rotate STEKs. + +##### tls.stek.standard + +Caddy's standard STEK provider module. It generates cyrptographically-secure keys in memory. There is nothing to configure, and it does not need to be specified in a config. + + +##### tls.stek.distributed + +๐Ÿข _Enterprise_ + +A distributed STEK provider module. This allows Caddy to share STEKs among other instances in its cluster (those which use the same storage configuration), increasing performance and reducing connection latency when behind a load balancer. + +```json +{ + "provider": "distributed", + "storage": {} +} +``` + +- `storage`: The storage module to use. All instances in the cluster should use the same storage configuration.