From d63d4d5aa85028d24af9fe30f92292fa5585ff4f Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Sat, 25 Oct 2025 01:46:35 +0300 Subject: [PATCH 01/20] command-line: document stdin input for `adapt` Signed-off-by: Mohammed Al Sahaf --- src/docs/markdown/command-line.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/markdown/command-line.md b/src/docs/markdown/command-line.md index e61a1a30..672b172c 100644 --- a/src/docs/markdown/command-line.md +++ b/src/docs/markdown/command-line.md @@ -119,7 +119,7 @@ The `--flags` may have a single-letter shortcut like `-f`. Adapts a configuration to Caddy's native JSON config structure and writes the output to stdout, along with any warnings to stderr, then exits. -`--config` is the path to the config file. If omitted, assumes `Caddyfile` in current directory if it exists; otherwise, this flag is required. +`--config` is the path to the config file. If omitted, assumes `Caddyfile` in current directory if it exists; otherwise, this flag is required. If you wish to use stdin instead of a regular file, use - as the path. `--adapter` specifies the config adapter to use; default is `caddyfile`. From 4582cdbff17c70b714f344f5ebc6546445fc0874 Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Sat, 25 Oct 2025 01:55:20 +0300 Subject: [PATCH 02/20] command-line: document `bcrypt-cost` Signed-off-by: Mohammed Al Sahaf --- src/docs/markdown/command-line.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/docs/markdown/command-line.md b/src/docs/markdown/command-line.md index 672b172c..96a364d6 100644 --- a/src/docs/markdown/command-line.md +++ b/src/docs/markdown/command-line.md @@ -248,6 +248,7 @@ Formats or prettifies a Caddyfile, then exits. The result is printed to stdout u
caddy hash-password
 	[-p, --plaintext <password>]
 	[-a, --algorithm <name>]
+ [--bcrypt-cost <cost>] Convenient way to hash a plaintext password. The resulting hash is written to stdout as a format usable directly in your Caddy config. @@ -255,7 +256,7 @@ Convenient way to hash a plaintext password. The resulting hash is written to st `--algorithm` may be `bcrypt` or any installed hash algorithm. Default is `bcrypt`. - +`--bcrypt-cost` is the hashing cost for bcrypt algorithm. Default is `14`. ### `caddy help` From 4546f4889c56eb76412466e4516b122387f5721c Mon Sep 17 00:00:00 2001 From: Steffen Busch <37350514+steffenbusch@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:38:16 +0100 Subject: [PATCH 03/20] docs: mention placeholders in reverse_proxy context (#506) Co-authored-by: Francis Lavoie --- src/docs/markdown/conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/markdown/conventions.md b/src/docs/markdown/conventions.md index 9a62741a..29d83e76 100644 --- a/src/docs/markdown/conventions.md +++ b/src/docs/markdown/conventions.md @@ -90,7 +90,7 @@ Placeholders are a similar idea to variables in other software. For example, [ng 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. +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. When a request passes through the [`reverse_proxy` handler](/docs/json/apps/http/servers/routes/handle/reverse_proxy/#docs), the handler sets several proxy-specific placeholders. These placeholders may be referenced during proxying as well as afterwards (in `handle_response`), for example when setting response headers or enriching access logs. The following placeholders are always available (global): From f4d9f9ad230ad1b316561027815b298c5d5ea165 Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Fri, 31 Oct 2025 00:53:36 +0300 Subject: [PATCH 04/20] verifiers: document `leaf` verifier and loaders Signed-off-by: Mohammed Al Sahaf --- src/docs/markdown/caddyfile/directives/tls.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/docs/markdown/caddyfile/directives/tls.md b/src/docs/markdown/caddyfile/directives/tls.md index a64dab3d..5212b0df 100644 --- a/src/docs/markdown/caddyfile/directives/tls.md +++ b/src/docs/markdown/caddyfile/directives/tls.md @@ -266,6 +266,42 @@ The `http` module obtains the trusted certificates from HTTP endpoints. The `end - `once` allows a remote server to request renegotiation once per connection. - `freely` allows a remote server to repeatedly request renegotiation. +### Verifiers + +Client certificate verifier modules are executed after validating they are issued from a trusted certificate authority, if the `trust_pool` is configured. The one verifier, currently, shipped in standard Caddy is `leaf`. + +#### Leaf + +The `leaf` verifier checks if the client certificate is one of a defined set of permitted certificates. The certificate set is loaded using [loader](https://caddyserver.com/docs/modules/tls.client_auth.verifier.leaf#leaf_certs_loaders) modules. + +##### Loaders + +Standard Caddy distribution bundles 4 loaders, 3 of them are available in Caddyfile. + +###### File + +The `file` loader loads the set of certificates from specified PEM files. + +```caddy-d +... file +``` + +###### Folder + +The `folder` loader recursively traverses the named directories searching for PEM files to be loaded as accepted client certificates. + +```caddy-d +... folder +``` + +###### PEM + +The `pem` loader accepts certificates inlined in the Caddyfile in PEM format. + +```caddy-d +... pem +``` + ### Issuers These issuers come standard with the `tls` directive: From ab9544f1f35ca5d54100c70c61ef57acdbb22737 Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Sat, 25 Oct 2025 01:46:35 +0300 Subject: [PATCH 05/20] command-line: document stdin input for `adapt` Signed-off-by: Mohammed Al Sahaf --- src/docs/markdown/command-line.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/markdown/command-line.md b/src/docs/markdown/command-line.md index 3e915f9b..c534fc51 100644 --- a/src/docs/markdown/command-line.md +++ b/src/docs/markdown/command-line.md @@ -119,7 +119,7 @@ The `--flags` may have a single-letter shortcut like `-f`. Adapts a configuration to Caddy's native JSON config structure and writes the output to stdout, along with any warnings to stderr, then exits. -`--config` is the path to the config file. If omitted, assumes `Caddyfile` in current directory if it exists; otherwise, this flag is required. +`--config` is the path to the config file. If omitted, assumes `Caddyfile` in current directory if it exists; otherwise, this flag is required. If you wish to use stdin instead of a regular file, use - as the path. `--adapter` specifies the config adapter to use; default is `caddyfile`. From 3353b4e5bd3144a660902327c48c2e792889b70f Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Sat, 25 Oct 2025 01:55:20 +0300 Subject: [PATCH 06/20] command-line: document `bcrypt-cost` Signed-off-by: Mohammed Al Sahaf --- src/docs/markdown/command-line.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/docs/markdown/command-line.md b/src/docs/markdown/command-line.md index c534fc51..9349d823 100644 --- a/src/docs/markdown/command-line.md +++ b/src/docs/markdown/command-line.md @@ -248,6 +248,7 @@ Formats or prettifies a Caddyfile, then exits. The result is printed to stdout u
caddy hash-password
 	[-p, --plaintext <password>]
 	[-a, --algorithm <name>]
+ [--bcrypt-cost <cost>] Convenient way to hash a plaintext password. The resulting hash is written to stdout as a format usable directly in your Caddy config. @@ -255,7 +256,7 @@ Convenient way to hash a plaintext password. The resulting hash is written to st `--algorithm` may be `bcrypt` or any installed hash algorithm. Default is `bcrypt`. - +`--bcrypt-cost` is the hashing cost for bcrypt algorithm. Default is `14`. ### `caddy help` From 2b58d643b08df06de83d8a48f8a41420fe4985bd Mon Sep 17 00:00:00 2001 From: Steffen Busch <37350514+steffenbusch@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:38:16 +0100 Subject: [PATCH 07/20] docs: mention placeholders in reverse_proxy context (#506) Co-authored-by: Francis Lavoie --- src/docs/markdown/conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/markdown/conventions.md b/src/docs/markdown/conventions.md index 9a62741a..29d83e76 100644 --- a/src/docs/markdown/conventions.md +++ b/src/docs/markdown/conventions.md @@ -90,7 +90,7 @@ Placeholders are a similar idea to variables in other software. For example, [ng 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. +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. When a request passes through the [`reverse_proxy` handler](/docs/json/apps/http/servers/routes/handle/reverse_proxy/#docs), the handler sets several proxy-specific placeholders. These placeholders may be referenced during proxying as well as afterwards (in `handle_response`), for example when setting response headers or enriching access logs. The following placeholders are always available (global): From 34de5801d6298d78eca6f828245c12bad20de875 Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Fri, 31 Oct 2025 00:53:36 +0300 Subject: [PATCH 08/20] verifiers: document `leaf` verifier and loaders Signed-off-by: Mohammed Al Sahaf --- src/docs/markdown/caddyfile/directives/tls.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/docs/markdown/caddyfile/directives/tls.md b/src/docs/markdown/caddyfile/directives/tls.md index a64dab3d..5212b0df 100644 --- a/src/docs/markdown/caddyfile/directives/tls.md +++ b/src/docs/markdown/caddyfile/directives/tls.md @@ -266,6 +266,42 @@ The `http` module obtains the trusted certificates from HTTP endpoints. The `end - `once` allows a remote server to request renegotiation once per connection. - `freely` allows a remote server to repeatedly request renegotiation. +### Verifiers + +Client certificate verifier modules are executed after validating they are issued from a trusted certificate authority, if the `trust_pool` is configured. The one verifier, currently, shipped in standard Caddy is `leaf`. + +#### Leaf + +The `leaf` verifier checks if the client certificate is one of a defined set of permitted certificates. The certificate set is loaded using [loader](https://caddyserver.com/docs/modules/tls.client_auth.verifier.leaf#leaf_certs_loaders) modules. + +##### Loaders + +Standard Caddy distribution bundles 4 loaders, 3 of them are available in Caddyfile. + +###### File + +The `file` loader loads the set of certificates from specified PEM files. + +```caddy-d +... file +``` + +###### Folder + +The `folder` loader recursively traverses the named directories searching for PEM files to be loaded as accepted client certificates. + +```caddy-d +... folder +``` + +###### PEM + +The `pem` loader accepts certificates inlined in the Caddyfile in PEM format. + +```caddy-d +... pem +``` + ### Issuers These issuers come standard with the `tls` directive: From f6e03a7acf4769ea678bf0249deec9cf1773dfc4 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 12 Nov 2025 01:47:54 -0500 Subject: [PATCH 09/20] Log sampling config --- src/docs/markdown/caddyfile/directives/log.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/docs/markdown/caddyfile/directives/log.md b/src/docs/markdown/caddyfile/directives/log.md index 234f65a7..cc32c056 100644 --- a/src/docs/markdown/caddyfile/directives/log.md +++ b/src/docs/markdown/caddyfile/directives/log.md @@ -71,6 +71,11 @@ log [] { output ... format ... level + sampling { + interval + first + thereafter + } } ``` @@ -98,6 +103,15 @@ log [] { Note that access logs currently only emit `INFO` and `ERROR` level logs. +- **sampling** configures log sampling to reduce log volume. If sampling is specified, then it is enabled, with the defaults below taking effect. Omitting this disables sampling. + + - **interval** is the [duration window](/docs/conventions#durations) over which to conduct sampling. Default: `1s` (disabled). + + - **first** is how many logs to keep within a given level and message for a each interval. Default: `100`. + + - **thereafter** is how many logs to skip in each interval after the first kept logs. Default: `100`. + + For example, with `interval 1s`, `first 5`, and `thereafter 10`, in each 10-second interval the first 5 log entries will be kept, then it will allow through every 10th log entry with the same level and message within that second. ### Output modules @@ -605,3 +619,17 @@ foo.example.com { log foo } ``` + + To reduce log volume with sampling, for example to keep the first 5 requests per second, then 1 out of every 10 requests thereafter: + +```caddy +example.com { + log { + sampling { + interval 1s + first 5 + thereafter 10 + } + } +} +``` From d033f7d06dcefbea74b9ac3ce9bd3454d08b8a2b Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 12 Nov 2025 02:06:21 -0500 Subject: [PATCH 10/20] tls.context namespace --- src/docs/markdown/extending-caddy/namespaces.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/docs/markdown/extending-caddy/namespaces.md b/src/docs/markdown/extending-caddy/namespaces.md index 556ab5c9..02499a6f 100644 --- a/src/docs/markdown/extending-caddy/namespaces.md +++ b/src/docs/markdown/extending-caddy/namespaces.md @@ -46,5 +46,6 @@ tls.issuance | [`certmagic.Issuer`](https://pkg.go.dev/github.com/caddyserver/ce 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 | ⚠️ Experimental tls.stek | [`caddytls.STEKProvider`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#STEKProvider) | TLS session ticket key source +tls.context | [`caddytls.HandshakeContext`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#HandshakeContext) | Intercept GetCertificate context | ⚠️ Experimental Namespaces marked as "Experimental" are subject to change. (Please develop with them so we can finalize their interfaces!) From fe412b99a9a8febd9cf9c380eea09a5c65b00e94 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 12 Nov 2025 02:11:44 -0500 Subject: [PATCH 11/20] keepalive globals --- src/docs/markdown/caddyfile/options.md | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/docs/markdown/caddyfile/options.md b/src/docs/markdown/caddyfile/options.md index d208eba1..07932c70 100644 --- a/src/docs/markdown/caddyfile/options.md +++ b/src/docs/markdown/caddyfile/options.md @@ -126,8 +126,12 @@ Possible options are (click on each option to jump to its documentation): idle } keepalive_interval + keepalive_idle + keepalive_count + trusted_proxies ... client_ip_headers + trace max_header_size enable_full_duplex @@ -938,6 +942,31 @@ The interval at which TCP keepalive packets are sent to keep the connection aliv ``` +##### `keepalive_idle` + +The duration a connection must be idle before TCP keepalive packets are sent when no other data is being transmitted. Defaults to `15s`. + +```caddy +{ + servers { + keepalive_idle 1m + } +} +``` + + +##### `keepalive_count` + +The maximum number of TCP keepalive packets to send before considering the connection dead. Defaults to `9`. + +```caddy +{ + servers { + keepalive_count 5 + } +} +``` + ##### `trusted_proxies` From ae2e8dee60482a711115be70be45de6083821456 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 12 Nov 2025 02:16:57 -0500 Subject: [PATCH 12/20] file server file_limit --- src/docs/markdown/caddyfile/directives/file_server.md | 3 +++ src/docs/markdown/command-line.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/docs/markdown/caddyfile/directives/file_server.md b/src/docs/markdown/caddyfile/directives/file_server.md index 7f2db513..ed6568e6 100644 --- a/src/docs/markdown/caddyfile/directives/file_server.md +++ b/src/docs/markdown/caddyfile/directives/file_server.md @@ -38,6 +38,7 @@ file_server [] [browse] { browse [] { reveal_symlinks sort [] + file_limit } precompressed [] status @@ -64,6 +65,8 @@ file_server [] [browse] { - **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. + - **file_limit** sets a maximum number of files to show in directory listings. Default: `10000`. If the number of files exceeds this limit, only the first N files will be shown, where N is the specified limit. + - **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. diff --git a/src/docs/markdown/command-line.md b/src/docs/markdown/command-line.md index 9349d823..dc8dbf1c 100644 --- a/src/docs/markdown/command-line.md +++ b/src/docs/markdown/command-line.md @@ -194,6 +194,7 @@ Prints the environment as seen by caddy, then exits. Can be useful when debuggin [-t, --templates] [--access-log] [-v, --debug] + [-f, --file-limit <number>] [--no-compress] [-p, --precompressed] @@ -215,6 +216,8 @@ Spins up a simple but production-ready static file server. `--debug` enables verbose logging. +`--file-limit` sets a maximum number of files to show in directory listings. Default: `10000`. If the number of files exceeds this limit, only the first N files will be shown, where N is the specified limit. + `--no-compress` disables compression. By default, Zstandard and Gzip compression are enabled. `--precompressed` specifies encoding formats to search for precompressed sidecar files. Can be repeated for multiple formats. See the [file_server directive](/docs/caddyfile/directives/file_server#precompressed) for more information. From 382a3a3dfbb5b22796f6d6764d7b93df107ffbfb Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 12 Nov 2025 02:27:25 -0500 Subject: [PATCH 13/20] vars CEL and CEL placeholder escape --- src/docs/markdown/caddyfile/matchers.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/docs/markdown/caddyfile/matchers.md b/src/docs/markdown/caddyfile/matchers.md index 25b5fc2e..3b139bd8 100644 --- a/src/docs/markdown/caddyfile/matchers.md +++ b/src/docs/markdown/caddyfile/matchers.md @@ -244,10 +244,10 @@ expression By any [CEL (Common Expression Language)](https://github.com/google/cel-spec) expression that returns `true` or `false`. -Caddy [placeholders](/docs/conventions#placeholders) (or [Caddyfile shorthands](/docs/caddyfile/concepts#placeholders)) may be used in these CEL expressions, as they are preprocessed and converted to regular CEL function calls before being interpreted by the CEL environment. - Most other request matchers can also be used in expressions as functions, which allows for more flexibility for boolean logic than outside expressions. See the documentation for each matcher for the supported syntax within CEL expressions. +Caddy [placeholders](/docs/conventions#placeholders) (or [Caddyfile shorthands](/docs/caddyfile/concepts#placeholders)) may be used in these CEL expressions, as they are preprocessed and converted to regular CEL function calls before being interpreted by the CEL environment. If a placeholder should be passed as a string argument to a matcher function, then the leading `{` should be escaped with a backslash `\` so that it is not preprocessed, for example `file('\{path}.md')`. + For convenience, the matcher name may be omitted if defining a named matcher that consists solely of a CEL expression. The CEL expression must be [quoted](/docs/caddyfile/concepts#tokens-and-quotes) (backticks or heredocs recommended). This reads quite nicely: ```caddy-d @@ -861,6 +861,9 @@ In a [CEL expression](#expression), it would look like this: ```caddy-d vars + +expression vars({'': ''}) +expression vars({'': ['']}) ``` By the value of a variable in the request context, or the value of a placeholder. Multiple values may be specified to match any of those possible values (OR'ed). @@ -898,12 +901,21 @@ example.com { } ``` +In a [CEL expression](#expression), it would look like this: + +```caddy-d +@magic `vars({'magic_number': ['3', '5']})` +``` + --- ### vars_regexp ```caddy-d vars_regexp [] + +expression vars_regexp('', '', '') +expression vars_regexp('', '') ``` Like [`vars`](#vars), but supports regular expressions. @@ -936,3 +948,9 @@ This can be simplified by omitting the name, which will be inferred from the nam ```caddy-d @magic vars_regexp {magic_number} ^(4.*) ``` + +In a [CEL expression](#expression), it would look like this: + +```caddy-d +@magic `vars_regexp('magic_number', '^(4.*)')` +``` From 39b12c77578edbb682dfe1d8193434ed798c5005 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 12 Nov 2025 02:39:15 -0500 Subject: [PATCH 14/20] RequestMatcherWithError namespace, cleanup table style --- .../markdown/extending-caddy/namespaces.md | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/docs/markdown/extending-caddy/namespaces.md b/src/docs/markdown/extending-caddy/namespaces.md index 02499a6f..7392041d 100644 --- a/src/docs/markdown/extending-caddy/namespaces.md +++ b/src/docs/markdown/extending-caddy/namespaces.md @@ -12,40 +12,47 @@ 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>." + + Namespace | Expected Interface Type | Description | Notes --------- | ------------- | ----------- | ---------- | | [`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)

[`caddy.AdminHandler`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#AdminHandler) | Registers HTTP routes for admin

HTTP handler middleware | -caddy.config_loaders | [`caddy.ConfigLoader`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#ConfigLoader) | Loads a config | ⚠️ Experimental -caddy.fs | [`fs.FS`](https://pkg.go.dev/io/fs#FS) | Virtual file system | ⚠️ Experimental +caddy.config_loaders | [`caddy.ConfigLoader`](https://pkg.go.dev/github.com/caddyserver/caddy/v2#ConfigLoader) | Loads a config | ⚠️ Experimental +caddy.fs | [`fs.FS`](https://pkg.go.dev/io/fs#FS) | Virtual file system | ⚠️ Experimental 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#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 | ⚠️ Experimental +events.handlers | [`caddyevents.Handler`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyevents#Handler) | Event handlers | ⚠️ Experimental http.authentication.hashes | [`caddyauth.Comparer`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth#Comparer)

[`caddyauth.Hasher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth#Hasher) | Password comparers

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)

[`encode.Encoder`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/encode#Encoder) | Creates an encoder (compression)

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#RequestMatcher)

[`caddyhttp.CELLibraryProducer`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp#CELLibraryProducer) | HTTP request matchers

Support for CEL expressions |

(Optional) +http.matchers | [`caddyhttp.RequestMatcher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp#RequestMatcher)

[`caddyhttp.RequestMatcherWithError`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp#RequestMatcherWithError)

[`caddyhttp.CELLibraryProducer`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp#CELLibraryProducer) | Request matcher (use WithError instead)

Request matcher with error short-circuit

Support for CEL expressions | ⚠️ Deprecated



(Optional) 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#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 | ⚠️ Experimental +http.reverse_proxy.upstreams | [`reverseproxy.UpstreamSource`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy#UpstreamSource) | Dynamic upstream source | ⚠️ Experimental 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.ech.publishers | [`caddytls.ECHPublisher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#ECHPublisher) | Publishes Encrypted ClientHello (ECH) configurations | ⚠️ Experimental -tls.get_certificate | [`certmagic.Manager`](https://pkg.go.dev/github.com/caddyserver/certmagic#Manager) | TLS certificate manager | ⚠️ Experimental +tls.ech.publishers | [`caddytls.ECHPublisher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#ECHPublisher) | Publishes Encrypted ClientHello (ECH) configurations | ⚠️ Experimental +tls.get_certificate | [`certmagic.Manager`](https://pkg.go.dev/github.com/caddyserver/certmagic#Manager) | TLS certificate manager | ⚠️ Experimental 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 | ⚠️ Experimental +tls.permission | [`caddytls.OnDemandPermission`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#OnDemandPermission) | Whether to obtain a cert for a domain | ⚠️ Experimental tls.stek | [`caddytls.STEKProvider`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#STEKProvider) | TLS session ticket key source -tls.context | [`caddytls.HandshakeContext`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#HandshakeContext) | Intercept GetCertificate context | ⚠️ Experimental +tls.context | [`caddytls.HandshakeContext`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls#HandshakeContext) | Intercept GetCertificate context | ⚠️ Experimental Namespaces marked as "Experimental" are subject to change. (Please develop with them so we can finalize their interfaces!) From 8912bb59a06b3f7cb69f34274f3d6e965dbe67c3 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 12 Nov 2025 02:45:03 -0500 Subject: [PATCH 15/20] {?query} placeholder --- src/docs/markdown/caddyfile/concepts.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/docs/markdown/caddyfile/concepts.md b/src/docs/markdown/caddyfile/concepts.md index 418957c2..56a8de15 100644 --- a/src/docs/markdown/caddyfile/concepts.md +++ b/src/docs/markdown/caddyfile/concepts.md @@ -343,11 +343,19 @@ You can use any placeholders in the Caddyfile, but for convenience you can also | `{hostport}` | `{http.request.hostport}` | | `{labels.*}` | `{http.request.host.labels.*}` | | `{method}` | `{http.request.method}` | +| `{orig_method}` | `{http.request.orig_method}` | +| `{orig_uri}` | `{http.request.orig_uri}` | +| `{orig_path}` | `{http.request.orig_uri.path}` | +| `{orig_dir}` | `{http.request.orig_uri.path.dir}` | +| `{orig_file}` | `{http.request.orig_uri.path.file}` | +| `{orig_query}` | `{http.request.orig_uri.query}` | +| `{orig_?query}` | `{http.request.orig_uri.prefixed_query}` | | `{path.*}` | `{http.request.uri.path.*}` | | `{path}` | `{http.request.uri.path}` | | `{port}` | `{http.request.port}` | | `{query.*}` | `{http.request.uri.query.*}` | | `{query}` | `{http.request.uri.query}` | +| `{?query}` | `{http.request.uri.prefixed_query}` | | `{re.*}` | `{http.regexp.*}` | | `{remote_host}` | `{http.request.remote.host}` | | `{remote_port}` | `{http.request.remote.port}` | From 07d842ca0e9348ecb5c0e9a8a2aa000a039d4c7c Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 12 Nov 2025 02:46:38 -0500 Subject: [PATCH 16/20] Escaped uri placeholders --- src/docs/markdown/caddyfile/concepts.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/docs/markdown/caddyfile/concepts.md b/src/docs/markdown/caddyfile/concepts.md index 56a8de15..bef580ea 100644 --- a/src/docs/markdown/caddyfile/concepts.md +++ b/src/docs/markdown/caddyfile/concepts.md @@ -352,9 +352,11 @@ You can use any placeholders in the Caddyfile, but for convenience you can also | `{orig_?query}` | `{http.request.orig_uri.prefixed_query}` | | `{path.*}` | `{http.request.uri.path.*}` | | `{path}` | `{http.request.uri.path}` | +| `{%path}` | `{http.request.uri.path_escaped}` | | `{port}` | `{http.request.port}` | | `{query.*}` | `{http.request.uri.query.*}` | | `{query}` | `{http.request.uri.query}` | +| `{%query}` | `{http.request.uri.query_escaped}` | | `{?query}` | `{http.request.uri.prefixed_query}` | | `{re.*}` | `{http.regexp.*}` | | `{remote_host}` | `{http.request.remote.host}` | @@ -373,6 +375,7 @@ You can use any placeholders in the Caddyfile, but for convenience you can also | `{tls_version}` | `{http.request.tls.version}` | | `{upstream_hostport}` | `{http.reverse_proxy.upstream.hostport}` | | `{uri}` | `{http.request.uri}` | +| `{%uri}` | `{http.request.uri_escaped}` | | `{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. From dff584a62ddc856b3ee62131360d3243c675cbae Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 12 Nov 2025 03:23:29 -0500 Subject: [PATCH 17/20] header search placeholders --- src/docs/markdown/caddyfile/directives/header.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/markdown/caddyfile/directives/header.md b/src/docs/markdown/caddyfile/directives/header.md index 292c9d54..275cb613 100644 --- a/src/docs/markdown/caddyfile/directives/header.md +++ b/src/docs/markdown/caddyfile/directives/header.md @@ -56,7 +56,7 @@ header [] [[+|-|?|>] [|] []] { - **<value>** is the header field value, when adding or setting a field. -- **<find>** is the substring or regular expression to search for. +- **<find>** is the regular expression to search for. Placeholders may be used for a dynamic input into the search pattern. 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). - **<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. From c8b9782c26c869a5ac241efb81c796e73768069b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Hild=C3=A9n?= Date: Tue, 13 Jan 2026 21:32:40 +0200 Subject: [PATCH 18/20] Add span attributes to tracing documentation (#496) --- src/docs/markdown/caddyfile/directives/tracing.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/docs/markdown/caddyfile/directives/tracing.md b/src/docs/markdown/caddyfile/directives/tracing.md index c7b6a2fa..a6cd2cd6 100644 --- a/src/docs/markdown/caddyfile/directives/tracing.md +++ b/src/docs/markdown/caddyfile/directives/tracing.md @@ -18,13 +18,18 @@ The trace ID and span ID are added to [access logs](/docs/caddyfile/directives/l ```caddy-d tracing { - [span ] + span + span_attributes { + + + } } ``` - **<span_name>** is a span name. Please see span [naming guidelines](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/specification/trace/api.md). +- **<span_attributes>** are additional attributes attached to each recorded span. Many span attributes are set by default according to OTEL [Semantic conventions for HTTP spans](https://opentelemetry.io/docs/specs/semconv/http/http-spans/) like details about the request, response and client. - [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. + [Placeholders](/docs/caddyfile/concepts#placeholders) may be used in span names and attributes. Keep in mind that the span name is set before the request is forwarded, so only request placeholders may be used. All placeholders are available in span attributes. @@ -64,6 +69,9 @@ example.com { handle { tracing { span app + span_attributes { + user_id {http.request.cookie.user-id} + } } reverse_proxy localhost:8080 } From 90188e6c74f5331e64d8a3418bfb6441cfe0af55 Mon Sep 17 00:00:00 2001 From: GreyXor <79602273+GreyXor@users.noreply.github.com> Date: Tue, 13 Jan 2026 20:35:43 +0100 Subject: [PATCH 19/20] feat: argon2id auth doc (#486) --- .../caddyfile/directives/basic_auth.md | 16 ++++++++- src/docs/markdown/command-line.md | 35 +++++++++++++++++-- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/docs/markdown/caddyfile/directives/basic_auth.md b/src/docs/markdown/caddyfile/directives/basic_auth.md index 3ac0b7a0..2145c825 100644 --- a/src/docs/markdown/caddyfile/directives/basic_auth.md +++ b/src/docs/markdown/caddyfile/directives/basic_auth.md @@ -26,7 +26,7 @@ basic_auth [] [ []] { } ``` -- **<hash_algorithm>** is the name of the password hashing algorithm (or KDF) used for the hashes in this configuration. Default: `bcrypt` +- **<hash_algorithm>** specifies the password hashing algorithm (or key derivation function) used for the hashes in this configuration. Available options include `argon2id`, the default is `bcrypt`. - **<realm>** is a custom realm name. @@ -64,3 +64,17 @@ example.com { } ``` +`argon2id` example + +```caddy +example.com { + root * /srv + + basic_auth /secret/* argon2id { + # Username "Bob", password "hiccup" + Bob $argon2id$v=19$m=47104,t=1,p=1$zJPvVe48N64JUa9MFlVhiw$b5Tznu0PxnA4TciY6qYe2BFPxncF1ePQaeNukHhH1cU + } + + file_server +} +``` diff --git a/src/docs/markdown/command-line.md b/src/docs/markdown/command-line.md index dc8dbf1c..dbfff9a1 100644 --- a/src/docs/markdown/command-line.md +++ b/src/docs/markdown/command-line.md @@ -255,11 +255,40 @@ Formats or prettifies a Caddyfile, then exits. The result is printed to stdout u Convenient way to hash a plaintext password. The resulting hash is written to stdout as a format usable directly in your Caddy config. -`--plaintext` is the plaintext form of the password. If omitted, interactive mode will be assumed and the user will be shown a prompt to enter the password manually. +`--plaintext` + The password to hash. If omitted, it will be read from stdin. + If Caddy is attached to a controlling TTY, the input will not be echoed. -`--algorithm` may be `bcrypt` or any installed hash algorithm. Default is `bcrypt`. +`--algorithm` + Selects the hashing algorithm. Valid options are: + * `argon2id` (recommended for modern security) + * `bcrypt` (legacy, slower, configurable cost, default cost is `14`) -`--bcrypt-cost` is the hashing cost for bcrypt algorithm. Default is `14`. +bcrypt-specific parameters: + +`--bcrypt-cost` + Sets the bcrypt hashing difficulty. Higher values increase security by + making the hash computation slower and more CPU-intensive. + Must be within the valid range [bcrypt.MinCost, bcrypt.MaxCost]. + If omitted or invalid, the default cost is used. + +Argon2id-specific parameters: + +`--argon2id-time` + Number of iterations to perform. Increasing this makes + hashing slower and more resistant to brute-force attacks. + +`--argon2id-memory` + Amount of memory to use during hashing. + Larger values increase resistance to GPU/ASIC attacks. + +`--argon2id-threads` + Number of CPU threads to use. Increase for faster hashing + on multi-core systems. + +`--argon2id-keylen` + Length of the resulting hash in bytes. Longer keys increase + security but slightly increase storage size. ### `caddy help` From 7fc7f273a5a928c9c154805c85a01a76a851d251 Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Sat, 17 Jan 2026 18:48:13 +0300 Subject: [PATCH 20/20] metrics: add `observe_catchall_hosts` docs (#518) Signed-off-by: Mohammed Al Sahaf --- src/docs/markdown/caddyfile/options.md | 12 ++++++++++++ src/docs/markdown/metrics.md | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/docs/markdown/caddyfile/options.md b/src/docs/markdown/caddyfile/options.md index 07932c70..f408bf23 100644 --- a/src/docs/markdown/caddyfile/options.md +++ b/src/docs/markdown/caddyfile/options.md @@ -78,6 +78,7 @@ Possible options are (click on each option to jump to its documentation): shutdown_delay metrics { per_host + observe_catchall_hosts } # TLS Options @@ -1057,6 +1058,17 @@ You can add the `per_host` option to label metrics with the host name of the met } ``` +Due to the infinite cardinality potential in observing all possible hosts may be sent by clients, Caddy will only record metrics for configured hosts, while all other hosts (e.g., attacker.com) are aggregated under "_other" label. To force observation of all hosts, and where potential infinite cardinality is an acceptable risk, you add `observe_catchall_hosts`. Note that adding `observe_catchall_hosts` will not enable `per_host`. However, this is automatically enabled for HTTPS servers (since certificates provide some protection against unbounded cardinality), but disabled for HTTP servers by default to prevent cardinality attacks from arbitrary Host headers. + +```caddy +{ + metrics { + per_host + observe_catchall_hosts + } +} +``` + ##### `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)). diff --git a/src/docs/markdown/metrics.md b/src/docs/markdown/metrics.md index 0a59acf2..7fe18b8f 100644 --- a/src/docs/markdown/metrics.md +++ b/src/docs/markdown/metrics.md @@ -33,6 +33,17 @@ To add per-host metrics you can insert the `per_host` option. Host specific metr } ``` +This configuration will observe configured hosts. If an HTTPS server is configured, the host is observed, even if not explicitly configured, e.g. on-demand TLS setup. If HTTPS is disbaled, the only configured hosts are enabled due to potential infinite cardinality risk. To observe all hosts in HTTP setup, even unconfigured ones, use `observe_catchall_hosts` option. + +```caddy +{ + metrics { + per_host + observe_catchall_hosts + } +} +``` + ## Prometheus [Prometheus](https://prometheus.io) is a monitoring platform that collects