mirror of
https://github.com/caddyserver/website.git
synced 2025-06-14 10:14:49 -04:00
Merge branch 'master' into logging
This commit is contained in:
commit
3e510970af
61 changed files with 1453 additions and 402 deletions
|
@ -1,7 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{md,css,js,html}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
indent_size = 4
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -258,13 +258,13 @@ Caddy's config API provides [ACID guarantees <img src="/old/resources/images/ext
|
|||
|
||||
For example, two clients may `GET /config/foo` at the same time, make an edit within that scope (config path), then call `POST|PUT|PATCH|DELETE /config/foo/...` at the same time to apply their changes, resulting in a collision: either one will overwrite the other, or the second might leave the config in an unintended state since it was applied to a different version of the config than it was prepared against. This is because the changes are not aware of each other.
|
||||
|
||||
Caddy's API does not support transactions spanning multiple requests, and HTTP is a stateless protocol. However, you can use the `Etag` trailer and `If-Match` header to detect and prevent collisions for any and all changes as a kind of optimistic concurrency control. This is useful if there is any chance that you are using Caddy's `/config/...` endpoints concurrently without synchronization. All responses to `GET /config/...` requests have an HTTP trailer called `Etag` that contains the path and a hash of the contents in that scope (e.g. `Etag: "/config/apps/http/servers 65760b8e"`). Simply set the `If-Match` header on a mutative request to that of an Etag trailer from a previous `GET` request.
|
||||
Caddy's API does not support transactions spanning multiple requests, and HTTP is a stateless protocol. However, you can use the `Etag` and `If-Match` headers to detect and prevent collisions for any and all changes as a kind of optimistic concurrency control. This is useful if there is any chance that you are using Caddy's `/config/...` endpoints concurrently without synchronization. All responses to `GET /config/...` requests have an HTTP header called `Etag` that contains the path and a hash of the contents in that scope (e.g. `Etag: "/config/apps/http/servers 65760b8e"`). Simply set the `If-Match` header on a mutative request to that of an Etag header from a previous `GET` request.
|
||||
|
||||
The basic algorithm for this is as follows:
|
||||
|
||||
1. Perform a `GET` request to any scope `S` within the config. Hold onto the `Etag` trailer of the response.
|
||||
1. Perform a `GET` request to any scope `S` within the config. Hold onto the `Etag` header of the response.
|
||||
2. Make your desired change on the returned config.
|
||||
3. Perform a `POST|PUT|PATCH|DELETE` request within scope `S`, setting the `If-Match` header to the recent `Etag` value.
|
||||
3. Perform a `POST|PUT|PATCH|DELETE` request within scope `S`, setting the `If-Match` request header to the stored `Etag` value.
|
||||
4. If the response is HTTP 412 (Precondition Failed), repeat from step 1, or give up after too many attempts.
|
||||
|
||||
This algorithm safely allows multiple, overlapping changes to Caddy's configuration without explicit synchronization. It is designed so that simultaneous changes to different parts of the config don't require a retry: only changes that overlap the same scope of the config can possibly cause a collision and thus require a retry.
|
||||
|
|
|
@ -33,7 +33,7 @@ The **[core library](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc)
|
|||
|
||||
<aside class="tip">
|
||||
|
||||
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/).
|
||||
|
||||
</aside>
|
||||
|
||||
|
|
|
@ -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 <img src="/old/resources/images/external-link.svg" class="external-link">](https://letsencrypt.org/docs/challenge-types/) the [DNS challenge](#dns-challenge) to obtain wildcard certificates.
|
||||
|
||||
|
||||
## Encrypted ClientHello (ECH)
|
||||
|
||||
Normally, TLS handshakes involve sending the ClientHello, including the Server Name Indicator (SNI; the domain being connected to), in plaintext. That's because it contains the parameters necessary for encrypting the connection that comes after the handshake. This, of course, exposes the domain name, which is the most sensitive part of the ClientHello, to anyone who can eavesdrop connections, even if they are not in your immediate physical vicinity. It reveals which service you are connecting to when the destination IP may serve many different sites, and it's how some governments censor the Internet.
|
||||
|
||||
With Encrypted ClientHello, the client can protect the domain name by wrapping the true ClientHello in an "outer" ClientHello that establishes parameters for decrypting the "inner" ClientHello. However, many moving parts need to come together perfectly for this to work and deliver actual privacy benefits.
|
||||
|
||||
First, the client needs to know what parameters, or configuration, to use to encrypt the ClientHello. This information includes a public key and "outer" domain (the "public name"), among other things. This configuration has to be published or distributed somehow in a reliable fashion.
|
||||
|
||||
You could theoretically write it down on a piece of paper and hand it out to everybody, but most major browsers support looking up HTTPS-type DNS records containing ECH parameters when connecting to a site. Hence, you will need to: (1) generate an ECH configuration (public/private key pair, among other parameters), and then (2) create an HTTPS-type DNS record containing the base64-encoded ECH configuration.
|
||||
|
||||
Or... you could let Caddy do that all for you. Caddy is the first and only web server that can automatically generate, publish, and serve ECH configurations.
|
||||
|
||||
Once the HTTPS record is published, clients will need to perform a DNS lookup for the HTTPS record when connecting to your site. Normally, DNS lookups are in plaintext, which compromises the security of the resulting ECH handshakes, so browsers will need to use a secure DNS protocol like DNS-over-HTTPS (DoH) or DNS-over-TLS (DoT). Depending on the browser, this may need to be manually enabled.
|
||||
|
||||
Once the client has securely downloaded the ECH config, it uses the embedded public key to encrypt the ClientHello, and proceeds to connect to your site. Caddy then decrypts the inner ClientHello and proceeds to serve your site, without the domain name ever appearing in plaintext over the wire.
|
||||
|
||||
### Deployment considerations
|
||||
|
||||
ECH is a nuanced technology. Even though Caddy completely automates ECH, many things need to be considered in order for maximum privacy benefits. You should also be aware of various trade-offs.
|
||||
|
||||
#### Publication
|
||||
|
||||
Caddy will only create an HTTPS record for a domain if there is already a record for that domain. This prevents breaking DNS lookups for a subdomain that may be covered by a wildcard. Ensure that your sites have at least an A/AAAA record pointing to your server. If you only use a wildcard for DNS records, then the wildcard domain will need to appear in your Caddy config as well.
|
||||
|
||||
Caddy will not publish an HTTPS record for a domain that has a CNAME record.
|
||||
|
||||
#### ECH GREASE
|
||||
|
||||
If you open Wireshark and then connect to any site (even one that does not support ECH) in a modern version of a major browser like Firefox or Chrome (even with ECH disabled), you may notice its handshake includes the `encrypted_client_hello` extension:
|
||||
|
||||

|
||||
|
||||
The purpose of this is to make true ECH handshakes indistinguishable from plaintext ones. If ECH handshakes looked different than normal ones, censors could just block ECH handshakes with minimal fallout/collateral damage. But if they blocked any handshake with a plausible ECH extension, they would essentially turn off most of the Internet. (The goal is to increase the cost of widespread censorship.)
|
||||
|
||||
This is mainly important to know when troubleshooting connections.
|
||||
|
||||
#### Key rotation
|
||||
|
||||
Like certificate keys, it is not good practice (and can be downright insecure) to use the same key for a long time. As such, ECH keys should be rotated on a regular basis. Unlike certificates, ECH configs don't strictly expire. But servers should rotate them nonetheless.
|
||||
|
||||
<aside class="tip">
|
||||
|
||||
(NOTE: Caddy 2.10 does not rotate keys because it is not possible to change keys on a running server with Go 1.24. However, it [should be possible by Go 1.25](https://github.com/golang/go/issues/71920), so at that point, Caddy will be updated to rotate keys.)
|
||||
|
||||
</aside>
|
||||
|
||||
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 <provider config...>
|
||||
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 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.
|
|
@ -102,7 +102,7 @@ Make sure to replace `<version>` 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.
|
||||
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
||||
<aside class="tip">
|
||||
Browsers don't support Zstandard encodings yet. Hopefully soon!
|
||||
</aside>
|
||||
|
||||
|
||||
<aside class="complete">Compression</aside>
|
||||
|
||||
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!
|
||||
|
||||
<aside class="complete">Matchers</aside>
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
```
|
||||
|
||||
⚠️ <i>Experimental</i> <span style='white-space: pre;'> | </span> <span>v2.9.x+</span>
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -38,7 +38,7 @@ acme_server [<matcher>] {
|
|||
|
||||
- **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.
|
||||
|
||||
|
|
|
@ -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('<a href="/docs/caddyfile/response-matchers#status" style="color: inherit;" title="Response matcher">status</a>')
|
||||
window.$('pre.chroma .k:contains("header")')
|
||||
.html('<a href="/docs/caddyfile/response-matchers#header" style="color: inherit;" title="Response matcher">header</a>')
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -16,16 +22,13 @@ Encodes responses using the configured encoding(s). A typical use for encoding i
|
|||
## Syntax
|
||||
|
||||
```caddy-d
|
||||
encode [<matcher>] <formats...> {
|
||||
encode [<matcher>] [<formats...>] {
|
||||
# encoding formats
|
||||
gzip [<level>]
|
||||
zstd
|
||||
zstd [<level>]
|
||||
|
||||
minimum_length <length>
|
||||
|
||||
# response matcher single line syntax
|
||||
match [header <field> [<value>]] | [status <code...>]
|
||||
# or response matcher block for multiple conditions
|
||||
match {
|
||||
status <code...>
|
||||
header <field> [<value>]
|
||||
|
@ -33,15 +36,15 @@ encode [<matcher>] <formats...> {
|
|||
}
|
||||
```
|
||||
|
||||
- **<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** <span id="gzip"/> enables Gzip compression, optionally at the specified level.
|
||||
- **gzip** <span id="gzip"/> enables Gzip compression, optionally at a specified level.
|
||||
|
||||
- **zstd** <span id="zstd"/> enables Zstandard compression.
|
||||
- **zstd** <span id="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** <span id="minimum_length"/> the minimum number of bytes a response should have to be encoded (default: 512).
|
||||
|
||||
- **match** <span id="match"/> is a [response matcher](#response-matcher). Only matching responses are encoded. The default looks like this:
|
||||
- **match** <span id="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 [<matcher>] <formats...> {
|
|||
```
|
||||
|
||||
|
||||
## Response matcher
|
||||
|
||||
**Response matchers** can be used to filter (or classify) responses by specific criteria.
|
||||
|
||||
|
||||
### status
|
||||
|
||||
```caddy-d
|
||||
status <code...>
|
||||
```
|
||||
|
||||
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
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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 [<matcher>] [browse] {
|
|||
browse [<template_file>] {
|
||||
reveal_symlinks
|
||||
}
|
||||
precompressed <formats...>
|
||||
precompressed [<formats...>]
|
||||
status <status>
|
||||
disable_canonical_uris
|
||||
pass_thru
|
||||
sort <sort_field> [<direction>]
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -55,11 +58,13 @@ file_server [<matcher>] [browse] {
|
|||
|
||||
- **browse** <span id="browse"/> enables file listings for requests to directories that do not have an index file.
|
||||
|
||||
- **<template_file>** <span id="template_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 ](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.
|
||||
- **<template_file>** <span id="template_file"/> is an optional custom template file to use for directory listings. Defaults to the template that can be extracted using the command `caddy file-server export-template`, which will print the default template to stdout. The embedded template can also be found [here in the source code ](https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/fileserver/browse.html). Browse templates can use actions from [the standard templates module](/docs/modules/http.handlers.templates#docs) as well.
|
||||
|
||||
- **reveal_symlinks** <span id="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** <span id="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** <span id="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** <span id="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 [<matcher>] [browse] {
|
|||
|
||||
- **pass_thru** <span id="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
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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 <upstreams...> {
|
|||
# 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
|
||||
}
|
||||
|
||||
|
|
|
@ -56,13 +56,13 @@ header [<matcher>] [[+|-|?|>]<field> [<value>|<find>] [<replace>]] {
|
|||
|
||||
- **<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
|
||||
|
|
|
@ -11,7 +11,7 @@ This directive is a special case: it is evaluated before the structure is parsed
|
|||
## Syntax
|
||||
|
||||
```caddy-d
|
||||
import <pattern> [<args...>]
|
||||
import <pattern> [<args...>] [{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 <pattern> [<args...>]
|
|||
|
||||
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.
|
||||
|
||||
⚠️ <i>Experimental</i> <span style='white-space: pre;'> | </span> <span>v2.9.x+</span>
|
||||
- **{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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
⚠️ <i>Experimental</i> <span style='white-space: pre;'> | </span> <span>v2.9.x+</span>
|
||||
|
||||
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 "<h1>hello world</h1>"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
93
src/docs/markdown/caddyfile/directives/intercept.md
Normal file
93
src/docs/markdown/caddyfile/directives/intercept.md
Normal file
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
title: intercept (Caddyfile directive)
|
||||
---
|
||||
|
||||
<script>
|
||||
window.$(function() {
|
||||
// Fix response matchers to render with the right color,
|
||||
// and link to response matchers section
|
||||
window.$('pre.chroma .k:contains("@")')
|
||||
.map(function(k, item) {
|
||||
let text = item.innerText.replace(/</g,'<').replace(/>/g,'>');
|
||||
let url = '#' + item.innerText.replace(/_/g, "-");
|
||||
window.$(item).addClass('nd').removeClass('k')
|
||||
window.$(item).html(`<a href="#response-matcher" style="color: inherit;" title="Response matcher">${text}</a>`);
|
||||
});
|
||||
|
||||
// Response matchers
|
||||
window.$('pre.chroma .nd:contains("@name")').first().slice(0, 3)
|
||||
.wrapAll('<span class="nd">').parent()
|
||||
.html('<a href="/docs/caddyfile/response-matchers" style="color: inherit;">@name</a>')
|
||||
window.$('pre.chroma .k:contains("replace_status")').first().next()
|
||||
.html('<a href="/docs/caddyfile/response-matchers" style="color: inherit;" title="Response matcher">[<matcher>]</a>')
|
||||
window.$('pre.chroma .k:contains("handle_response")').first().next()
|
||||
.html('<a href="/docs/caddyfile/response-matchers" style="color: inherit;" title="Response matcher">[<matcher>]</a>')
|
||||
window.$('pre.chroma .k')
|
||||
.filter((i, el) => el.innerText === 'status')
|
||||
.html('<a href="/docs/caddyfile/response-matchers#status" style="color: inherit;">status</a>')
|
||||
window.$('pre.chroma .k:contains("header")').first()
|
||||
.html('<a href="/docs/caddyfile/response-matchers#header" style="color: inherit;">header</a>')
|
||||
|
||||
// We'll add links to all the subdirectives if a matching anchor tag is found on the page.
|
||||
addLinksToSubdirectives();
|
||||
});
|
||||
</script>
|
||||
|
||||
# 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 [<matcher>] {
|
||||
@name {
|
||||
status <code...>
|
||||
header <field> [<value>]
|
||||
}
|
||||
|
||||
replace_status [<matcher>] <code>
|
||||
|
||||
handle_response [<matcher>] {
|
||||
<directives...>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **@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** <span id="replace_status"/> simply changes the status code of response when matched by the given matcher.
|
||||
|
||||
- **handle_response** <span id="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
|
||||
}
|
||||
```
|
|
@ -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 [<logger_name>] {
|
||||
hostnames <hostnames...>
|
||||
no_hostname
|
||||
output <writer_module> ...
|
||||
format <encoder_module> ...
|
||||
level <level>
|
||||
}
|
||||
```
|
||||
|
||||
- **logger_name** is an optional override of the logger name for this site.
|
||||
- **logger_name** <span id="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** <span id="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** <span id="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** <span id="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** <span id="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** <span id="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> {
|
|||
|
||||
- **<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 <img src="/old/resources/images/external-link.svg" class="external-link">](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 <filename> {
|
|||
|
||||
- **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 <encoder_module> {
|
|||
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.
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
49
src/docs/markdown/caddyfile/directives/log_name.md
Normal file
49
src/docs/markdown/caddyfile/directives/log_name.md
Normal file
|
@ -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 [<matcher>] <names...>
|
||||
```
|
||||
|
||||
|
||||
## 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"
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ push [<matcher>] [<resource>] {
|
|||
}
|
||||
```
|
||||
|
||||
- **<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).
|
||||
|
||||
|
||||
|
|
|
@ -6,17 +6,19 @@ title: request_body (Caddyfile directive)
|
|||
|
||||
Manipulates or sets restrictions on the bodies of incoming requests.
|
||||
|
||||
|
||||
## Syntax
|
||||
|
||||
```caddy-d
|
||||
request_body [<matcher>] {
|
||||
max_size <value>
|
||||
set <body_content>
|
||||
}
|
||||
```
|
||||
|
||||
- **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`.
|
||||
|
||||
⚠️ <i>Experimental</i> <span style='white-space: pre;'> | </span> <span>v2.10.0+</span>
|
||||
- **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
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -11,13 +11,18 @@ window.$(function() {
|
|||
let text = item.innerText.replace(/</g,'<').replace(/>/g,'>');
|
||||
let url = '#' + item.innerText.replace(/_/g, "-");
|
||||
window.$(item).addClass('nd').removeClass('k')
|
||||
window.$(item).html(`<a href="#response-matcher" style="color: inherit;" title="Response matcher">${text}</a>`);
|
||||
window.$(item).html(`<a href="/docs/caddyfile/response-matchers" style="color: inherit;" title="Response matcher">${text}</a>`);
|
||||
});
|
||||
|
||||
// Fix matcher placeholder
|
||||
window.$('pre.chroma .k:contains("handle_response")').first().nextAll().slice(0, 3)
|
||||
window.$('pre.chroma .nd:contains("@name")').first()
|
||||
.html('<a href="/docs/caddyfile/response-matchers" style="color: inherit;" title="Response matcher">@name</a>')
|
||||
window.$('pre.chroma .k:contains("replace_status")').first().next().slice(0, 3)
|
||||
.wrapAll('<span class="nd">').parent()
|
||||
.html('<a href="#response-matcher" style="color: inherit;" title="Response matcher">[<matcher>]</a>')
|
||||
.html('<a href="/docs/caddyfile/response-matchers" style="color: inherit;" title="Response matcher">[<matcher>]</a>')
|
||||
window.$('pre.chroma .k:contains("handle_response")').first().next().slice(0, 3)
|
||||
.wrapAll('<span class="nd">').parent()
|
||||
.html('<a href="/docs/caddyfile/response-matchers" style="color: inherit;" title="Response matcher">[<matcher>]</a>')
|
||||
|
||||
// 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 [<matcher>] [<upstreams...>] {
|
|||
lb_retry_match <request-matcher>
|
||||
|
||||
# active health checking
|
||||
health_uri <uri>
|
||||
health_port <port>
|
||||
health_interval <interval>
|
||||
health_timeout <duration>
|
||||
health_status <status>
|
||||
health_body <regexp>
|
||||
health_uri <uri>
|
||||
health_upstream <ip:port>
|
||||
health_port <port>
|
||||
health_interval <interval>
|
||||
health_passes <num>
|
||||
health_fails <num>
|
||||
health_timeout <duration>
|
||||
health_method <method>
|
||||
health_status <status>
|
||||
health_request_body <body>
|
||||
health_body <regexp>
|
||||
health_follow_redirects
|
||||
health_headers {
|
||||
<field> [<values...>]
|
||||
}
|
||||
|
@ -125,19 +136,20 @@ reverse_proxy [<matcher>] [<upstreams...>] {
|
|||
|
||||
|
||||
|
||||
### Upstreams
|
||||
## Upstreams
|
||||
|
||||
- **<upstreams...>** is a list of upstreams (backends) to which to proxy.
|
||||
- **to** <span id="to"/> is an alternate way to specify the list of upstreams, one (or more) per line.
|
||||
- **dynamic** <span id="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 <weights...>` 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 <weights...>` 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** <span id="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** <span id="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** <span id="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** <span id="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** <span id="health_uri"/> is the URI path (and optional query) for active health checks.
|
||||
|
||||
- **health_port** <span id="health_port"/> is the port to use for active health checks, if different from the upstream's port.
|
||||
- **health_upstream** <span id="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** <span id="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** <span id="health_interval"/> is a [duration value](/docs/conventions#durations) that defines how often to perform active health checks. Default: `30s`.
|
||||
|
||||
- **health_passes** <span id="health_passes"/> is the number of consecutive health checks required before marking the backend as healthy again. Default: `1`.
|
||||
|
||||
- **health_fails** <span id="health_fails"/> is the number of consecutive health checks required before marking the backend as unhealthy. Default: `1`.
|
||||
|
||||
- **health_timeout** <span id="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** <span id="health_method"/> is the HTTP method to use for the active health check. Default: `GET`.
|
||||
|
||||
- **health_status** <span id="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** <span id="health_request_body"/> is a string representing the request body to send with the active health check.
|
||||
|
||||
- **health_body** <span id="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** <span id="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** <span id="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** <span id="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** <span id="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** <span id="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** <span id="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 <versions...>
|
||||
compression off
|
||||
max_conns_per_host <count>
|
||||
forward_proxy_url <url>
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -580,15 +610,26 @@ transport http {
|
|||
|
||||
- **keepalive_idle_conns_per_host** <span id="keepalive_idle_conns_per_host"/> if non-zero, controls the maximum idle (keep-alive) connections to keep per-host. Default: `32`.
|
||||
|
||||
- **versions** <span id="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** <span id="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** <span id="compression"/> can be used to disable compression to the backend by setting it to `off`.
|
||||
|
||||
- **max_conns_per_host** <span id="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** <span id="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** <span id="replace_status"/> simply changes the status code of response when matched by the given matcher.
|
||||
|
||||
- **handle_response** <span id="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** <span id="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** <span id="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 <code...>
|
||||
```
|
||||
|
||||
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}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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 "}}"}}`
|
||||
}
|
||||
```
|
||||
|
|
|
@ -24,7 +24,7 @@ Compatibility note: Due to its sensitive nature as a security protocol, delibera
|
|||
tls [internal|<email>] | [<cert_file> <key_file>] {
|
||||
protocols <min> [<max>]
|
||||
ciphers <cipher_suites...>
|
||||
curves <curves...>
|
||||
curves <groups...>
|
||||
alpn <values...>
|
||||
load <paths...>
|
||||
ca <ca_dir_url>
|
||||
|
@ -42,8 +42,6 @@ tls [internal|<email>] | [<cert_file> <key_file>] {
|
|||
client_auth {
|
||||
mode [request|require|verify_if_given|require_and_verify]
|
||||
trust_pool <module>
|
||||
trusted_leaf_cert <base64_der>
|
||||
trusted_leaf_cert_file <filename>
|
||||
verifier <module>
|
||||
}
|
||||
issuer <issuer_name> [<params...>]
|
||||
|
@ -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** <span id="curves"/> specifies the list of EC curves to support. It is recommended to not change these. Supported values are:
|
||||
- **curves** <span id="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** <span id="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** <span id="trusted_leaf_cert"/> is a base64 DER-encoded client leaf certificate to accept.
|
||||
|
||||
- **trusted_leaf_cert_file** <span id="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** <span id="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 <port>
|
||||
eab <key_id> <mac_key>
|
||||
trusted_roots <pem_files...>
|
||||
dns <provider_name> [<options>]
|
||||
dns [<provider_name> [<options>]]
|
||||
propagation_timeout <duration>
|
||||
propagation_delay <duration>
|
||||
dns_ttl <duration>
|
||||
|
@ -328,7 +323,7 @@ Obtains certificates using the ACME protocol. Note that `acme` is a default issu
|
|||
|
||||
- **trusted_roots** <span id="trusted_roots"/> is one or more root certificates (as PEM filenames) to trust when connecting to the ACME CA server.
|
||||
|
||||
- **dns** <span id="dns"/> configures the DNS challenge.
|
||||
- **dns** <span id="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** <span id="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 [<api_key>] {
|
||||
...
|
||||
... zerossl <api_key> {
|
||||
validity_days <days>
|
||||
alt_http_port <port>
|
||||
dns <provider_name> ...
|
||||
propagation_delay <duration>
|
||||
propagation_timeout <duration>
|
||||
resolvers <list...>
|
||||
dns_ttl <duration>
|
||||
}
|
||||
```
|
||||
|
||||
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** <span id="validity_days"/> defines the certificate lifetime. Only certain values are accepted; see [ZeroSSL's docs](https://zerossl.com/documentation/api/create-certificate/) for details.
|
||||
<!--
|
||||
Default: `https://acme-v02.api.letsencrypt.org/directory`
|
||||
-->
|
||||
- **alt_http_port** <span id="zerossl_alt_http_port"/> is the port to use for completing ZeroSSL's HTTP validation, if not port 80.
|
||||
- **dns** <span id="zerossl_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` <img src="/old/resources/images/external-link.svg" class="external-link">](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** <span id="zerossl_propagation_delay"/> is how long to wait before checking for CNAME record propagation.
|
||||
- **propagation_timeout** <span id="zerossl_propagation_timeout"/> is how long to wait for CNAME record propagation before giving up.
|
||||
- **resolvers** <span id="zerossl_resolvers"/> defines custom DNS resolvers to use when checking for CNAME record propagation.
|
||||
- **dns_ttl** <span id="zerossl_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** <span id="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** <span id="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** <span id="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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ Rewrites the request URI path to the first of the listed files which exists in t
|
|||
|
||||
```caddy-d
|
||||
try_files <files...> {
|
||||
policy first_exist|smallest_size|largest_size|most_recently_modified
|
||||
policy first_exist|first_exist_fallback|smallest_size|largest_size|most_recently_modified
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -18,23 +18,56 @@ uri [<matcher>] strip_prefix <target>
|
|||
uri [<matcher>] strip_suffix <target>
|
||||
uri [<matcher>] replace <target> <replacement> [<limit>]
|
||||
uri [<matcher>] path_regexp <target> <replacement>
|
||||
uri [<matcher>] query [-|+]<param> [<value>]
|
||||
uri [<matcher>] query {
|
||||
<param> [<value>] [<replacement>]
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
- 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
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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 [<matcher>] [<name> <value>] {
|
|||
|
||||
- **<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
|
||||
|
||||
|
|
|
@ -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 <path>
|
||||
try_files <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 <delims...>
|
||||
}
|
||||
file <files...>
|
||||
|
@ -308,7 +308,7 @@ file <files...>
|
|||
expression `file({
|
||||
'root': '<path>',
|
||||
'try_files': ['<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': ['<delims...>']
|
||||
})`
|
||||
expression file('<files...>')
|
||||
|
@ -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 <field> [<value>]
|
||||
header <field> [<value> ...]
|
||||
|
||||
expression header({'<field>': '<value>'})
|
||||
```
|
||||
|
@ -406,7 +408,7 @@ By request header fields.
|
|||
|
||||
- `<field>` is the name of the HTTP header field to check.
|
||||
- If prefixed with `!`, the field must not exist to match (omit value arg).
|
||||
- `<value>` is the value the field must have to match.
|
||||
- `<value>` 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.<name>.<capture_group>}` 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.<name>.<capture_group>}` 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 <key>=<val>...
|
||||
query ""
|
||||
|
||||
expression query({'<key>': '<val>'})
|
||||
expression query({'<key>': ['<vals...>']})
|
||||
```
|
||||
|
||||
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.<name>.<capture_group>}` 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:
|
||||
|
||||
|
|
|
@ -76,6 +76,9 @@ Possible options are (click on each option to jump to its documentation):
|
|||
}
|
||||
grace_period <duration>
|
||||
shutdown_delay <duration>
|
||||
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 <mac_key>
|
||||
}
|
||||
acme_dns <provider> ...
|
||||
dns <provider> ...
|
||||
ech <public_names...> {
|
||||
dns <provider> ...
|
||||
}
|
||||
on_demand_tls {
|
||||
ask <endpoint>
|
||||
interval <duration>
|
||||
burst <n>
|
||||
ask <endpoint>
|
||||
permission <module>
|
||||
}
|
||||
key_type ed25519|p256|p384|rsa2048|rsa4096
|
||||
cert_issuer <name> ...
|
||||
renew_interval <duration>
|
||||
cert_lifetime <duration>
|
||||
ocsp_interval <duration>
|
||||
ocsp_stapling off
|
||||
preferred_chains [smallest] {
|
||||
|
@ -121,7 +128,7 @@ Possible options are (click on each option to jump to its documentation):
|
|||
keepalive_interval <duration>
|
||||
trusted_proxies <module> ...
|
||||
client_ip_headers <headers...>
|
||||
metrics
|
||||
trace
|
||||
max_header_size <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
|
|||
|
||||
</aside>
|
||||
|
||||
- **interval** and **burst** allows `<n>` certificate operations within `<duration>` 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 <img src="/old/resources/images/external-link.svg" class="external-link">](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 <duration>
|
||||
allow <cidrs...>
|
||||
deny <cidrs...>
|
||||
fallback_policy <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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
108
src/docs/markdown/caddyfile/response-matchers.md
Normal file
108
src/docs/markdown/caddyfile/response-matchers.md
Normal file
|
@ -0,0 +1,108 @@
|
|||
---
|
||||
title: Response matchers (Caddyfile)
|
||||
---
|
||||
|
||||
<script>
|
||||
window.$(function() {
|
||||
// Response matchers
|
||||
window.$('pre.chroma .nd:contains("@")')
|
||||
.map(function(k, item) {
|
||||
let text = item.innerText.replace(/</g,'<').replace(/>/g,'>');
|
||||
let url = '#' + item.innerText.replace(/_/g, "-");
|
||||
window.$(item).addClass('nd').removeClass('k')
|
||||
window.$(item).html(`<a href="#syntax" style="color: inherit;">${text}</a>`);
|
||||
});
|
||||
window.$('pre.chroma .k:contains("status")')
|
||||
.html('<a href="#status" style="color: inherit;">status</a>');
|
||||
window.$('pre.chroma .k:contains("header")')
|
||||
.html('<a href="#header" style="color: inherit;">header</a>');
|
||||
|
||||
// We'll add links to all the subdirectives if a matching anchor tag is found on the page.
|
||||
addLinksToSubdirectives();
|
||||
});
|
||||
</script>
|
||||
|
||||
# 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 <code...>
|
||||
header <field> [<value>]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Matchers
|
||||
|
||||
### status
|
||||
|
||||
```caddy-d
|
||||
status <code...>
|
||||
```
|
||||
|
||||
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 <field> [<value>]
|
||||
```
|
||||
|
||||
By response header fields.
|
||||
|
||||
- `<field>` is the name of the HTTP header field to check.
|
||||
- If prefixed with `!`, the field must not exist to match (omit value arg).
|
||||
- `<value>` 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
|
||||
```
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
<aside class="tip">
|
||||
|
||||
Placeholders are a similar idea to variables in other software. For example, [nginx has variables <img src="/old/resources/images/external-link.svg" class="external-link">](https://nginx.org/en/docs/varindex.html) like `$uri` and `$document_root`.
|
||||
Placeholders are a similar idea to variables in other software. For example, [nginx has variables <img src="/old/resources/images/external-link.svg" class="external-link">](https://nginx.org/en/docs/varindex.html) like `$uri` and `$document_root`, whereas Caddy's equivalent would be [`{http.request.uri}`](/docs/json/apps/http/#docs) and [`{http.vars.root}`](/docs/caddyfile/directives/root).
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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.)
|
||||
|
||||
|
|
|
@ -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 <name> [<option>]
|
||||
// gizmo <name> [<option>]
|
||||
//
|
||||
func (g *Gizmo) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
d.Next() // consume directive name
|
||||
|
@ -57,9 +57,9 @@ To accept more configuration than can fit on a single line, you may wish to allo
|
|||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
switch d.Val() {
|
||||
case "sub_directive_1":
|
||||
// ...
|
||||
// ...
|
||||
case "sub_directive_2":
|
||||
// ...
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -101,20 +101,25 @@ func parseCaddyfileHandler(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler,
|
|||
|
||||
See the [`httpcaddyfile` package godoc](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile?tab=doc) for more information about how to use the `httpcaddyfile.Helper` type.
|
||||
|
||||
|
||||
### Handler order
|
||||
|
||||
All directives which return HTTP middleware/handler values need to be evaluated in the correct order. For example, a handler that sets the root directory of the site has to come before a handler that accesses the root directory, so that it will know what the directory path is.
|
||||
|
||||
The HTTP Caddyfile [has a hard-coded ordering for the standard directives](/docs/caddyfile/directives#directive-order). This ensures that users do not need to know the implementation details of the most common functions of their web server, and makes it easier for them to write correct configurations. A single, hard-coded list also prevents nondeterminism given the extensible nature of the Caddyfile.
|
||||
|
||||
**When you register a new handler directive, it must be added to that list before it can be used (outside of a `route` block).** This is done in configuration using one of two methods:
|
||||
**When you register a new handler directive, it must be added to that list before it can be used (outside of a `route` block).** This is done using one of three methods:
|
||||
|
||||
- The [`order` global option](/docs/caddyfile/options) modifies the standard order for that configuration only. For example: `order mydir before respond` will insert a new directive `mydir` to be evaluated before the `respond` handler. Then the directive can be used normally.
|
||||
- Or, use the directive in a [`route` block](/docs/caddyfile/directives/route). Because directives in a route block are not reordered, the directives used in a route block do not need to appear in the list.
|
||||
- (Recommended) The plugin author can call [`httpcaddyfile.RegisterDirectiveOrder`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile#RegisterDirectiveOrder) in `init()` after registering the directive, to insert the directive into the order relative to another [standard directive](/docs/caddyfile/directives#directive-order). Doing so, users can use the directive directly in their sites without extra setup. For example, to insert your directive `gizmo` to be evaluated after the `header` handler:
|
||||
|
||||
Please document for your users where in the list is the right place for your directive to be ordered so that they can use it properly.
|
||||
```go
|
||||
httpcaddyfile.RegisterDirectiveOrder("gizmo", httpcaddyfile.After, "header")
|
||||
```
|
||||
|
||||
- Users may add the [`order` global option](/docs/caddyfile/options) to modify the standard order for their Caddyfile. For example: `order gizmo before respond` will insert a new directive `gizmo` to be evaluated before the `respond` handler. Then the directive can be used normally.
|
||||
|
||||
- Users can place the directive in a [`route` block](/docs/caddyfile/directives/route). Because directives in a route block are not reordered, the directives used in a route block do not need to appear in the list.
|
||||
|
||||
If you choose one of the latter two options, please document a recommendation for your users for where in the list is the right place for your directive to be ordered, so that they can use it properly.
|
||||
|
||||
### Classes
|
||||
|
||||
|
@ -129,7 +134,6 @@ tls.connection_policy | `*caddytls.ConnectionPolicy` | TLS connection policy
|
|||
tls.cert_issuer | `certmagic.Issuer` | TLS certificate issuer
|
||||
tls.cert_loader | `caddytls.CertificateLoader` | TLS certificate loader
|
||||
|
||||
|
||||
## Server Types
|
||||
|
||||
Structurally, the Caddyfile is a simple format, so there can be different types of Caddyfile formats (sometimes called "server types") to suit different needs.
|
||||
|
|
|
@ -12,34 +12,39 @@ Documentation for non-standard module namespaces can be found with the documenta
|
|||
One way to read this table is, "If your module is in <namespace>, then it should compile as <type>."
|
||||
</aside>
|
||||
|
||||
Namespace | Expected Type | Description | Notes
|
||||
Namespace | Expected Interface Type | Description | Notes
|
||||
--------- | ------------- | ----------- | ----------
|
||||
| | [`caddy.App`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#App) | Caddy app
|
||||
| | [`caddy.App`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#App) | Caddy app
|
||||
admin.api | [`caddy.AdminRouter`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#AdminRouter)<br><br>[`caddy.AdminHandler`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#AdminHandler) | Registers HTTP routes for admin<br><br>HTTP handler middleware |
|
||||
caddy.config_loaders | [`caddy.ConfigLoader`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#ConfigLoader) | Loads a config | <i>⚠️ Experimental</i>
|
||||
caddy.fs | [`fs.FS`](https://pkg.go.dev/io/fs#FS) | Virtual file system | <i>⚠️ Experimental</i>
|
||||
caddy.listeners | [`caddy.ListenerWrapper`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#ListenerWrapper) | Wrap network listeners
|
||||
caddy.logging.encoders | [`zapcore.Encoder`](https://pkg.go.dev/go.uber.org/zap/zapcore#Encoder) | Log entry encoder
|
||||
caddy.logging.encoders.filter | [`logging.LogFieldFilter`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/logging?tab=doc#LogFieldFilter) | Log field filter
|
||||
caddy.logging.writers | [`caddy.WriterOpener`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#WriterOpener) | Log writers
|
||||
caddy.storage | [`caddy.StorageConverter`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#StorageConverter) | Storage backends
|
||||
dns.providers | [`certmagic.ACMEDNSProvider`](https://pkg.go.dev/github.com/caddyserver/certmagic#ACMEDNSProvider) | DNS challenge solver
|
||||
caddy.logging.encoders.filter | [`logging.LogFieldFilter`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/logging#LogFieldFilter) | Log field filter
|
||||
caddy.logging.writers | [`caddy.WriterOpener`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#WriterOpener) | Log writers
|
||||
caddy.storage | [`caddy.StorageConverter`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#StorageConverter) | Storage backends
|
||||
dns.providers | [`certmagic.DNSProvider`](https://pkg.go.dev/github.com/caddyserver/certmagic#DNSProvider) | DNS challenge solver
|
||||
events.handlers | [`caddyevents.Handler`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyevents#Handler) | Event handlers | <i>⚠️ Experimental</i>
|
||||
http.authentication.hashes | [`caddyauth.Comparer`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth?tab=doc#Comparer) | Password hashers/comparers
|
||||
http.authentication.providers | [`caddyauth.Authenticator`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth?tab=doc#Authenticator) | HTTP authentication providers
|
||||
http.encoders | [`encode.Encoder`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/encode#Encoder) | Usually, compression
|
||||
http.authentication.hashes | [`caddyauth.Comparer`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth#Comparer)<br><br>[`caddyauth.Hasher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth#Hasher) | Password comparers<br><br>Password hashers
|
||||
http.authentication.providers | [`caddyauth.Authenticator`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth#Authenticator) | HTTP authentication providers
|
||||
http.encoders | [`encode.Encoding`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/encode#Encoding)<br><br>[`encode.Encoder`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/encode#Encoder) | Creates an encoder (compression)<br><br>Encodes a data stream
|
||||
http.handlers | [`caddyhttp.MiddlewareHandler`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp#MiddlewareHandler) | HTTP handlers
|
||||
http.ip_sources | [`caddyhttp.IPRangeSource`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp#IPRangeSource) | IP ranges for trusted proxies
|
||||
http.matchers | [`caddyhttp.RequestMatcher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp?tab=doc#RequestMatcher) | HTTP request matchers
|
||||
http.matchers | [`caddyhttp.RequestMatcher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp#RequestMatcher)<br><br>[`caddyhttp.CELLibraryProducer`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp#CELLibraryProducer) | HTTP request matchers<br><br>Support for CEL expressions | <br><br><i>(Optional)</i>
|
||||
http.precompressed | [`encode.Precompressed`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/encode#Precompressed) | Supported precompress mappings
|
||||
http.reverse_proxy.circuit_breakers | [`reverseproxy.CircuitBreaker`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy?tab=doc#CircuitBreaker) | Reverse proxy circuit breakers
|
||||
http.reverse_proxy.selection_policies | [`reverseproxy.Selector`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy?tab=doc#Selector) | Load balancing selection policies
|
||||
http.reverse_proxy.transport | [`http.RoundTripper`](https://pkg.go.dev/net/http?tab=doc#RoundTripper) | HTTP reverse proxy transports
|
||||
http.reverse_proxy.upstreams | [`reverseproxy.UpstreamSource`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy?tab=doc#UpstreamSource) | Dynamic upstream source | <i>⚠️ Experimental</i>
|
||||
tls.certificates | [`caddytls.CertificateLoader`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls?tab=doc#CertificateLoader) | TLS certificate source
|
||||
http.reverse_proxy.circuit_breakers | [`reverseproxy.CircuitBreaker`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy#CircuitBreaker) | Reverse proxy circuit breakers
|
||||
http.reverse_proxy.selection_policies | [`reverseproxy.Selector`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy#Selector) | Load balancing selection policies
|
||||
http.reverse_proxy.transport | [`http.RoundTripper`](https://pkg.go.dev/net/http#RoundTripper) | HTTP reverse proxy transports
|
||||
http.reverse_proxy.upstreams | [`reverseproxy.UpstreamSource`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy#UpstreamSource) | Dynamic upstream source | <i>⚠️ Experimental</i>
|
||||
tls.ca_pool.source | [`caddytls.CA`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#CA) | Source of trusted root certs
|
||||
tls.certificates | [`caddytls.CertificateLoader`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#CertificateLoader) | TLS certificate source
|
||||
tls.client_auth | [`caddytls.ClientCertificateVerifier`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#ClientCertificateVerifier) | Verifies client certificates
|
||||
tls.handshake_match | [`caddytls.ConnectionMatcher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls?tab=doc#ConnectionMatcher) | TLS connection matcher
|
||||
tls.issuance | [`certmagic.Issuer`](https://pkg.go.dev/github.com/caddyserver/certmagic?tab=doc#Issuer) | TLS certificate issuer
|
||||
tls.get_certificate | [`certmagic.Manager`](https://pkg.go.dev/github.com/caddyserver/certmagic?tab=doc#Manager) | TLS certificate manager | <i>⚠️ Experimental</i>
|
||||
tls.stek | [`caddytls.STEKProvider`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls?tab=doc#STEKProvider) | TLS session ticket key source
|
||||
tls.ech.publishers | [`caddytls.ECHPublisher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#ECHPublisher) | Publishes Encrypted ClientHello (ECH) configurations | <i>⚠️ Experimental</i>
|
||||
tls.get_certificate | [`certmagic.Manager`](https://pkg.go.dev/github.com/caddyserver/certmagic#Manager) | TLS certificate manager | <i>⚠️ Experimental</i>
|
||||
tls.handshake_match | [`caddytls.ConnectionMatcher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#ConnectionMatcher) | TLS connection matcher
|
||||
tls.issuance | [`certmagic.Issuer`](https://pkg.go.dev/github.com/caddyserver/certmagic#Issuer) | TLS certificate issuer
|
||||
tls.leaf_cert_loader | [`caddytls.LeafCertificateLoader`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#LeafCertificateLoader) | Loads trusted leaf certs
|
||||
tls.permission | [`caddytls.OnDemandPermission`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#OnDemandPermission) | Whether to obtain a cert for a domain | <i>⚠️ Experimental</i>
|
||||
tls.stek | [`caddytls.STEKProvider`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#STEKProvider) | TLS session ticket key source
|
||||
|
||||
Namespaces marked as "Experimental" are subject to change. (Please develop with them so we can finalize their interfaces!)
|
||||
|
|
110
src/docs/markdown/extending-caddy/placeholders.md
Normal file
110
src/docs/markdown/extending-caddy/placeholders.md
Normal file
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
title: "Placeholder Support"
|
||||
---
|
||||
|
||||
# Placeholders
|
||||
|
||||
In Caddy, placeholders are processed by each individual plugin as needed; they do not automatically work everywhere.
|
||||
|
||||
This means that if you wish for your plugin to support placeholders, you must explicitly add support for them.
|
||||
|
||||
If you are not yet familiar with placeholders, start by [reading here](/docs/conventions#placeholders)!
|
||||
|
||||
## Placeholders Overview
|
||||
|
||||
[Placeholders](/docs/conventions#placeholders) are a string in the format `{foo.bar}` used as dynamic configuration values, which is later evaluated at runtime.
|
||||
|
||||
Caddyfile [environment variables substitutions](/docs/caddyfile/concepts#environment-variables) which start with a dollar sign like `{$FOO}` are evaluated at Caddyfile-parse time, and do not need to be handled by your plugin. These are _not_ placeholders, despite sharing the same `{ }` syntax.
|
||||
|
||||
It is therefore important to understand that `{env.HOST}` (a [global placeholder](/docs/conventions#placeholders)) is inherently different from `{$HOST}` (a Caddyfile env-var substitution).
|
||||
|
||||
As an example, see the following Caddyfile:
|
||||
```caddy
|
||||
:8080 {
|
||||
respond {$HOST} 200
|
||||
}
|
||||
|
||||
:8081 {
|
||||
respond {env.HOST} 200
|
||||
}
|
||||
```
|
||||
|
||||
When you adapt this Caddyfile to JSON with `HOST=example caddy adapt` you will get:
|
||||
|
||||
```json
|
||||
{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [":8080"],
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"body": "example",
|
||||
"handler": "static_response",
|
||||
"status_code": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"srv1": {
|
||||
"listen": [":8081"],
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"body": "{env.HOST}",
|
||||
"handler": "static_response",
|
||||
"status_code": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In particular, look at the `"body"` field in both `srv0` and `srv1`.
|
||||
|
||||
Since `srv0` used `{$HOST}` (Caddyfile env-var substitution), the value became `example`, as it was processed during Caddyfile parse time when producing the JSON config.
|
||||
|
||||
Since `srv1` used `{env.HOST}` (a global placeholder), it remains untouched when adapting to JSON.
|
||||
|
||||
This does mean that users writing JSON config (not using Caddyfile) cannot use the `{$ENV}` syntax. For that reason, it's important that plugin authors implement support for replacing placeholders when the config is provisioned. This is explained below.
|
||||
|
||||
|
||||
## Implementing placeholder support
|
||||
|
||||
You should not process placeholders in [`UnmarshalCaddyfile()`](/docs/extending-caddy/caddyfile). Instead, placeholders should be replaced later, either in the [`Provision()`](/docs/extending-caddy#provisioning) step, or during your module's execution (e.g. `ServeHTTP()` for HTTP handlers, `Match()` for matchers, etc.), using a `caddy.Replacer`.
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
Here, we are using a newly constructed replacer to process placeholders. It has access to [global placeholders](/docs/conventions#placeholders) such as `{env.HOST}`, but _not_ HTTP placeholders such as `{http.request.uri}` because provisioning happens when the config is loaded, and not during a request.
|
||||
|
||||
```go
|
||||
func (g *Gizmo) Provision(ctx caddy.Context) error {
|
||||
repl := caddy.NewReplacer()
|
||||
g.Name = repl.ReplaceAll(g.Name,"")
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
Here, we fetch the replacer from the request context `r.Context()` during `ServeHTTP`. This replacer has access to both global placeholders _and_ per-request HTTP placeholders such as `{http.request.uri}`.
|
||||
|
||||
```go
|
||||
func (g *Gizmo) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
|
||||
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
_, err := w.Write([]byte(repl.ReplaceAll(g.Name,"")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return next.ServeHTTP(w, r)
|
||||
}
|
||||
```
|
|
@ -83,7 +83,7 @@ If you wish to use the packaged support files (systemd services, bash completion
|
|||
|
||||
This package comes with both of Caddy's [systemd service](/docs/running#linux-service) unit files, but does not enable them by default. Using the service is recommended. If you do, please read the [service usage instructions](/docs/running#using-the-service).
|
||||
|
||||
Fedora or RHEL/CentOS 8:
|
||||
Fedora or RHEL/CentOS 8 and later:
|
||||
|
||||
<pre><code class="cmd"><span class="bash">dnf install 'dnf-command(copr)'</span>
|
||||
<span class="bash">dnf copr enable @caddy/caddy</span>
|
||||
|
|
|
@ -17,19 +17,27 @@ If using a Caddyfile, enable metrics [in global options](/docs/caddyfile/options
|
|||
|
||||
```caddy
|
||||
{
|
||||
servers {
|
||||
metrics
|
||||
}
|
||||
metrics
|
||||
}
|
||||
```
|
||||
|
||||
If using JSON, add `"metrics": {}` to your [`apps > http > servers` configuration](/docs/json/apps/http/servers/).
|
||||
|
||||
To add per-host metrics you can insert the `per_host` option. Host specific metrics will now have a Host tag.
|
||||
|
||||
```caddy
|
||||
{
|
||||
metrics {
|
||||
per_host
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Prometheus
|
||||
|
||||
[Prometheus](https://prometheus.io) is a monitoring platform that collects
|
||||
metrics from monitored targets by scraping metrics HTTP endpoints on these
|
||||
targets. As well as helping you to display metrics with a dashboarding tool like [Grafana](https://grafana.com/docs/grafana/latest/getting-started/what-is-grafana/), Prometheus is also used for [alerting](https://prometheus.io/docs/alerting/latest/overview/).
|
||||
targets. As well as helping you to display metrics with a dashboarding tool like [Grafana](https://grafana.com/docs/grafana/latest/introduction/), Prometheus is also used for [alerting](https://prometheus.io/docs/alerting/latest/overview/).
|
||||
|
||||
Like Caddy, Prometheus is written in Go and distributed as a single binary. To
|
||||
install it, see the [Prometheus Installation docs](https://prometheus.io/docs/prometheus/latest/installation/),
|
||||
|
|
|
@ -13,6 +13,9 @@ While Caddy can be run directly with its [command line interface](/docs/command-
|
|||
- [Using the Service](#using-the-service)
|
||||
- [Local HTTPS](#local-https-with-systemd)
|
||||
- [Overrides](#overrides)
|
||||
- [Environment variables](#environment-variables)
|
||||
- [`run` and `reload` override](#run-and-reload-override)
|
||||
- [Restart on crash](#restart-on-crash)
|
||||
- [SELinux Considerations](#selinux-considerations)
|
||||
- [Windows service](#windows-service)
|
||||
- [sc.exe](#scexe)
|
||||
|
@ -30,7 +33,7 @@ The recommended way to run Caddy on Linux distributions with systemd is with our
|
|||
|
||||
### Unit Files
|
||||
|
||||
We provide two different systemd unit files that you can choose between, depending on your usecase:
|
||||
We provide two different systemd unit files that you can choose between, depending on your use case:
|
||||
|
||||
- [**`caddy.service`**](https://github.com/caddyserver/dist/blob/master/init/caddy.service) if you configure Caddy with a [Caddyfile](/docs/caddyfile). If you prefer to use a different config adapter or a JSON config file, you may [override](#overrides) the `ExecStart` and `ExecReload` commands.
|
||||
|
||||
|
@ -138,7 +141,9 @@ The best way to override aspects of the service files is with this command:
|
|||
|
||||
This will open a blank file with your default terminal text editor in which you can override or add directives to the unit definition. This is called a "drop-in" file.
|
||||
|
||||
For example, if you need to define environment variables for use in your config, you may do so like this:
|
||||
#### Environment variables
|
||||
|
||||
If you need to define environment variables for use in your config, you may do so like this:
|
||||
```systemd
|
||||
[Service]
|
||||
Environment="CF_API_TOKEN=super-secret-cloudflare-tokenvalue"
|
||||
|
@ -150,7 +155,15 @@ Similarly, if you prefer to maintain a separate file to maintain the environment
|
|||
EnvironmentFile=/etc/caddy/.env
|
||||
```
|
||||
|
||||
Or, for example if you need to change the config file from the default of the Caddyfile, to instead using a JSON file (note that `Exec*` directives [must be reset with empty strings](https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=) before setting a new value):
|
||||
Then your `/etc/caddy/.env` file may look like this (do not use `"` quotes around the values):
|
||||
|
||||
```env
|
||||
CF_API_TOKEN=super-secret-cloudflare-tokenvalue
|
||||
```
|
||||
|
||||
#### `run` and `reload` override
|
||||
|
||||
If you need to change the config file from the default of the Caddyfile, to instead using a JSON file (note that `Exec*` directives [must be reset with empty strings](https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=) before setting a new value):
|
||||
```systemd
|
||||
[Service]
|
||||
ExecStart=
|
||||
|
@ -159,7 +172,9 @@ ExecReload=
|
|||
ExecReload=/usr/bin/caddy reload --config /etc/caddy/caddy.json
|
||||
```
|
||||
|
||||
Or, for example, if you'd like caddy to restart itself after 5s if it ever crashes unexpectedly:
|
||||
#### Restart on crash
|
||||
|
||||
If you'd like caddy to restart itself after 5s if it ever crashes unexpectedly:
|
||||
```systemd
|
||||
[Service]
|
||||
# Automatically restart caddy if it crashes except if the exit code was 1
|
||||
|
@ -171,6 +186,8 @@ RestartSec=5s
|
|||
Then, save the file and exit the text editor, and restart the service for it to take effect:
|
||||
<pre><code class="cmd bash">sudo systemctl restart caddy</code></pre>
|
||||
|
||||
|
||||
|
||||
### SELinux Considerations
|
||||
|
||||
On SELinux enabled systems you have two options:
|
||||
|
|
|
@ -4,7 +4,7 @@ title: Verifying Asset Signatures
|
|||
|
||||
# Signature Verification
|
||||
|
||||
Artifact signing allows you to validate the artifact you have is the same one created by the project's workflow and was not modified by an unauthorized party (e.g. man-in-the-middle). The validation provides common ground, assurance, and knowledge that all parties are refering to the same artifact, collection of bytes, whether it is an executable, SBOM, or text file.
|
||||
Artifact signing allows you to validate the artifact you have is the same one created by the project's workflow and was not modified by an unauthorized party (e.g. man-in-the-middle). The validation provides common ground, assurance, and knowledge that all parties are referring to the same artifact, collection of bytes, whether it is an executable, SBOM, or text file.
|
||||
|
||||
As of Caddy v2.6.0, CI/CD release artifacts are signed using project [Sigstore](https://www.sigstore.dev/) technology, which issues certificates containing details about the subject to whom the certificate is issued. You can start by inspecting the certificate used to sign your artifact of choice. The certificates are base64-encoded, so you first have to base64-decode it to receive the PEM file. In this example, we'll work with the `caddy_2.6.0_checksums.txt` artifact and assume a Linux-like environment.
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
<option value="linux-ppc64">Linux ppc64</option>
|
||||
<option value="linux-ppc64le">Linux ppc64le</option>
|
||||
<option value="linux-s390x">Linux s390x</option>
|
||||
<option value="linux-loong64">Linux loong64</option>
|
||||
<option value="darwin-amd64">macOS amd64 (Intel)</option>
|
||||
<option value="darwin-arm64">macOS arm64 (Apple)</option>
|
||||
<option value="openbsd-amd64">OpenBSD amd64</option>
|
||||
|
|
|
@ -537,7 +537,7 @@
|
|||
<div class="feature-row">
|
||||
<h4>Revocation handling</h4>
|
||||
<div class="benefits">
|
||||
Revoked certificates automatically get replaced. Because Caddy staples and refreshes OCSP responses, it can detect if your certificate has been revoked, and if so, it will replace i
|
||||
Revoked certificates automatically get replaced. Because Caddy staples and refreshes OCSP responses, it can detect if your certificate has been revoked, and if so, it will replace it with a fresh certificate.
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-row">
|
||||
|
@ -708,6 +708,16 @@
|
|||
<li>CommonName of any</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="feature-row">
|
||||
<h4>Renewal scheduling</h4>
|
||||
<div class="benefits">
|
||||
Caddy chooses conservative timeframes to renew certificates, and also takes guidance from the ACME CA if it provides ACME Renewal Information (ARI).
|
||||
</div>
|
||||
<ul class="detail">
|
||||
<li>Internal</li>
|
||||
<li>ARI</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -1505,9 +1515,9 @@
|
|||
If your deployment pipeline compresses site resources, Caddy can automatically detect them and serve them in their "precompressed" encoding for higher efficiency and greater throughput.
|
||||
</div>
|
||||
<ul class="detail">
|
||||
<li>Gzip</li>
|
||||
<li>Brotli</li>
|
||||
<li>Zstandard</li>
|
||||
<li>Gzip</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="feature-row">
|
||||
|
|
|
@ -38,7 +38,7 @@ root * /var/www
|
|||
|
||||
# Serve precompressed files if present
|
||||
file_server /downloads/* {
|
||||
precompressed gzip zstd br
|
||||
precompressed
|
||||
}
|
||||
|
||||
# Compress everything else that would benefit
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
{
|
||||
# Enable FrankenPHP
|
||||
frankenphp
|
||||
order php_server before file_server
|
||||
}
|
||||
|
||||
example.com {
|
||||
|
@ -17,4 +16,4 @@ example.com {
|
|||
|
||||
<script>
|
||||
window.$_('.ex-frankenphp code').classList.add('dark');
|
||||
</script>
|
||||
</script>
|
||||
|
|
|
@ -67,9 +67,9 @@
|
|||
</div>
|
||||
<div class="sponsors-divider"></div>
|
||||
<div class="sponsor-logos">
|
||||
<!-- <a href="https://opensource.mercedes-benz.com/" target="_blank">
|
||||
<a href="https://opensource.mercedes-benz.com/" target="_blank">
|
||||
<img src="/resources/images/sponsors/mercedes-benz.svg" alt="Mercedes-Benz" title="Mercedes-Benz" height="38">
|
||||
</a> -->
|
||||
</a>
|
||||
<a href="https://tailscale.com" target="_blank">
|
||||
<img src="/resources/images/sponsors/tailscale.svg" alt="Tailscale" title="Tailscale" height="22">
|
||||
</a>
|
||||
|
@ -79,8 +79,11 @@
|
|||
<a href="https://framer.com" target="_blank">
|
||||
<img src="/resources/images/sponsors/framer.svg" alt="Framer" title="Framer" height="32">
|
||||
</a>
|
||||
<a href="https://hopetv.org/" target="_blank">
|
||||
<img src="/resources/images/sponsors/hopetv.png" alt="Hope Channel" title="Hope Channel" height="42">
|
||||
</a>
|
||||
<a href="https://sourcegraph.com/" target="_blank">
|
||||
<img src="/resources/images/sponsors/sourcegraph.svg" alt="Sourcegraph" title="Sourcegraph" height="30">
|
||||
<img src="/resources/images/sponsors/sourcegraph.png" alt="Sourcegraph" title="Sourcegraph" height="23">
|
||||
</a>
|
||||
<a href="https://fusionauth.com" target="_blank">
|
||||
<img src="/resources/images/sponsors/fusionauth.svg" alt="FusionAuth" title="FusionAuth" height="35">
|
||||
|
@ -176,12 +179,26 @@
|
|||
</div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="sponsorship-primer">
|
||||
<h3>sponsored by <b>users like you</b></h3>
|
||||
<p>
|
||||
Caddy is free software and relies on sponsorships to survive. Not just donations: sponsorships ensure ongoing development and provide your business with tangible benefits.
|
||||
</p>
|
||||
<a href="/sponsor" class="button purple">See sponsorships</a>
|
||||
<div class="cols">
|
||||
<div class="col">
|
||||
<div class="sponsorship-primer">
|
||||
<h3>sponsored by <b>users like you</b></h3>
|
||||
<p>
|
||||
Caddy is free software and relies on sponsorships to survive. Not just donations: sponsorships ensure ongoing development and provide your business with tangible benefits.
|
||||
</p>
|
||||
<a href="/sponsor" class="button purple">See sponsorships</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="sponsor-experience hopetv">
|
||||
<img src="/resources/images/sponsors/hopetv.png" class="experience-logo">
|
||||
<div class="experience-content">
|
||||
<div class="experience-quote">
|
||||
"Caddy's powerful technology has enabled Hope Channel International to deliver hope to millions, reaching people in every corner of the world."
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -29,16 +29,17 @@
|
|||
<a href="/docs/caddyfile">Caddyfile</a>
|
||||
<ul>
|
||||
<li><a href="/docs/caddyfile/concepts">Concepts</a></li>
|
||||
<li><a href="/docs/caddyfile/options">Global options</a></li>
|
||||
<li><a href="/docs/caddyfile/directives">Directives</a></li>
|
||||
<li><a href="/docs/caddyfile/matchers">Request matchers</a></li>
|
||||
<li><a href="/docs/caddyfile/options">Global options</a></li>
|
||||
<li><a href="/docs/caddyfile/response-matchers">Response matchers</a></li>
|
||||
<li><a href="/docs/caddyfile/patterns">Common patterns</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/docs/modules/">Modules</a></li>
|
||||
<li><a href="/docs/json/">JSON Config Structure</a></li>
|
||||
<li><a href="/docs/automatic-https">Automatic HTTPS</a></li>
|
||||
|
||||
|
||||
<li class="heading">Articles</li>
|
||||
<li><a href="/docs/architecture">Caddy Architecture</a></li>
|
||||
<li><a href="/docs/conventions">Conventions</a></li>
|
||||
|
@ -56,6 +57,7 @@
|
|||
<ul>
|
||||
<li><a href="/docs/extending-caddy/caddyfile">Caddyfile Support</a></li>
|
||||
<li><a href="/docs/extending-caddy/config-adapters">Config Adapters</a></li>
|
||||
<li><a href="/docs/extending-caddy/placeholders">Placeholders</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/docs/extending-caddy/namespaces">Module Namespaces</a></li>
|
||||
|
|
|
@ -718,8 +718,13 @@ table {
|
|||
font-size: 16px;
|
||||
}
|
||||
|
||||
article > table {
|
||||
margin: 25px auto;
|
||||
article > .table-wrapper {
|
||||
overflow-x: scroll;
|
||||
margin: 25px 0 0 0;
|
||||
}
|
||||
|
||||
article table {
|
||||
margin: 0 auto 25px auto;
|
||||
}
|
||||
|
||||
th, td {
|
||||
|
|
|
@ -342,6 +342,13 @@ function makeSubmoduleList(module, path, value) {
|
|||
}
|
||||
var submodList = '<ul>';
|
||||
if (module.namespaces && module.namespaces[value.module_namespace]) {
|
||||
module.namespaces[value.module_namespace].sort(function(a, b){
|
||||
if(isStandard(a.package) && !isStandard(b.package)) return -1;
|
||||
if(!isStandard(a.package) && isStandard(b.package)) return 1;
|
||||
if(a.name < b.name) return -1;
|
||||
if(a.name > b.name) return 1;
|
||||
return 0;
|
||||
});
|
||||
for (var j = 0; j < module.namespaces[value.module_namespace].length; j++) {
|
||||
var submod = module.namespaces[value.module_namespace][j];
|
||||
var href = canTraverse(module) ? '.'+path+'/'+submod.name+'/' : './'+value.module_namespace+'.'+submod.name;
|
||||
|
|
|
@ -55,6 +55,9 @@ $(function() {
|
|||
let text = item.innerText.replace(/</g,'<').replace(/>/g,'>');
|
||||
$(item).html('<a href="/docs/caddyfile/matchers#syntax" style="color: inherit;" title="Matcher token">' + text + '</a>');
|
||||
});
|
||||
|
||||
// Wrap all tables in a div so we can apply overflow-x: scroll
|
||||
$('table').wrap('<div class="table-wrapper"></div>');
|
||||
});
|
||||
|
||||
// addLinkaddLinksToSubdirectivessToAnchors finds all the ID anchors
|
||||
|
|
|
@ -264,7 +264,7 @@ main nav li ul {
|
|||
|
||||
|
||||
article {
|
||||
font-family: 'Albert Sans', system-ui; /* considered: Figtree, Asisstant, Red Hat Text, Be Vietnam Pro
|
||||
font-family: 'Albert Sans', system-ui; /* considered: Figtree, Assistant, Red Hat Text, Be Vietnam Pro */
|
||||
font-size: 20px;
|
||||
word-wrap: break-word;
|
||||
|
||||
|
@ -587,8 +587,14 @@ table {
|
|||
font-size: 16px;
|
||||
}
|
||||
|
||||
article > table {
|
||||
margin: 25px auto;
|
||||
article > .table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin: 25px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
article table {
|
||||
margin: 0 auto 25px auto;
|
||||
}
|
||||
|
||||
th, td {
|
||||
|
|
|
@ -141,4 +141,49 @@ h3 {
|
|||
.feature-row {
|
||||
grid: auto / 1fr 2fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font-size: 14px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.diagonal,
|
||||
.diagonal > * {
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
margin: 25px 0;
|
||||
}
|
||||
|
||||
.feature-row {
|
||||
padding: .5em;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.feature-row .benefits {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
p + h3 {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
section {
|
||||
background: white !important;
|
||||
color: inherit !important;
|
||||
padding: 25px 0;
|
||||
}
|
||||
|
||||
.diagonal {
|
||||
padding: 25px 0;
|
||||
margin: 25px 0;
|
||||
}
|
||||
}
|
|
@ -247,7 +247,7 @@ section.dark .cols {
|
|||
|
||||
|
||||
.cols .col {
|
||||
min-width: 350px;
|
||||
min-width: 300px;
|
||||
padding-left: 1em;
|
||||
flex: 1;
|
||||
}
|
||||
|
@ -567,6 +567,7 @@ div.ap-wrapper:fullscreen div.ap-player {
|
|||
|
||||
.sponsor-experience .experience-logo {
|
||||
width: 150px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.sponsor-experience .experience-credit {
|
||||
|
@ -600,6 +601,10 @@ div.ap-wrapper:fullscreen div.ap-player {
|
|||
background: linear-gradient(to right, #635bff 30%, #d4b8ff);
|
||||
}
|
||||
|
||||
.sponsor-experience.hopetv {
|
||||
background: linear-gradient(to right, rgb(11, 77, 162) 30%, rgb(255, 212, 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -659,4 +664,4 @@ div.ap-wrapper:fullscreen div.ap-player {
|
|||
.display.left > * {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
BIN
src/resources/images/ech-grease.png
Normal file
BIN
src/resources/images/ech-grease.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
BIN
src/resources/images/sponsors/hopetv.png
Normal file
BIN
src/resources/images/sponsors/hopetv.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
src/resources/images/sponsors/sourcegraph.png
Normal file
BIN
src/resources/images/sponsors/sourcegraph.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
|
@ -1,77 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 312.8 55.5" style="enable-background:new 0 0 312.8 55.5;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FF5543;}
|
||||
.st1{fill:#A112FF;}
|
||||
.st2{fill:#00CBEC;}
|
||||
.st3{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M34.8,53.8c-2.8,0.5-5.5-1.3-6-4.1L21.2,8.2c-0.5-2.8,1.3-5.5,4.1-6s5.5,1.3,6,4.1l7.6,41.5
|
||||
C39.4,50.6,37.5,53.3,34.8,53.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M14.9,46.7C13.1,47,11.3,46.4,10,45c-1.8-2.2-1.6-5.4,0.6-7.2l32.1-27.3c2.2-1.8,5.4-1.6,7.2,0.6
|
||||
s1.6,5.4-0.6,7.2l-32,27.3C16.6,46.2,15.7,46.6,14.9,46.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st2" d="M50.8,40.1c-0.9,0.2-1.8,0.1-2.6-0.2L8.4,25.8c-2.7-1-4.1-3.9-3.1-6.6s3.9-4.1,6.6-3.1l39.7,14.1
|
||||
c2.7,1,4.1,3.9,3.1,6.6C54.1,38.6,52.5,39.8,50.8,40.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path class="st3" d="M84,35.1c0-1-0.4-1.8-1.1-2.4c-0.7-0.6-1.6-1.2-2.7-1.7s-2.3-1-3.6-1.6c-1.3-0.5-2.5-1.2-3.6-2s-2-1.8-2.7-3
|
||||
s-1.1-2.7-1.1-4.5c0-1.6,0.3-3,0.8-4.1c0.5-1.2,1.3-2.1,2.3-2.9c1-0.8,2.1-1.3,3.5-1.7c1.3-0.4,2.8-0.6,4.5-0.6
|
||||
c1.9,0,3.7,0.2,5.3,0.5s3.1,0.8,4.1,1.4l-2,5.3C87,17.4,86,17,84.6,16.7c-1.4-0.4-2.8-0.5-4.4-0.5c-1.5,0-2.6,0.3-3.4,0.9
|
||||
s-1.2,1.4-1.2,2.4c0,0.9,0.4,1.7,1.1,2.3c0.7,0.6,1.6,1.2,2.7,1.7s2.3,1.1,3.6,1.6c1.3,0.6,2.5,1.2,3.6,2s2,1.8,2.7,2.9
|
||||
c0.7,1.2,1.1,2.6,1.1,4.3s-0.3,3.2-0.9,4.5s-1.4,2.3-2.4,3.1s-2.3,1.5-3.8,1.9s-3.1,0.6-4.9,0.6c-2.3,0-4.4-0.2-6.1-0.7
|
||||
c-1.8-0.4-3.1-0.9-3.9-1.3l2-5.4c0.3,0.2,0.8,0.4,1.3,0.6c0.5,0.2,1.2,0.4,1.8,0.6c0.7,0.2,1.4,0.3,2.2,0.5
|
||||
c0.8,0.1,1.5,0.2,2.3,0.2c1.9,0,3.3-0.3,4.3-1C83.5,37.3,84,36.4,84,35.1z"/>
|
||||
<path class="st3" d="M93.1,32.2c0-3.9,1-7,2.9-9.1s4.6-3.2,8.1-3.2c1.9,0,3.5,0.3,4.8,0.9c1.4,0.6,2.5,1.4,3.4,2.5s1.6,2.4,2,3.9
|
||||
s0.7,3.2,0.7,5c0,3.9-1,7-2.9,9.1s-4.6,3.2-8.1,3.2c-1.9,0-3.5-0.3-4.8-0.9c-1.4-0.6-2.5-1.4-3.4-2.5s-1.6-2.4-2-3.9
|
||||
C93.3,35.7,93.1,34,93.1,32.2z M99.3,32.2c0,1,0.1,2,0.3,2.8c0.2,0.9,0.5,1.6,0.8,2.3c0.4,0.7,0.9,1.2,1.5,1.5
|
||||
c0.6,0.4,1.3,0.5,2.2,0.5c1.6,0,2.8-0.6,3.5-1.7c0.8-1.1,1.2-3,1.2-5.4c0-2.1-0.4-3.9-1.1-5.2c-0.7-1.3-1.9-2-3.6-2
|
||||
c-1.5,0-2.7,0.6-3.5,1.7C99.7,27.8,99.3,29.6,99.3,32.2z"/>
|
||||
<path class="st3" d="M124.1,20.6v13.2c0,1.9,0.2,3.3,0.7,4.1c0.4,0.8,1.3,1.3,2.6,1.3c1.1,0,2.1-0.3,2.9-1c0.8-0.7,1.3-1.5,1.7-2.5
|
||||
v-15h6v16.2c0,1.3,0.1,2.5,0.2,3.7c0.1,1.2,0.3,2.3,0.6,3.3h-4.6l-1.1-3.4h-0.2c-0.7,1.2-1.7,2.2-3,2.9c-1.3,0.8-2.8,1.2-4.5,1.2
|
||||
c-1.2,0-2.2-0.2-3.2-0.5c-0.9-0.3-1.7-0.8-2.3-1.5c-0.6-0.7-1.1-1.7-1.4-2.9S118,36.9,118,35V20.6H124.1z"/>
|
||||
<path class="st3" d="M155.5,26.2c-1-0.3-1.8-0.5-2.6-0.5c-1.1,0-2,0.3-2.7,0.9s-1.2,1.3-1.5,2.2v15h-6V20.6h4.7l0.7,3.1h0.2
|
||||
c0.5-1.1,1.2-2,2.1-2.7s2-0.9,3.2-0.9c0.8,0,2.5,0.4,3.4,0.8L155.5,26.2z"/>
|
||||
<path class="st3" d="M175.1,42.4c-0.9,0.7-2.1,1.2-3.4,1.6c-1.3,0.4-2.7,0.5-4.1,0.5c-1.9,0-3.4-0.3-4.7-0.9
|
||||
c-1.3-0.6-2.3-1.4-3.1-2.5s-1.4-2.4-1.7-3.9c-0.4-1.5-0.5-3.2-0.5-5c0-3.9,0.9-7,2.7-9.1s4.3-3.2,7.7-3.2c1.7,0,3.1,0.1,4.1,0.4
|
||||
s2,0.6,2.8,1.1l-1.4,4.9c-0.7-0.3-1.4-0.6-2.1-0.8c-0.7-0.2-1.5-0.3-2.4-0.3c-1.7,0-2.9,0.6-3.8,1.7c-0.9,1.1-1.3,2.9-1.3,5.3
|
||||
c0,1,0.1,1.9,0.3,2.7s0.5,1.6,1,2.2c0.4,0.6,1,1.1,1.7,1.5s1.5,0.5,2.4,0.5c1,0,1.9-0.1,2.6-0.4c0.7-0.3,1.3-0.6,1.9-1L175.1,42.4z
|
||||
"/>
|
||||
<path class="st3" d="M196.4,41.8c-0.9,0.7-2.2,1.4-3.8,1.9s-3.3,0.8-5.1,0.8c-3.8,0-6.5-1.1-8.2-3.3s-2.6-5.2-2.6-9
|
||||
c0-4.1,1-7.2,2.9-9.2s4.7-3.1,8.2-3.1c1.2,0,2.3,0.2,3.4,0.5s2.1,0.8,3,1.5s1.6,1.7,2.1,2.9c0.5,1.2,0.8,2.7,0.8,4.5
|
||||
c0,0.7,0,1.3-0.1,2.1c-0.1,0.7-0.2,1.5-0.3,2.3h-14c0.1,2,0.6,3.4,1.5,4.4s2.4,1.5,4.4,1.5c1.3,0,2.4-0.2,3.4-0.6
|
||||
c1-0.4,1.8-0.8,2.3-1.2L196.4,41.8z M187.7,24.7c-1.6,0-2.8,0.5-3.5,1.4c-0.8,0.9-1.2,2.2-1.4,3.8h8.6c0.1-1.7-0.1-3-0.8-3.9
|
||||
C190.1,25.2,189.1,24.7,187.7,24.7z"/>
|
||||
<path class="st3" d="M220.6,43.8c0,3.4-0.9,5.9-2.7,7.5s-4.4,2.4-7.7,2.4c-2.2,0-4-0.2-5.3-0.5s-2.3-0.6-2.9-1l1.3-4.8
|
||||
c0.7,0.3,1.5,0.6,2.5,0.8s2.1,0.4,3.5,0.4c2.1,0,3.5-0.5,4.3-1.4s1.1-2.2,1.1-3.8V42h-0.2c-1.1,1.5-3,2.2-5.8,2.2
|
||||
c-3,0-5.2-0.9-6.7-2.8c-1.5-1.9-2.2-4.8-2.2-8.7c0-4.2,1-7.3,3-9.4s4.9-3.2,8.6-3.2c2,0,3.8,0.1,5.3,0.4s2.8,0.6,3.8,1L220.6,43.8
|
||||
L220.6,43.8z M210.4,39.3c1.2,0,2.1-0.3,2.7-0.8c0.6-0.5,1.1-1.3,1.5-2.4V25.7c-1-0.4-2.2-0.6-3.6-0.6c-1.6,0-2.8,0.6-3.6,1.7
|
||||
c-0.9,1.2-1.3,3-1.3,5.6c0,2.3,0.4,4,1.1,5.2C207.9,38.8,209,39.3,210.4,39.3z"/>
|
||||
<path class="st3" d="M238.1,26.2c-1-0.3-1.8-0.5-2.6-0.5c-1.1,0-2,0.3-2.7,0.9s-1.2,1.3-1.5,2.2v15h-6V20.6h4.7l0.7,3.1h0.2
|
||||
c0.5-1.1,1.2-2,2.1-2.7s2-0.9,3.2-0.9c0.8,0,1.7,0.2,2.7,0.5L238.1,26.2z"/>
|
||||
<path class="st3" d="M241,21.7c1.2-0.6,2.1-0.8,3.8-1.1c1.7-0.3,3.5-0.5,5.3-0.5c1.6,0,3,0.2,4,0.6s1.9,0.9,2.6,1.7
|
||||
c0.6,0.7,1.1,1.6,1.3,2.6c0.3,1,0.4,2.1,0.4,3.3c0,1.4,0,2.7-0.1,4.1c-0.1,1.4-0.1,2.7-0.2,4.1c0,1.3,0,2.6,0.1,3.9
|
||||
s0.3,2.4,0.7,3.6H254l-1-3.2l0,0c-0.6,1-1.5,1.8-2.6,2.5s-2.5,1-4.3,1c-1.1,0-2.1-0.2-2.9-0.5c-0.9-0.3-1.6-0.8-2.2-1.4
|
||||
s-1.1-1.3-1.4-2.1c-0.3-0.8-0.5-1.7-0.5-2.8c0-1.4,0.3-2.6,1-3.6s1.5-1.8,2.7-2.4s2.6-1,4.3-1.3s3.5-0.3,5.6-0.2
|
||||
c0.2-1.7,0.1-3-0.4-3.7c-0.5-0.8-1.5-1.1-3.1-1.1c-1.2,0-2.5,0.1-3.8,0.4s-1.9,0.4-2.7,0.8L241,21.7z M248.1,39.2
|
||||
c1.2,0,2.2-0.3,2.9-0.8s1.2-1.1,1.6-1.7v-3c-1-0.1-1.9-0.1-2.8,0c-0.9,0.1-1.7,0.2-2.3,0.4s-1.2,0.5-1.6,0.9
|
||||
c-0.4,0.4-0.6,0.9-0.6,1.5c0,0.9,0.3,1.5,0.8,2C246.5,39,247.2,39.2,248.1,39.2z"/>
|
||||
<path class="st3" d="M263.1,20.6h4.4l0.7,2.8h0.2c0.8-1.2,1.8-2,2.9-2.6s2.4-0.8,4-0.8c2.9,0,5.1,0.9,6.6,2.8s2.2,4.8,2.2,8.9
|
||||
c0,2-0.2,3.8-0.7,5.4s-1.2,3-2.1,4.1s-2,2-3.3,2.6s-2.8,0.9-4.5,0.9c-1,0-1.8-0.1-2.4-0.2c-0.6-0.1-1.2-0.4-1.9-0.7v9.5h-6V20.6
|
||||
H263.1z M273.4,25c-1.2,0-2.1,0.3-2.8,0.9s-1.2,1.5-1.6,2.7v9.7c0.4,0.3,0.9,0.6,1.4,0.8s1.2,0.3,2,0.3c1.7,0,3-0.6,3.9-1.8
|
||||
s1.3-3.2,1.3-6.1c0-2-0.3-3.6-1-4.7C276,25.6,274.9,25,273.4,25z"/>
|
||||
<path class="st3" d="M301.6,43.8V30.6c0-1.9-0.3-3.3-0.8-4.1s-1.5-1.3-2.9-1.3c-1,0-2,0.3-2.8,1c-0.9,0.7-1.4,1.6-1.7,2.7v14.8h-6
|
||||
V11.3h6v11.9h0.2c0.7-1,1.7-1.8,2.7-2.4c1.1-0.6,2.5-0.9,4.1-0.9c1.2,0,2.2,0.2,3.1,0.5s1.7,0.8,2.3,1.5s1.1,1.7,1.3,2.9
|
||||
s0.4,2.7,0.4,4.5v14.5C307.5,43.8,301.6,43.8,301.6,43.8z"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 6 KiB |
|
@ -386,5 +386,35 @@
|
|||
"picture": "https://pbs.twimg.com/profile_images/675305310056259586/JFaKTdR9_400x400.jpg",
|
||||
"quote": "I was quickly able to run the app on @Hetzner_Online! One thing I used to provision SSL was @caddyserver's #certmagic. Yes, It's magic ✨.",
|
||||
"link": "https://twitter.com/paganotoni/status/1757042179535503784"
|
||||
},
|
||||
{
|
||||
"username": "2RM60Z",
|
||||
"picture": "https://styles.redditmedia.com/t5_n97t9/styles/profileIcon_snoo-nftv2_bmZ0X2VpcDE1NToxMzdfYzhkM2EzYTgzYmRlNWRhZDA2ZDQzNjY5NGUzZTIyYWMzZTY0ZDU3N180OTI5Nzcw_rare_4ab84a19-b119-4455-8cff-563873a71268-headshot.png?width=64&height=64&frame=1&auto=webp&crop=64:64,smart&s=28f2fdbbce5b1d946e43dd36cecf8d594dbcff2e",
|
||||
"quote": "I switched [to Caddy] about a month ago. It really is so much more easy. Most of the things you mention are done by default and do not need any configuration whatsoever.",
|
||||
"link": "https://www.reddit.com/r/selfhosted/comments/1aepuvr/comment/kk9vayt/"
|
||||
},
|
||||
{
|
||||
"username": "that_czech_dude",
|
||||
"quote": "Caddy is, IMHO, the future. It's got all batteries includes and when it's going to get k8s ingress parity with nginx, or anything similar to nginx-proxy-manager, it will be unstoppable. You can literally leave Caddy running for years and just update it with package manager and not worry about TLS certs anymore. That stability is not guaranteed with certbot, it's just yet another daemon service you need to take care of and it broke for me multiple times. Much love to mholt and his team.",
|
||||
"link": "https://www.reddit.com/r/linuxadmin/comments/1aepv6g/comment/kkb46kz/"
|
||||
},
|
||||
{
|
||||
"username": "prairievoice",
|
||||
"picture": "https://styles.redditmedia.com/t5_uekn0/styles/profileIcon_6epo2g1awjhc1.jpg?width=64&height=64&frame=1&auto=webp&crop=64:64,smart&s=c6caa42b862fd43c6a9e2bc8105ba5ccd468bff0",
|
||||
"quote": "I've been migrating everything to Caddy. Its absolutely fantastic.",
|
||||
"link": "https://www.reddit.com/r/linuxadmin/comments/1aepv6g/comment/ko82ejk/"
|
||||
},
|
||||
{
|
||||
"username": "-quakeguy-",
|
||||
"picture": "https://styles.redditmedia.com/t5_j3yov/styles/profileIcon_snoo247a5811-9e53-46df-b68e-4c00e9e38eed-headshot.png?width=64&height=64&frame=1&auto=webp&crop=64:64,smart&s=f94656080d5924377a7bcabacf6adb5045dd24cd",
|
||||
"quote": "I really love how the entirety of caddy config for a single site is usually a grand total of 2-3 lines and that's it. Theres a lot to be said about sane defaults that are always there, but out of the way of cluttering your config.",
|
||||
"link": "https://www.reddit.com/r/linuxadmin/comments/1aepv6g/comment/kobjm4b/"
|
||||
},
|
||||
{
|
||||
"name": "Max Kovalevskii",
|
||||
"username": "@maxoidIO",
|
||||
"picture": "https://pbs.twimg.com/profile_images/1743317837135884288/LGlh3GTp_400x400.jpg",
|
||||
"quote": "Website is live! 🤯 I didn't expect it to be SO fast to up website with @caddyserver. Doing the same with apache/nginx before feels like a nightmare...",
|
||||
"link": "https://twitter.com/maxoidIO/status/1768652388297252881"
|
||||
}
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue