diff --git a/.editorconfig b/.editorconfig index 5fdace9..4308623 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,9 @@ +root = true + [*] end_of_line = lf insert_final_newline = true [*.{md,css,js,html}] indent_style = tab -indent_size = 4 \ No newline at end of file +indent_size = 4 diff --git a/README.md b/README.md index 8b85e6e..c174a55 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,13 @@ Your first time, you may be prompted for a password. This is so Caddy can serve You can then load [https://localhost](https://localhost) (or whatever address you configured) in your browser. +### Docker + +You can run rootless with docker with +``` +docker stop caddy-website || true && docker rm caddy-website || true +docker run --name caddy-website -it -p 8443:443 -v ./:/wd caddy sh -c "cd /wd && caddy run" +``` + +This will allow you to connect to https://localhost:8443 diff --git a/src/docs/markdown/api.md b/src/docs/markdown/api.md index f54037b..5774401 100644 --- a/src/docs/markdown/api.md +++ b/src/docs/markdown/api.md @@ -258,13 +258,13 @@ Caddy's config API provides [ACID guarantees -Sometimes the terms *module*, *plugin*, and *extension* get used interchangably, and usually that's OK. Technically, all modules are plugins, but not all plugins are modules. Modules are specifically a kind of plugin that extends Caddy's [config structure](/docs/json/). +Sometimes the terms *module*, *plugin*, and *extension* get used interchangeably, and usually that's OK. Technically, all modules are plugins, but not all plugins are modules. Modules are specifically a kind of plugin that extends Caddy's [config structure](/docs/json/). diff --git a/src/docs/markdown/automatic-https.md b/src/docs/markdown/automatic-https.md index 6f180e6..d8a7878 100644 --- a/src/docs/markdown/automatic-https.md +++ b/src/docs/markdown/automatic-https.md @@ -30,6 +30,7 @@ Here's a 28-second video showing how it works: - [Errors](#errors) - [Storage](#storage) - [Wildcard certificates](#wildcard-certificates) +- [Encrypted ClientHello (ECH)](#encrypted-clienthello-ech) @@ -116,7 +117,7 @@ All hostnames (domain names) qualify for fully-managed certificates if they: In addition, hostnames qualify for publicly-trusted certificates if they: -- are not localhost (including `.localhost`, `.local` and `.home.arpa` TLDs) +- 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 @@ -196,7 +197,7 @@ The DNS challenge performs an authoritative DNS lookup for the candidate hostnam 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. 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. +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) @@ -272,10 +273,173 @@ Before attempting any ACME transactions, Caddy will test the configured storage ## 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`. +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"). -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. +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_. + +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_ 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 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 Crtificate 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. \ No newline at end of file diff --git a/src/docs/markdown/build.md b/src/docs/markdown/build.md index 5bd222f..04d1072 100644 --- a/src/docs/markdown/build.md +++ b/src/docs/markdown/build.md @@ -102,7 +102,7 @@ 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 Caddy with the provided modules, similar to the process [outlined above](#xcaddy). +The builder uses `xcaddy` to build Caddy with the provided modules, similar to the process [outlined above](#xcaddy). To use Docker Compose, see our recommended [`compose.yml`](/docs/running#docker-compose) and usage instructions. diff --git a/src/docs/markdown/caddyfile-tutorial.md b/src/docs/markdown/caddyfile-tutorial.md index ec82491..1c2f76b 100644 --- a/src/docs/markdown/caddyfile-tutorial.md +++ b/src/docs/markdown/caddyfile-tutorial.md @@ -134,16 +134,11 @@ It's good practice to compress responses with a quick and modern compression alg ```caddy localhost -encode zstd gzip +encode templates file_server browse ``` - - - That's the basic process for getting a semi-advanced, production-ready site up and running! @@ -161,7 +156,7 @@ Our Caddyfile so far: ```caddy localhost -encode zstd gzip +encode templates file_server browse ``` @@ -170,7 +165,7 @@ is equivalent to this one: ```caddy localhost { - encode zstd gzip + encode templates file_server browse } @@ -209,9 +204,9 @@ You can then define as many different sites as you want, as long as each address ## 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 static file, or the reverse proxy will proxy the request to a backend. +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: +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 @@ -225,15 +220,15 @@ In practice, we may want to use the reverse proxy only for API requests, i.e. re ```caddy localhost -file_server 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/*` token 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). +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 name 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! +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! diff --git a/src/docs/markdown/caddyfile.md b/src/docs/markdown/caddyfile.md index 26f6550..fb6f7c2 100644 --- a/src/docs/markdown/caddyfile.md +++ b/src/docs/markdown/caddyfile.md @@ -11,7 +11,7 @@ It looks like this: ```caddy example.com { root * /var/www/wordpress - encode gzip + encode php_fastcgi unix//run/php/php-version-fpm.sock file_server } diff --git a/src/docs/markdown/caddyfile/concepts.md b/src/docs/markdown/caddyfile/concepts.md index e3fbf92..418957c 100644 --- a/src/docs/markdown/caddyfile/concepts.md +++ b/src/docs/markdown/caddyfile/concepts.md @@ -254,7 +254,7 @@ Wildcards (`*`) may be used, but only to represent precisely one label of the ho 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, either with spaces or commas. The following three examples are equivalent: +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 @@ -320,45 +320,55 @@ Matcher tokens can be omitted entirely to match all requests; for example, `*` d ## Placeholders -You can use any [Caddy placeholders](/docs/conventions#placeholders) in the Caddyfile, but for convenience you can also use some equivalent shorthand ones: +[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. -| Shorthand | Replaces | -|-----------------|-----------------------------------| -| `{cookie.*}` | `{http.request.cookie.*}` | -| `{client_ip}` | `{http.vars.client_ip}` | -| `{dir}` | `{http.request.uri.path.dir}` | -| `{err.*}` | `{http.error.*}` | -| `{file_match.*}` | `{http.matchers.file.*}` | -| `{file.base}` | `{http.request.uri.path.file.base}` | -| `{file.ext}` | `{http.request.uri.path.file.ext}` | -| `{file}` | `{http.request.uri.path.file}` | -| `{header.*}` | `{http.request.header.*}` | -| `{host}` | `{http.request.host}` | -| `{hostport}` | `{http.request.hostport}` | -| `{labels.*}` | `{http.request.host.labels.*}` | -| `{method}` | `{http.request.method}` | -| `{path.*}` | `{http.request.uri.path.*}` | -| `{path}` | `{http.request.uri.path}` | -| `{port}` | `{http.request.port}` | -| `{query.*}` | `{http.request.uri.query.*}` | -| `{query}` | `{http.request.uri.query}` | -| `{re.*}` | `{http.regexp.*}` | -| `{remote_host}` | `{http.request.remote.host}` | -| `{remote_port}` | `{http.request.remote.port}` | -| `{remote}` | `{http.request.remote}` | -| `{rp.*}` | `{http.reverse_proxy.*}` | -| `{scheme}` | `{http.request.scheme}` | -| `{tls_cipher}` | `{http.request.tls.cipher_suite}` | +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}` | +| `{tls_version}` | `{http.request.tls.version}` | | `{upstream_hostport}` | `{http.reverse_proxy.upstream.hostport}` | -| `{uri}` | `{http.request.uri}` | -| `{vars.*}` | `{http.vars.*}` | +| `{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. + @@ -413,6 +423,29 @@ b.example.com { } ``` +⚠️ 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.** @@ -469,7 +502,7 @@ If your configuration relies on environment variables, you can use them in the C Environment variables in this form are substituted **before Caddyfile parsing begins**, so they can expand to empty values (i.e. `""`), partial tokens, complete tokens, or even multiple tokens and lines. -For example, a environement variable `UPSTREAMS="app1:8080 app2:8080 app3:8080"` would expand to multiple [tokens](#tokens-and-quotes): +For example, an environment variable `UPSTREAMS="app1:8080 app2:8080 app3:8080"` would expand to multiple [tokens](#tokens-and-quotes): ```caddy example.com { diff --git a/src/docs/markdown/caddyfile/directives.md b/src/docs/markdown/caddyfile/directives.md index 3533610..72f43b7 100644 --- a/src/docs/markdown/caddyfile/directives.md +++ b/src/docs/markdown/caddyfile/directives.md @@ -55,10 +55,12 @@ Directive | Description **[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 @@ -126,6 +128,7 @@ fs root log_append log_skip +log_name header copy_response_headers # only in reverse_proxy's handle_response block @@ -145,6 +148,7 @@ forward_auth request_header encode push +intercept templates # special routing & dispatching directives @@ -192,6 +196,6 @@ For ease of use, the Caddyfile adapter sorts directives according to the followi - 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 eachother, so the most specific matcher should be evaluated 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. diff --git a/src/docs/markdown/caddyfile/directives/acme_server.md b/src/docs/markdown/caddyfile/directives/acme_server.md index a074ab8..e461133 100644 --- a/src/docs/markdown/caddyfile/directives/acme_server.md +++ b/src/docs/markdown/caddyfile/directives/acme_server.md @@ -38,7 +38,7 @@ acme_server [] { - **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 hot is not an IP address, the addresses are resolved using the [name resolution convention](https://golang.org/pkg/net/#hdr-Name_Resolution) of the Go standard library. If multiple resolvers are specified, then one is chosen at random. +- **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. diff --git a/src/docs/markdown/caddyfile/directives/encode.md b/src/docs/markdown/caddyfile/directives/encode.md index 410bd86..f63506c 100644 --- a/src/docs/markdown/caddyfile/directives/encode.md +++ b/src/docs/markdown/caddyfile/directives/encode.md @@ -6,6 +6,12 @@ title: encode (Caddyfile directive) window.$(function() { // We'll add links to all the subdirectives if a matching anchor tag is found on the page. addLinksToSubdirectives(); + + // Response matchers + window.$('pre.chroma .k:contains("status")') + .html('status') + window.$('pre.chroma .k:contains("header")') + .html('header') }); @@ -16,16 +22,13 @@ Encodes responses using the configured encoding(s). A typical use for encoding i ## Syntax ```caddy-d -encode [] { +encode [] [] { # encoding formats gzip [] - zstd + zstd [] minimum_length - # response matcher single line syntax - match [header []] | [status ] - # or response matcher block for multiple conditions match { status header [] @@ -33,15 +36,15 @@ encode [] { } ``` -- **<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. +- **<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 the specified level. +- **gzip** enables Gzip compression, optionally at a specified level. -- **zstd** enables Zstandard compression. +- **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](#response-matcher). Only matching responses are encoded. The default looks like this: +- **match** is a [response matcher](/docs/caddyfile/response-matchers). Only matching responses are encoded. The default looks like this: ```caddy-d match { @@ -82,27 +85,6 @@ encode [] { ``` -## Response matcher - -**Response matchers** can be used to filter (or classify) responses by specific criteria. - - -### status - -```caddy-d -status -``` - -By HTTP status code. - -- **<code...>** is a list of HTTP status codes. Special cases are `2xx`, `3xx`, ... which match against all status codes in the range of 200-299, 300-399, ... respectively - - -### header - -See the [header](/docs/caddyfile/matchers#header) request matcher for the supported syntax. - - ## Examples Enable Gzip compression: @@ -117,12 +99,18 @@ Enable Zstandard and Gzip compression (with Zstandard implicitly preferred, sinc 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 zstd gzip + encode file_server } ``` diff --git a/src/docs/markdown/caddyfile/directives/file_server.md b/src/docs/markdown/caddyfile/directives/file_server.md index 6434e3e..68aaa1a 100644 --- a/src/docs/markdown/caddyfile/directives/file_server.md +++ b/src/docs/markdown/caddyfile/directives/file_server.md @@ -24,6 +24,8 @@ Most often, the `file_server` directive is paired with the [`root`](root) direct 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 @@ -36,10 +38,11 @@ file_server [] [browse] { browse [] { reveal_symlinks } - precompressed + precompressed [] status disable_canonical_uris pass_thru + sort [] } ``` @@ -55,11 +58,13 @@ file_server [] [browse] { - **browse** enables file listings for requests to directories that do not have an index file. - - **** is an optional custom template file to use for directory listings. Defaults to the template that can be extracted using the command `caddy file-server export-template`, which will print the defaut template to stdout. The embedded template can also be found [here in the source code ![external link](/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. + - **** 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. -- **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`). + - **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. @@ -69,6 +74,7 @@ file_server [] [browse] { - **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: @@ -113,10 +119,10 @@ file_server { } ``` -If supported by the client (`Accept-Encoding` header) checks the existence of precompressed files along side the requested file. So if `/path/to/file` is requested, it checks for `/path/to/file.zst`, `/path/to/file.br` and `/path/to/file.gz` in that order and serves the first available file with corresponding Content-Encoding: +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 zstd br gzip + precompressed } ``` diff --git a/src/docs/markdown/caddyfile/directives/forward_auth.md b/src/docs/markdown/caddyfile/directives/forward_auth.md index 2b07f53..203b72d 100644 --- a/src/docs/markdown/caddyfile/directives/forward_auth.md +++ b/src/docs/markdown/caddyfile/directives/forward_auth.md @@ -35,7 +35,7 @@ An opinionated directive which proxies a clone of the request to an authenticati - [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 usecase. This directive is actually just a convenient way to use a longer, more common configuration (below). +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. @@ -94,11 +94,9 @@ reverse_proxy { # On a successful response, copy response headers @good status 2xx handle_response @good { - request_header { - # for example, for each copy_headers field... - Remote-User {rp.header.Remote-User} - Remote-Email {rp.header.Remote-Email} - } + # for example, for each copy_headers field... + request_header Remote-User {rp.header.Remote-User} + request_header Remote-Email {rp.header.Remote-Email} } } ``` @@ -120,7 +118,7 @@ auth.example.com { # Serve your app app1.example.com { forward_auth authelia:9091 { - uri /api/verify?rd=https://auth.example.com + uri /api/authz/forward-auth copy_headers Remote-User Remote-Groups Remote-Name Remote-Email } diff --git a/src/docs/markdown/caddyfile/directives/header.md b/src/docs/markdown/caddyfile/directives/header.md index 76ee347..0ea0942 100644 --- a/src/docs/markdown/caddyfile/directives/header.md +++ b/src/docs/markdown/caddyfile/directives/header.md @@ -56,13 +56,13 @@ header [] [[+|-|?|>] [|] []] { - **<find>** is the substring or regular expression to search for. -- **<replace>** is the replacement value; required if performing a search-and-replace. +- **<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** will force the header operations to be deferred until the response is being written out to the client. This is automatically enabled if any of the header fields are being deleted with `-`, when setting a default value with `?`, or when having used the `>` prefix. For multiple header manipulations, you can open a block and specify one manipulation per line in the same way. -When using the `?` prefix to set a default header value, it is recommended to separate this into its own `header` directive. [Under the hood](/docs/modules/http.handlers.headers#response/require), using `?` configures a response matcher which applies to the directive's entire handler, which only applies the header operations if the field is not yet set. For example, in the same directive, you have these two manipulations: `-Hidden` and `?Foo default`, then the `Hidden` header is _only_ removed if `Foo` is empty, which is typically not the intended effect. +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 diff --git a/src/docs/markdown/caddyfile/directives/import.md b/src/docs/markdown/caddyfile/directives/import.md index bc7a248..c0e8e62 100644 --- a/src/docs/markdown/caddyfile/directives/import.md +++ b/src/docs/markdown/caddyfile/directives/import.md @@ -11,7 +11,7 @@ This directive is a special case: it is evaluated before the structure is parsed ## Syntax ```caddy-d -import [] +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. @@ -34,6 +34,11 @@ import [] 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 @@ -85,3 +90,76 @@ 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" + } +} +``` diff --git a/src/docs/markdown/caddyfile/directives/intercept.md b/src/docs/markdown/caddyfile/directives/intercept.md new file mode 100644 index 0000000..f472bfc --- /dev/null +++ b/src/docs/markdown/caddyfile/directives/intercept.md @@ -0,0 +1,93 @@ +--- +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 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. + +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 +} +``` diff --git a/src/docs/markdown/caddyfile/directives/log.md b/src/docs/markdown/caddyfile/directives/log.md index 59e89de..234f65a 100644 --- a/src/docs/markdown/caddyfile/directives/log.md +++ b/src/docs/markdown/caddyfile/directives/log.md @@ -59,7 +59,7 @@ To add custom fields to the log entries, use the [`log_append` directive](log_ap - [append](#append) - [Examples](#examples) -Since Caddy v2.5, by default, headers with potentially sensitive information (`Cookie`, `Set-Cookie`, `Authorization` and `Proxy-Authorization`) will be logged with empty values. This behaviour can be disabled with the [`log_credentials`](/docs/caddyfile/options#log-credentials) global server option. +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 @@ -67,29 +67,34 @@ Since Caddy v2.5, by default, headers with potentially sensitive information (`C ```caddy-d log [] { hostnames + no_hostname output ... format ... level } ``` -- **logger_name** is an optional override of the logger name for this site. +- **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. +- **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. -- **output** configures where to write the logs. See [`output` modules](#output-modules) 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. +- **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`. +- **level** is the minimum entry level to log. Default: `INFO`. Note that access logs currently only emit `INFO` and `ERROR` level logs. @@ -150,9 +155,7 @@ output file { - **<filename>** is the path to the log file. -- **mode** is the file mode octal value as specified with the unix `chmod` command. For example `0600` would set the mode to `rw-,---,---`, while `0640` would set the mode to `rw-,r--,---`. - - Default: `0600` +- **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. @@ -174,7 +177,6 @@ output file { - **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. @@ -263,8 +265,9 @@ format { Default: `seconds`. May be one of: - - `seconds` Floating-point number of seconds elapsed. - - `nano` Integer number of nanoseconds elapsed. + - `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. diff --git a/src/docs/markdown/caddyfile/directives/log_append.md b/src/docs/markdown/caddyfile/directives/log_append.md index a996894..d7a3738 100644 --- a/src/docs/markdown/caddyfile/directives/log_append.md +++ b/src/docs/markdown/caddyfile/directives/log_append.md @@ -37,3 +37,21 @@ example.com { } } ``` + +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} + } +} +``` diff --git a/src/docs/markdown/caddyfile/directives/log_name.md b/src/docs/markdown/caddyfile/directives/log_name.md new file mode 100644 index 0000000..c1dce38 --- /dev/null +++ b/src/docs/markdown/caddyfile/directives/log_name.md @@ -0,0 +1,49 @@ +--- +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" + } +} +``` diff --git a/src/docs/markdown/caddyfile/directives/php_fastcgi.md b/src/docs/markdown/caddyfile/directives/php_fastcgi.md index 42897e1..2946c34 100644 --- a/src/docs/markdown/caddyfile/directives/php_fastcgi.md +++ b/src/docs/markdown/caddyfile/directives/php_fastcgi.md @@ -78,15 +78,18 @@ The `php_fastcgi` directive (without subdirectives) is the same as the following ```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 + # 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} @@ -105,7 +108,9 @@ route { - 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 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 a 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. + 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. @@ -113,7 +118,7 @@ route { - 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 rewrite to `index.php` if that file exists (it almost always should 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. + - 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. diff --git a/src/docs/markdown/caddyfile/directives/push.md b/src/docs/markdown/caddyfile/directives/push.md index 9a8b9df..358b2b2 100644 --- a/src/docs/markdown/caddyfile/directives/push.md +++ b/src/docs/markdown/caddyfile/directives/push.md @@ -40,7 +40,7 @@ push [] [] { } ``` -- **<resource>** is the target URI path to push. If used within the block, may optionally be preceeded by the method (GET or POST; GET is default). +- **<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). diff --git a/src/docs/markdown/caddyfile/directives/request_body.md b/src/docs/markdown/caddyfile/directives/request_body.md index 3dfbf3e..e25cd26 100644 --- a/src/docs/markdown/caddyfile/directives/request_body.md +++ b/src/docs/markdown/caddyfile/directives/request_body.md @@ -6,17 +6,19 @@ title: request_body (Caddyfile directive) 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 @@ -30,3 +32,21 @@ example.com { 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 + } + } +} +``` diff --git a/src/docs/markdown/caddyfile/directives/reverse_proxy.md b/src/docs/markdown/caddyfile/directives/reverse_proxy.md index 1fdc564..9d96b2b 100644 --- a/src/docs/markdown/caddyfile/directives/reverse_proxy.md +++ b/src/docs/markdown/caddyfile/directives/reverse_proxy.md @@ -11,13 +11,18 @@ window.$(function() { let text = item.innerText.replace(//g,'>'); let url = '#' + item.innerText.replace(/_/g, "-"); window.$(item).addClass('nd').removeClass('k') - window.$(item).html(`${text}`); + window.$(item).html(`${text}`); }); // Fix matcher placeholder - window.$('pre.chroma .k:contains("handle_response")').first().nextAll().slice(0, 3) + window.$('pre.chroma .nd:contains("@name")').first() + .html('@name') + window.$('pre.chroma .k:contains("replace_status")').first().next().slice(0, 3) .wrapAll('').parent() - .html('[<matcher>]') + .html('[<matcher>]') + window.$('pre.chroma .k:contains("handle_response")').first().next().slice(0, 3) + .wrapAll('').parent() + .html('[<matcher>]') // We'll add links to all the subdirectives if a matching anchor tag is found on the page. addLinksToSubdirectives(); @@ -66,12 +71,18 @@ reverse_proxy [] [] { lb_retry_match # active health checking - health_uri - health_port - health_interval - health_timeout - health_status - health_body + 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 { [] } @@ -125,19 +136,20 @@ reverse_proxy [] [] { -### Upstreams +## 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 +### 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` @@ -145,6 +157,7 @@ Static upstream addresses can take the form of a URL that contains only scheme a - `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. @@ -157,6 +170,8 @@ By default, connections are made to the upstream over plaintext HTTP. When using 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. @@ -164,12 +179,12 @@ Upstream addresses **cannot** contain paths or query strings, as that would impl 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 +### 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 +#### SRV Retrieves upstreams from SRV DNS records. @@ -196,7 +211,7 @@ Retrieves upstreams from SRV DNS records. -##### A/AAAA +#### A/AAAA Retrieves upstreams from A/AAAA DNS records. @@ -221,7 +236,7 @@ Retrieves upstreams from A/AAAA DNS records. - **versions** is the list of IP versions to resolve for. Default: `ipv4 ipv6` which correspond to both A and AAAA records respectively. -##### Multi +#### 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. @@ -236,7 +251,7 @@ Append the results of multiple dynamic upstream modules. Useful if you want redu -### Load balancing +## 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). @@ -263,7 +278,7 @@ This is enabled by default, with the `random` policy. Retries are disabled by de - `round_robin` iterates each upstream in turn - - `weighted_round_robin ` iterates each upstream in turn, respecting the weights provided. The amount of weight arguments should match the amount of upstreams configured. Weights should be non-zero positive integers. For example with two upstreams and weights `5 1`, the first upstream would be selected 5 times in a row before the second upstream is selected once, then the cycle repeats. + - `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 @@ -298,41 +313,53 @@ This is enabled by default, with the `random` policy. Retries are disabled by de - **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 usecase. + 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 succeded but the subsequent round-trip failed. If the connection to the upstream failed, a retry is always allowed. By default, only `GET` requests are retried. +- **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 Active health checks perform health checking in the background on a timer. To enable this, `health_uri` or `health_port` are required. - **health_uri** is the URI path (and optional query) for active health checks. -- **health_port** is the port to use for active health checks, if different from the upstream's port. +- **health_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 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 usecase. +- **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`. @@ -345,7 +372,7 @@ Passive health checks happen inline with actual proxied requests. To enable this 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 +## 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: @@ -356,7 +383,7 @@ In both cases, the `host` is included as metadata in the event to identify the u -### Streaming +## Streaming By default, the proxy partially buffers the response for wire efficiency. @@ -383,7 +410,7 @@ By default, WebSocket connections are forcibly closed (with a Close control mess -### Headers +## Headers The proxy can **manipulate headers** between itself and the backend: @@ -431,9 +458,9 @@ 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 +### Defaults -By default, Caddy passes thru incoming headers—including `Host`—to the backend without modifications, with three exceptions: +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. @@ -454,7 +481,7 @@ If you're using Cloudflare in front of Caddy, be aware that you may be vulnerabl 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 +### 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: @@ -466,9 +493,11 @@ reverse_proxy https://example.com { 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 + +## 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`. @@ -486,14 +515,14 @@ For example, the request could be sent to an authentication gateway to decide wh -### Transports +## Transports Caddy's proxy **transport** is pluggable: - **transport** defines how to communicate with the backend. Default is `http`. -#### The `http` transport +### The `http` transport ```caddy-d transport http { @@ -522,6 +551,7 @@ transport http { versions compression off max_conns_per_host + forward_proxy_url } ``` @@ -580,15 +610,26 @@ transport http { - **keepalive_idle_conns_per_host** if non-zero, controls the maximum idle (keep-alive) connections to keep per-host. Default: `32`. -- **versions** allows customizing which versions of HTTP to support. As a special case, "h2c" is a valid value which will enable cleartext HTTP/2 connections to the upstream (however, this is a non-standard feature that does not use Go's default HTTP transport, so it is exclusive of other features; subject to change or removal). Default: `1.1 2`, or if scheme is `h2c://`, `h2c 2` +- **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. +- **forward_proxy_url** specifies the URL of a server that the HTTP transport will use to proxy requests to the upstream server. By default, Caddy respects proxy configured via environment variables as per the [Go stdlib](https://pkg.go.dev/golang.org/x/net/http/httpproxy#FromEnvironment) like `HTTP_PROXY`. When a value is provided for this parameter, requests will flow through the reverse proxy in the following order: + - Client (users) 🡒 `reverse_proxy` 🡒 `forward_proxy_url` 🡒 upstream -#### The `fastcgi` transport + +### The `fastcgi` transport ```caddy-d transport fastcgi { @@ -627,46 +668,33 @@ If you're trying to serve a modern PHP application, you may be looking for the [ -### Intercepting responses +## Intercepting responses -The reverse proxy can be configured to intercept responses from the backend. To facilitate this, response matchers can be defined (similar to the syntax for request matchers) and the first matching `handle_response` route will be invoked. +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](#response-matcher). As long as each response matcher has a unique name, multiple matchers can be defined. A response can be matched on the status code and presence or value of a response header. +- **@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. - -#### Response matcher - -**Response matchers** can be used to filter (or classify) responses by specific criteria. - -##### 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. - -##### header - -See the [`header`](/docs/caddyfile/matchers#header) request matcher for the supported syntax. - +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). @@ -752,13 +780,13 @@ example.com { ``` -Instead you may establish trust with the upstream by explicitly [trusting the upstream's certificate](#tls_trusted_ca_certs), and (optionally) setting TLS-SNI to match the hostname in the upstream's certificate: +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_trusted_ca_certs /path/to/cert.pem + tls_trust_pool file /path/to/cert.pem tls_server_name app.example.com } } @@ -845,3 +873,16 @@ 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} + } + } +} +``` diff --git a/src/docs/markdown/caddyfile/directives/templates.md b/src/docs/markdown/caddyfile/directives/templates.md index c640829..c0f60fe 100644 --- a/src/docs/markdown/caddyfile/directives/templates.md +++ b/src/docs/markdown/caddyfile/directives/templates.md @@ -54,6 +54,6 @@ To serve a simple static response using a template, make sure to set `Content-Ty example.com { header Content-Type text/plain templates - respond "Current year is: {{printf "{{"}}now | date "2006"{{printf "}}"}}" + respond `Current year is: {{printf "{{"}}now | date "2006"{{printf "}}"}}` } ``` diff --git a/src/docs/markdown/caddyfile/directives/tls.md b/src/docs/markdown/caddyfile/directives/tls.md index c1c122d..a64dab3 100644 --- a/src/docs/markdown/caddyfile/directives/tls.md +++ b/src/docs/markdown/caddyfile/directives/tls.md @@ -24,7 +24,7 @@ Compatibility note: Due to its sensitive nature as a security protocol, delibera tls [internal|] | [ ] { protocols [] ciphers - curves + curves alpn load ca @@ -42,8 +42,6 @@ tls [internal|] | [ ] { client_auth { mode [request|require|verify_if_given|require_and_verify] trust_pool - trusted_leaf_cert - trusted_leaf_cert_file verifier } issuer [] @@ -84,7 +82,8 @@ Keep in mind that Let's Encrypt may send you emails about your certificate neari - `TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA` - `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA` -- **curves** specifies the list of EC curves to support. It is recommended to not change these. Supported values are: +- **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` @@ -140,16 +139,12 @@ Keep in mind that Let's Encrypt may send you emails about your certificate neari | verify_if_given | Ask clients for a certificate; allow even if there isn't one, but verify it if there is | | require_and_verify | Require clients to present a valid certificate that is verified | - Default: `require_and_verify` if any `trusted_ca_cert` or `trusted_leaf_cert` are provided; otherwise, `require`. + 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). - - **trusted_leaf_cert** is a base64 DER-encoded client leaf certificate to accept. - - - **trusted_leaf_cert_file** is a path to a PEM CA certificate file against which to validate client certificates. - Multiple `trusted_*` directives may be used to specify multiple CA or leaf certificates. Client certificates which are not listed as one of the leaf certificates or signed by any of the specified CAs will be rejected according to the **mode**. - **verifier** enables the use of a custom client certificate verifier module. These can perform custom client authentication checks, such as ensuring the certificate is not revoked. @@ -291,7 +286,7 @@ Obtains certificates using the ACME protocol. Note that `acme` is a default issu alt_tlsalpn_port eab trusted_roots - dns [] + dns [ []] propagation_timeout propagation_delay dns_ttl @@ -328,7 +323,7 @@ Obtains certificates using the ACME protocol. Note that `acme` is a default issu - **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. +- **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. @@ -362,19 +357,32 @@ Obtains certificates using the ACME protocol. Note that `acme` is a default issu #### zerossl -Obtains certificates using the ACME protocol, specifically with ZeroSSL. Note that `zerossl` is a default issuer, so configuring it explicitly is usually unnecessary. +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 [] { - ... +... zerossl { + validity_days + alt_http_port + dns ... + propagation_delay + propagation_timeout + resolvers + dns_ttl } ``` -The syntax for `zerossl` is exactly the same as for [`acme`](#acme), except that its name is `zerossl` and it can optionally take your ZeroSSL API key. +- **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. -Its functionality is also the same, except that it will use ZeroSSL's directory by default and it can automatically negotiate EAB credentials (whereas with the `acme` issuer, you have to manually provide EAB credentials and set the directory endpoint). -When explicitly configuring `zerossl`, configuring an `email` is required so that your certificates can appear in your ZeroSSL dashboard. #### internal @@ -394,7 +402,7 @@ Obtains certificates from an internal certificate authority. 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 interally issued leaf certificates. Default: `12h`. It is NOT recommended to change this, unless absolutely necessary. It must be shorter than the intermediate's lifetime. +- **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). @@ -501,15 +509,13 @@ https:// { } ``` -Enable TLS Client Authentication and require clients to present a valid certificate that is verified against all the provided CA's via `trusted_ca_cert_file` +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 { - mode require_and_verify - trusted_ca_cert_file ../caddy.ca.cer - trusted_ca_cert_file ../root.ca.cer + trust_pool file ../caddy.ca.cer ../root.ca.cer } } } diff --git a/src/docs/markdown/caddyfile/directives/tracing.md b/src/docs/markdown/caddyfile/directives/tracing.md index 881f279..c7b6a2f 100644 --- a/src/docs/markdown/caddyfile/directives/tracing.md +++ b/src/docs/markdown/caddyfile/directives/tracing.md @@ -10,7 +10,9 @@ When enabled, it will propagate an existing trace context or initialize a new on 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 is added to [access logs](/docs/caddyfile/directives/log) as the standard `traceID` field. +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 @@ -24,6 +26,8 @@ tracing { [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 @@ -41,6 +45,8 @@ 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: @@ -51,6 +57,7 @@ example.com { tracing { span api } + request_header X-Trace-Id {http.vars.trace_id} reverse_proxy localhost:8081 } diff --git a/src/docs/markdown/caddyfile/directives/try_files.md b/src/docs/markdown/caddyfile/directives/try_files.md index 5512622..39df0f8 100644 --- a/src/docs/markdown/caddyfile/directives/try_files.md +++ b/src/docs/markdown/caddyfile/directives/try_files.md @@ -11,7 +11,7 @@ Rewrites the request URI path to the first of the listed files which exists in t ```caddy-d try_files { - policy first_exist|smallest_size|largest_size|most_recently_modified + policy first_exist|first_exist_fallback|smallest_size|largest_size|most_recently_modified } ``` diff --git a/src/docs/markdown/caddyfile/directives/uri.md b/src/docs/markdown/caddyfile/directives/uri.md index 5acb2c7..7287f27 100644 --- a/src/docs/markdown/caddyfile/directives/uri.md +++ b/src/docs/markdown/caddyfile/directives/uri.md @@ -18,23 +18,56 @@ uri [] strip_prefix uri [] strip_suffix uri [] replace [] uri [] path_regexp +uri [] query [-|+] [] +uri [] query { + [] [] + ... +} ``` -- The first (non-matcher) argument specifies the operation: +The first (non-matcher) argument specifies the operation: - - **strip_prefix** strips the prefix from the path. +- **strip_prefix** strips the prefix from the path. - - **strip_suffix** strips the suffix from the path. +- **strip_suffix** strips the suffix from the path. - - **replace** performs a substring replacement across the whole URI. +- **replace** performs a substring replacement across the whole URI. - - **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. -- **<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. -- **<replacement>** is the replacement string (only valid with `replace` and `path_regexp`). Supports using capture groups with `$name` or `${name}` syntax, or with a number for the index, such as `$1`. See the [Go documentation](https://golang.org/pkg/regexp/#Regexp.Expand) for details. + - **<limit>** is an optional limit to the maximum number of replacements. -- **<limit>** is an optional limit to the maximum number of replacements (only valid with `replace`). +- **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`. @@ -74,3 +107,44 @@ Collapse all repeated slashes in the request path (but not the request query) to ```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 +} +``` diff --git a/src/docs/markdown/caddyfile/directives/vars.md b/src/docs/markdown/caddyfile/directives/vars.md index 2497c2c..e002138 100644 --- a/src/docs/markdown/caddyfile/directives/vars.md +++ b/src/docs/markdown/caddyfile/directives/vars.md @@ -6,9 +6,11 @@ title: vars (Caddyfile directive) 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"}}` +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. -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. +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 @@ -24,7 +26,7 @@ vars [] [ ] { - **<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, you may wrap the output with [quotes](/docs/caddyfile/concepts#tokens-and-quotes) and they will stay strings. + 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 diff --git a/src/docs/markdown/caddyfile/matchers.md b/src/docs/markdown/caddyfile/matchers.md index 344822e..53e62b4 100644 --- a/src/docs/markdown/caddyfile/matchers.md +++ b/src/docs/markdown/caddyfile/matchers.md @@ -146,7 +146,7 @@ Then you can use the matcher like so, by specifying it as the first argument to directive @name ``` -For example, this proxies websocket requests to `localhost:6001`, and other requests to `localhost:8080`. It matches requests that have a header field named `Connection` _containing_ `Upgrade`, **and** another field named `Upgrade` with exactly `websocket`: +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 { @@ -300,7 +300,7 @@ respond @api "Hello, API!" file { root try_files - try_policy first_exist|smallest_size|largest_size|most_recently_modified + try_policy first_exist|first_exist_fallback|smallest_size|largest_size|most_recently_modified split_path } file @@ -308,7 +308,7 @@ file expression `file({ 'root': '', 'try_files': [''], - 'try_policy': 'first_exist|smallest_size|largest_size|most_recently_modified', + 'try_policy': 'first_exist|first_exist_fallback|smallest_size|largest_size|most_recently_modified', 'split_path': [''] })` expression file('') @@ -330,6 +330,8 @@ By files. - `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. @@ -397,7 +399,7 @@ Some more examples using [CEL expressions](#expression). Keep in mind that place ### header ```caddy-d -header [] +header [ ...] expression header({'': ''}) ``` @@ -406,7 +408,7 @@ 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. +- `` 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). @@ -439,10 +441,10 @@ Match requests that do not have the `Foo` header field at all: @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`: +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'})` +@websockets `header({'Connection':'*Upgrade*','Upgrade':'websocket'}) || header({':protocol': 'websocket'})` ``` @@ -460,7 +462,7 @@ 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 is used in the same named matcher. +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: @@ -471,7 +473,7 @@ Capture groups can be accessed via [placeholder](/docs/caddyfile/concepts#placeh 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. Multiple different fields will be AND'ed. +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: @@ -705,7 +707,7 @@ Like [`path`](#path), but supports regular expressions. Runs against the URI-dec 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 is used in the same named matcher. +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: @@ -716,7 +718,7 @@ Capture groups can be accessed via [placeholder](/docs/caddyfile/concepts#placeh 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. +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: @@ -774,12 +776,13 @@ With a [CEL expression](#expression): ```caddy-d query =... +query "" expression query({'': ''}) expression query({'': ['']}) ``` -By query string parameters. Should be a sequence of `key=value` pairs. Keys are matched exactly (case-sensitively) but also support `*` to match any value. Values can use placeholders. +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. @@ -874,6 +877,12 @@ Match an output of the [`map` directive](/docs/caddyfile/directives/map) named ` 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 +``` + --- @@ -887,7 +896,7 @@ 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 is used in the same named matcher. +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: @@ -898,7 +907,7 @@ Capture groups can be accessed via [placeholder](/docs/caddyfile/concepts#placeh 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 `vars_regexp` matcher per named matcher. +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: diff --git a/src/docs/markdown/caddyfile/options.md b/src/docs/markdown/caddyfile/options.md index cf58874..d208eba 100644 --- a/src/docs/markdown/caddyfile/options.md +++ b/src/docs/markdown/caddyfile/options.md @@ -76,6 +76,9 @@ Possible options are (click on each option to jump to its documentation): } grace_period shutdown_delay + metrics { + per_host + } # TLS Options auto_https off|disable_redirects|ignore_loaded_certs|disable_certs @@ -91,14 +94,18 @@ Possible options are (click on each option to jump to its documentation): mac_key } acme_dns ... + dns ... + ech { + dns ... + } on_demand_tls { - ask - interval - burst + ask + permission } key_type ed25519|p256|p384|rsa2048|rsa4096 cert_issuer ... renew_interval + cert_lifetime ocsp_interval ocsp_stapling off preferred_chains [smallest] { @@ -121,7 +128,7 @@ Possible options are (click on each option to jump to its documentation): keepalive_interval trusted_proxies ... client_ip_headers - metrics + trace max_header_size enable_full_duplex log_credentials @@ -328,7 +335,7 @@ The name can be passed to indicate a specific logger for which to customize the Multiple loggers with different names can be configured by using the `log` multiple times. -The 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. +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. @@ -548,6 +555,49 @@ The tokens following the name of the provider set up the provider the same as if ``` +##### `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. @@ -563,7 +613,9 @@ The ask endpoint should return _as fast as possible_, in a few milliseconds, ide -- **interval** and **burst** allows `` certificate operations within `` interval. These are deprecated and will be removed in a future version, due to not working as intended. +- **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 { @@ -623,6 +675,22 @@ Default: `10m` ``` +##### `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. @@ -768,9 +836,13 @@ http:// { 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. -There is a special no-op [`tls`](/docs/json/apps/http/servers/listener_wrappers/tls/) listener wrapper provided as a standard module which marks where TLS should be handled in the chain of listener wrappers. It should only be used if another listener wrapper must be placed in front of the TLS handshake. This _does not_ enable TLS for a server; e.g. if this is used on your `:80` HTTP server, it will still act as a no-op. +###### `tls` -The included [`http_redirect`](/docs/json/apps/http/servers/listener_wrappers/http_redirect/) listener wrapper can look at the first few bytes of an incoming request to determine if it's likely HTTP (instead of TLS), and trigger an HTTP-to-HTTPS redirect on the same port but using the `https://` scheme. 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. Don't use this on an HTTP server. It must be placed _before_ the `tls` listener wrapper. For example: +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 { @@ -783,7 +855,34 @@ The included [`http_redirect`](/docs/json/apps/http/servers/listener_wrappers/ht } ``` -Also included is 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), which 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: +###### `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 { @@ -792,6 +891,8 @@ Also included is the [`proxy_protocol`](/docs/json/apps/http/servers/listener_wr 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 } @@ -911,10 +1012,34 @@ Pairing with [`trusted_proxies`](#trusted-proxies), allows configuring which hea 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 { - metrics + trace } } ``` @@ -956,7 +1081,7 @@ Test thoroughly with your HTTP clients, as some older clients may not support fu ##### `log_credentials` -Since Caddy v2.5, by default, headers with potentially sensitive information (`Cookie`, `Set-Cookie`, `Authorization` and `Proxy-Authorization`) will be logged with empty values in access logs (see the [`log` directive](/docs/caddyfile/directives/log)). +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. diff --git a/src/docs/markdown/caddyfile/patterns.md b/src/docs/markdown/caddyfile/patterns.md index adcba9c..770dd12 100644 --- a/src/docs/markdown/caddyfile/patterns.md +++ b/src/docs/markdown/caddyfile/patterns.md @@ -66,7 +66,7 @@ With a PHP FastCGI service running, something like this works for most modern PH ```caddy example.com { root * /srv/public - encode gzip + encode php_fastcgi localhost:9000 file_server } @@ -217,7 +217,7 @@ A typical SPA config usually looks something like this: ```caddy example.com { root * /srv - encode gzip + encode try_files {path} /index.html file_server } @@ -227,7 +227,7 @@ If your SPA is coupled with an API or other server-side-only endpoints, you will ```caddy example.com { - encode gzip + encode handle /api/* { reverse_proxy backend:8000 @@ -241,6 +241,15 @@ example.com { } ``` +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 diff --git a/src/docs/markdown/caddyfile/response-matchers.md b/src/docs/markdown/caddyfile/response-matchers.md new file mode 100644 index 0000000..be9e11f --- /dev/null +++ b/src/docs/markdown/caddyfile/response-matchers.md @@ -0,0 +1,108 @@ +--- +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 + +```caddy-d +@name { + status + 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 +``` diff --git a/src/docs/markdown/command-line.md b/src/docs/markdown/command-line.md index 90196d1..e61a1a3 100644 --- a/src/docs/markdown/command-line.md +++ b/src/docs/markdown/command-line.md @@ -38,7 +38,7 @@ The `--flags` may have a single-letter shortcut like `-f`. A simple but production-ready file server - **[caddy file-server export-template](#caddy-file-server-export-template)** - Auxilary command for the file server to export the default file browser template + Auxiliary command for the file server to export the default file browser template - **[caddy fmt](#caddy-fmt)** Formats a Caddyfile @@ -319,7 +319,7 @@ 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. +`--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. Note that only TCP addresses are supported at this time. @@ -455,7 +455,7 @@ 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" 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. +`--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. @@ -654,7 +654,7 @@ Validates a configuration file, then exits. This command deserializes the config `--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, if the config file is not in Caddy's native JSON format. If the config file starts with `Caddyfile`, the `caddyfile` adapter is used by default. +`--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. diff --git a/src/docs/markdown/conventions.md b/src/docs/markdown/conventions.md index 2b4b932..9a62741 100644 --- a/src/docs/markdown/conventions.md +++ b/src/docs/markdown/conventions.md @@ -38,11 +38,14 @@ 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. @@ -59,6 +62,8 @@ 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 ``` @@ -74,24 +79,25 @@ Caddy network addresses are not URLs. URLs couple the lower and higher layers of ## Placeholders -Caddy's configuration supports the use of _placeholders_ (variables). Using placeholders is a simple way to inject dynamic values into a static configuration. +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 variable name inside, for example: `{foo.bar}`. Placeholder braces can be escaped, `\{like so\}`. Variable names are typically namespaced with dots to avoid collisions across modules. +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: +The following placeholders are always available (global): Placeholder | Description ------------|------------- -`{env.*}` | Environment variable (example: `{env.HOME}`) +`{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 @@ -104,7 +110,7 @@ Placeholder | Description `{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. +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. diff --git a/src/docs/markdown/extending-caddy.md b/src/docs/markdown/extending-caddy.md index 54b849c..13c2d77 100644 --- a/src/docs/markdown/extending-caddy.md +++ b/src/docs/markdown/extending-caddy.md @@ -15,7 +15,7 @@ Caddy is easy to extend because of its modular architecture. Most kinds of Caddy ## Quick Start -A Caddy module is any named type that registers itself as a Caddy module when its package is imported. Crucially, a module always implements the [caddy.Module](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Module) interface, which provides its name and a constructor function. +A Caddy module is any named type that registers itself as a Caddy module when its package is imported. Crucially, a module always implements the [`caddy.Module`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Module) interface, which provides its name and a constructor function. In a new Go module, paste the following template into a Go file and customize your package name, type name, and Caddy module ID: @@ -108,7 +108,7 @@ The name within a namespace is significant and highly visible to users, but is n ## App Modules -Apps are modules with an empty namespace, and which conventionally become their own top-level namespace. App modules implement the [caddy.App](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#App) interface. +Apps are modules with an empty namespace, and which conventionally become their own top-level namespace. App modules implement the [`caddy.App`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#App) interface. These modules appear in the [`"apps"`](/docs/json/#apps) property of the top-level of Caddy's config: @@ -138,7 +138,7 @@ type Gizmo struct { } ``` -Using struct tags in this way will ensure that config properties are consisently named across all of Caddy. +Using the `omitempty` option in the struct tag will omit the field from the JSON output if it is the zero value for its type. This is useful to keep the JSON config clean and concise when marshaled (e.g. adapting from Caddyfile to JSON). When a module is initialized, it will already have its configuration filled out. It is also possible to perform additional [provisioning](#provisioning) and [validation](#validating) steps after a module is initialized. @@ -149,18 +149,18 @@ A module's life begins when it is loaded by a host module. The following happens 1. [`New()`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#ModuleInfo.New) is called to get an instance of the module's value. 2. The module's configuration is unmarshaled into that instance. -3. If the module is a [caddy.Provisioner](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Provisioner), the `Provision()` method is called. -4. If the module is a [caddy.Validator](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Validator), the `Validate()` method is called. +3. If the module is a [`caddy.Provisioner`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Provisioner), the `Provision()` method is called. +4. If the module is a [`caddy.Validator`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Validator), the `Validate()` method is called. 5. At this point, the host module is given the loaded guest module as an `interface{}` value, so the host module will usually type-assert the guest module into a more useful type. Check the documentation for the host module to know what is required of a guest module in its namespace, e.g. what methods need to be implemented. -6. When a module is no longer needed, and if it is a [caddy.CleanerUpper](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#CleanerUpper), the `Cleanup()` method is called. +6. When a module is no longer needed, and if it is a [`caddy.CleanerUpper`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#CleanerUpper), the `Cleanup()` method is called. -Note that multiple loaded instances of your module may overlap at a given time! During config changes, new modules are started before the old ones are stopped. Be sure to use global state carefully. Use the [caddy.UsagePool](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#UsagePool) type to help manage global state across module loads. If your module listens on a socket, use `caddy.Listen*()` to get a socket that supports overlapping usage. +Note that multiple loaded instances of your module may overlap at a given time! During config changes, new modules are started before the old ones are stopped. Be sure to use global state carefully. Use the [`caddy.UsagePool`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#UsagePool) type to help manage global state across module loads. If your module listens on a socket, use `caddy.Listen*()` to get a socket that supports overlapping usage. ### Provisioning -A module's configuration will be unmarshaled into its value automatically. This means, for example, that struct fields will be filled out for you. +A module's configuration will be unmarshaled into its value automatically (when loading the JSON config). This means, for example, that struct fields will be filled out for you. -However, if your module requires additional provisioning steps, you can implement the (optional) [caddy.Provisioner](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Provisioner) interface: +However, if your module requires additional provisioning steps, you can implement the (optional) [`caddy.Provisioner`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Provisioner) interface: ```go // Provision sets up the module. @@ -170,7 +170,9 @@ func (g *Gizmo) Provision(ctx caddy.Context) error { } ``` -This is typically where host modules will load their guest/child modules, but it can be used for pretty much anything. Module provisioning is done in an arbitrary order. +This is where you should set default values for fields that were not provided by the user (fields that are not their zero value). If a field is required, you may return an error if it is not set. For numeric fields where the zero value has meaning (e.g. some timeout duration), you may want to support `-1` to mean "off" rather than `0`, so you may set a default value if the user did not configure it. + +This is also typically where host modules will load their guest/child modules. A module may access other apps by calling `ctx.App()`, but modules must not have circular dependencies. In other words, a module loaded by the `http` app cannot depend on the `tls` app if a module loaded by the `tls` app depends on the `http` app. (Very similar to rules forbidding import cycles in Go.) diff --git a/src/docs/markdown/extending-caddy/caddyfile.md b/src/docs/markdown/extending-caddy/caddyfile.md index 1e84606..68d204e 100644 --- a/src/docs/markdown/extending-caddy/caddyfile.md +++ b/src/docs/markdown/extending-caddy/caddyfile.md @@ -15,7 +15,7 @@ An unmarshaler's job is simply to set up your module's type, e.g. by populating ```go // UnmarshalCaddyfile implements caddyfile.Unmarshaler. Syntax: // -// gizmo [