diff --git a/src/docs/markdown/api.md b/src/docs/markdown/api.md index d2d846d..ecb12e8 100644 --- a/src/docs/markdown/api.md +++ b/src/docs/markdown/api.md @@ -334,7 +334,7 @@ Returns the current status of the configured reverse proxy upstreams (backends) Each entry in the JSON array is a configured [upstream](/docs/json/apps/http/servers/routes/handle/reverse_proxy/upstreams/) stored in the global upstream pool. -- **address** is the dial address of the upstream. For SRV upstreams, this is the `lookup_srv` DNS name. +- **address** is the dial address of the upstream. - **num_requests** is the amount of active requests currently being handled by the upstream. - **fails** the current number of failed requests remembered, as configured by passive health checks. diff --git a/src/docs/markdown/caddyfile/concepts.md b/src/docs/markdown/caddyfile/concepts.md index 25ea240..ae86340 100644 --- a/src/docs/markdown/caddyfile/concepts.md +++ b/src/docs/markdown/caddyfile/concepts.md @@ -7,6 +7,9 @@ title: Caddyfile Concepts This document will help you learn about the HTTP Caddyfile in detail. 1. [Structure](#structure) + - [Blocks](#blocks) + - [Directives](#directives) + - [Tokens and quotes](#tokens-and-quotes) 2. [Addresses](#addresses) 3. [Matchers](#matchers) 4. [Placeholders](#placeholders) @@ -156,6 +159,25 @@ directive "first line second line" ``` +Heredocs are also supported: + +```caddy +example.com { + respond < + Foo + Foo + + HTML 200 +} +``` + +The opening heredoc marker must start with `<<`, followed by any text (uppercase letters recommended). The closing heredoc marker must be the same text (in the above example, `HTML`). + +The closing marker can be indented, which causes every line of text to have that much indentation stripped (inspired by [PHP](https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc)) which is nice for readability inside [blocks](#blocks) while giving great control of the whitespace in the token text. The trailing newline is also stripped, but can be retained by adding an extra blank line before the closing marker. + +Additional tokens may follow the closing marker as arguments to the directive (such as in the example above, the status code `200`). + ## Addresses @@ -236,6 +258,7 @@ You can use any [Caddy placeholders](/docs/conventions#placeholders) in the Cadd | 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.*}` | @@ -297,7 +320,7 @@ You can pass arguments to imported configuration and use them like so: ```caddy (snippet) { - respond "Yahaha! You found {args.0}!" + respond "Yahaha! You found {args[0]}!" } a.example.com { diff --git a/src/docs/markdown/caddyfile/directives/acme_server.md b/src/docs/markdown/caddyfile/directives/acme_server.md index e1c970b..84ff39a 100644 --- a/src/docs/markdown/caddyfile/directives/acme_server.md +++ b/src/docs/markdown/caddyfile/directives/acme_server.md @@ -18,10 +18,14 @@ Using ACME server defaults, ACME clients should simply be configured to use `htt ```caddy-d acme_server [] { - ca - lifetime + ca + lifetime + resolvers } ``` - **ca** specifies the ID of the certificate authority with which to sign certificates. The default is `local`, which is Caddy's default CA, intended for locally-used, self-signed certificates, which is most common in dev environments. For broader use, it is recommended to specify a different CA to avoid confusion. If the CA with the given ID does not already exist, it will be created. See the [PKI app global options](/docs/caddyfile/options#pki-options) to configure alternate CAs. + - **lifetime** (Default: `12h`) is a [duration](/docs/conventions#durations) which specifies the validity period for issued certificates. This value must be less than the lifetime of the [intermediate certificate](/docs/caddyfile/options#intermediate-lifetime) used for signing. It is not recommended to change this unless absolutely necessary. + +- **resolvers** are the addresses of DNS resolvers to use when looking up the TXT records for solving ACME DNS challenges. Accepts [network addresses](/docs/conventions#network-addresses) defaulting to UDP and port 53 unless specified. If the host is an IP address, it will be dialed directly to resolve the upstream server. If the 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. diff --git a/src/docs/markdown/caddyfile/directives/header.md b/src/docs/markdown/caddyfile/directives/header.md index 90025a4..cf96cad 100644 --- a/src/docs/markdown/caddyfile/directives/header.md +++ b/src/docs/markdown/caddyfile/directives/header.md @@ -12,12 +12,18 @@ By default, header operations are performed immediately unless any of the header ## Syntax ```caddy-d -header [] [[+|-|?] [|] []] { +header [] [[+|-|?|>] [|] []] { # Replace - # Add or Set - [+] + # Add + + + + # Set + + + # Set with defer + > # Delete - @@ -37,13 +43,15 @@ header [] [[+|-|?] [|] []] { Prefix with `?` to set a default value for the field. The field is only written if it doesn't yet exist. + Prefix with `>` to set the field, and enable `defer`, as a shortcut. + - **<value>** is the header field value, if adding or setting a field. - **<find>** is the substring or regular expression to search for. - **<replace>** is the replacement value; required if performing a search-and-replace. -- **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 `-`, or when setting a default value with `?`. +- **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. @@ -106,3 +114,10 @@ Set a default cache expiration if upstream doesn't define one: header ?Cache-Control "max-age=3600" reverse_proxy upstream:443 ``` + +To override the cache expiration that a proxy upstream had set for paths starting with `/no-cache`; enabling `defer` is necessary to ensure the header is set _after_ the proxy writes its headers: + +```caddy-d +header /no-cache* >Cache-Control nocache +reverse_proxy upstream:443 +``` diff --git a/src/docs/markdown/caddyfile/directives/import.md b/src/docs/markdown/caddyfile/directives/import.md index cfa849f..6aa7be1 100644 --- a/src/docs/markdown/caddyfile/directives/import.md +++ b/src/docs/markdown/caddyfile/directives/import.md @@ -23,7 +23,16 @@ import [] If the pattern is a filename or glob, it is always relative to the file the `import` appears in. If using a glob pattern `*` as the final path segment, hidden files (i.e. files starting with a `.`) are ignored. To import hidden files, use `.*` as the final segment. -- **<args...>** is an optional list of arguments to pass to the imported tokens. They can be used with a placeholder of the form `{args.N}` where `N` is the 0-based positional index of the parameter. This placeholder is a special case and is evaluated at parse-time, not run-time. +- **<args...>** is an optional list of arguments to pass to the imported tokens. This placeholder is a special case and is evaluated at Caddyfile-parse-time, not at run-time. They can be used in various forms, similarly to [Go's slice syntax](https://gobyexample.com/slices): + - `{args[n]}` where `n` is the 0-based positional index of the parameter + - `{args[:]}` where all the arguments are inserted + - `{args[:m]}` where the arguments before `m` are inserted + - `{args[n:]}` where the arguments beginning with `n` are inserted + - `{args[n:m]}` where the arguments in the range between `n` and `m` are inserted + + For the forms that insert many tokens, the placeholder _must_ be a [token](/docs/caddyfile/concepts#tokens-and-quotes) on its own, it cannot be part of another token. In other words, it must have spaces around it, and cannot be in quotes. + + Note that prior to v2.7.0, the syntax was `{args.N}` but this form was deprecated in favor of the more flexible syntax above. ## Examples @@ -38,8 +47,8 @@ Import a snippet that sets CORS headers using an import argument: ```caddy (cors) { - @origin header Origin {args.0} - header @origin Access-Control-Allow-Origin "{args.0}" + @origin header Origin {args[0]} + header @origin Access-Control-Allow-Origin "{args[0]}" header @origin Access-Control-Allow-Methods "OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" } @@ -47,3 +56,32 @@ example.com { import cors example.com } ``` + +Import a snippet which takes a list of proxy upstreams as arguments: + +```caddy +(https-proxy) { + reverse_proxy {args[:]} { + transport http { + tls + } + } +} + +example.com { + import https-proxy 10.0.0.1 10.0.0.2 10.0.0.3 +} +``` + +Import a snippet which creates a proxy with a prefix rewrite rule as the first argument: + +```caddy +(proxy-rewrite) { + rewrite * {args[0]}{uri} + reverse_proxy {args[1:]} +} + +example.com { + import proxy-rewrite /api 10.0.0.1 10.0.0.2 10.0.0.3 +} +``` diff --git a/src/docs/markdown/caddyfile/directives/respond.md b/src/docs/markdown/caddyfile/directives/respond.md index 2e7d5c5..7bf339b 100644 --- a/src/docs/markdown/caddyfile/directives/respond.md +++ b/src/docs/markdown/caddyfile/directives/respond.md @@ -54,3 +54,14 @@ respond /secret/* "Access denied" 403 { close } ``` + +Write an HTML response, using [heredoc syntax](/docs/caddyfile/concepts#heredocs) to control whitespace: + +```caddy-d +respond < + Foo + Foo + + HTML 200 +``` diff --git a/src/docs/markdown/caddyfile/directives/reverse_proxy.md b/src/docs/markdown/caddyfile/directives/reverse_proxy.md index f6a8ce0..2d2bb66 100644 --- a/src/docs/markdown/caddyfile/directives/reverse_proxy.md +++ b/src/docs/markdown/caddyfile/directives/reverse_proxy.md @@ -141,6 +141,7 @@ Static upstream addresses can take the form of a URL that contains only scheme a - `example.com` - `unix//var/php.sock` - `unix+h2c//var/grpc.sock` +- `localhost:8001-8006` 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. @@ -152,7 +153,7 @@ 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 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. +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. @@ -203,6 +204,7 @@ Retrieves upstreams from A/AAAA DNS records. resolvers dial_timeout dial_fallback_delay + versions ipv4|ipv6 } ``` @@ -212,6 +214,7 @@ Retrieves upstreams from A/AAAA DNS records. - **resolvers** is the list of DNS resolvers to override system resolvers. - **dial_timeout** is the timeout for dialing the query. - **dial_fallback_delay** is how long to wait before spawning an RFC 6555 Fast Fallback connection. Default: `300ms` +- **versions** is the list of IP versions to resolve for. Default: `ipv4 ipv6` which correspond to both A and AAAA records respectively. ##### Multi @@ -237,25 +240,31 @@ Load balancing is used whenever more than one upstream is defined. For policies that involve hashing, the [highest-random-weight (HRW)](https://en.wikipedia.org/wiki/Rendezvous_hashing) algorithm is used to ensure that a client or request with the same hash key is mapped to the same upstream, even if the list of upstreams change. + Some policies support fallback as an option, if noted, in which case they take a [block](/docs/caddyfile/concepts#blocks) with `fallback ` which takes another load balancing policy. For those policies, the default fallback is `random`. Configuring a fallback allows using a secondary policy if the primary does not select one, allowing for powerful combinations. Fallbacks can be nested multiple times if desired. For example, `header` can be used as primary to allow for developers to choose a specific upstream, with a fallback of `first` for all other connections to implement primary/secondary failover. + - `random` randomly chooses an upstream - `random_choose ` selects two or more upstreams randomly, then chooses one with least load (`n` is usually 2) - - `first` chooses the first available upstream, from the order they are defined in the config + - `first` chooses the first available upstream, from the order they are defined in the config, allowing for primary/secondary failover; remember to enable health checks along with this, otherwise failover will not occur - `round_robin` iterates each upstream in turn - `least_conn` choose upstream with fewest number of current requests; if more than one host has the least number of requests, then one of those hosts is chosen at random - - `ip_hash` maps the client IP to a sticky upstream + - `ip_hash` maps the remote IP (the immediate peer) to a sticky upstream + + - `client_ip_hash` maps the client IP to a sticky upstream; this is best paired with the [`servers > trusted_proxies` global option](/docs/caddyfile/options#trusted-proxies) which enables real client IP parsing, otherwise it behaves the same as `ip_hash` - `uri_hash` maps the request URI (path and query) to a sticky upstream - - `header [field]` maps a request header to a sticky upstream, by hashing the header value; if the specified header field is not present, a random upstream is selected + - `query [key]` maps a request query to a sticky upstream, by hashing the query value; if the specified key is not present, the fallback policy will be used to select an upstream (`random` by default) - - `cookie [ []]` on the first request from a client (when there's no cookie), a random upstream is selected, and a `Set-Cookie` header is added to the response (default cookie name is `lb` if not specified). The cookie value is the upstream dial address of the chosen upstream, hashed with HMAC-SHA256 (using `` as the shared secret, empty string if not specified). + - `header [field]` maps a request header to a sticky upstream, by hashing the header value; if the specified header field is not present, the fallback policy will be used to select an upstream (`random` by default) + + - `cookie [ []]` on the first request from a client (when there's no cookie), the fallback policy will be used to select an upstream (`random` by default), and a `Set-Cookie` header is added to the response (default cookie name is `lb` if not specified). The cookie value is the upstream dial address of the chosen upstream, hashed with HMAC-SHA256 (using `` as the shared secret, empty string if not specified). - On subsequent requests where the cookie is present, the cookie value will be mapped to the same upstream if it's available; if not available or not found, a new random upstream is selected and the cookie is added to the response. + On subsequent requests where the cookie is present, the cookie value will be mapped to the same upstream if it's available; if not available or not found, a new upstream is selected with the fallback policy, and the cookie is added to the response. If you wish to use a particular upstream for debugging purposes, you may hash the upstream address with the secret, and set the cookie in your HTTP client (browser or otherwise). For example, with PHP, you could run the following to compute the cookie value, where `10.1.0.10:8080` is the address of one of your upstreams, and `secret` is your configured secret. ```php @@ -364,12 +373,18 @@ To delete a request header, preventing it from reaching the backend: header_up -Some-Header ``` -To delete all matching request, using a suffix match: +To delete all matching request headers, using a suffix match: ```caddy-d header_up -Some-* ``` +To delete _all_ request headers, to be able to individually add the ones you want (not recommended): + +```caddy-d +header_up -* +``` + To perform a regular expression replacement on a request header: ```caddy-d @@ -391,7 +406,7 @@ By default, Caddy passes thru incoming headers—including `Host`—to t If Caddy is not the first server being connected to by your clients (for example when a CDN is in front of Caddy), you may configure `trusted_proxies` with a list of IP ranges (CIDRs) from which incoming requests are trusted to have sent good values for these headers. -It is recommended that you configure this via the [`servers > trusted_proxies` global option](/docs/caddyfile/options#trusted-proxies) so that this applies to all proxy handlers in your server, without repetition. +It is strongly recommended that you configure this via the [`servers > trusted_proxies` global option](/docs/caddyfile/options#trusted-proxies) instead of in the proxy, so that this applies to all proxy handlers in your server, and this has the benefit of enabling client IP parsing.