Commit Graph

363 Commits (master)

Author SHA1 Message Date
XiaoleC05 c1645c544d
caddyfile: Expand imports inside named routes (#7986) 2026-09-16 09:09:57 -04:00
Zen Dodd ef1877210e
Merge commit from fork
* caddyhttp: canonicalise paths after prefix stripping

* caddyhttp: cover suffix stripping in path canonicalisation
2026-09-14 12:50:44 -06:00
Sash 97a08d8251
fileserver: reject non-integer browse file_limit (#7988) 2026-09-04 16:37:02 +10:00
Faiyaz Rahman 4974956b9c
chore: fix lint errors from newer golangci-lint (#7958)
Signed-off-by: Faiyaz Rahman <faiyazrahman03@gmail.com>
2026-08-25 13:12:10 +10:00
yangshenghui 0bbda5c728
fix: close resources on error paths (#7940) 2026-08-16 21:02:39 +10:00
SillyZir 1fed032234
caddyhttp: shield specific hostnames from a covering wildcard's client auth (#7920)
* caddyhttp: shield specific hostnames from a covering wildcard's client auth (@sillyzir)

A connection policy for a wildcard hostname (e.g. *.example.com with
client_auth) is first-match by SNI, so it also applied client
authentication to more specific hostnames served by their own site
blocks (public.example.com) — sites that never asked for mTLS.

Two cases produce the shielding empty policy that fixes this:

  - site blocks whose TLS config yields a connection policy with no
    settings (previously discarded as having no effect);
  - site blocks with no TLS connection policy at all — the reported
    case — for which an empty policy is now synthesized.

Either way the empty policy is hoisted directly above the first
client-auth-bearing policy whose wildcard SNI covers the hostname, so
first-match shields it from the client-auth requirement.

Deliberately scoped to client authentication: other wildcard policy
settings, such as certificate selection, are ones a covered hostname
generally WANTS to inherit (see tls_automation_wildcard_shadowing);
sni matchers that fail to decode emit an adapt warning instead of
being silently skipped.

Fixes #7860

* caddyhttp: shield preserves the wildcard policy's other settings (@sillyzir)

The hoisted shield was an empty policy, and connection policies are
first-match: it lifted the client-auth requirement but also suppressed
every other setting the covering wildcard policy carried (certificate
selection, protocol bounds, ALPN). Hoist a copy of the covering policy
with only client_authentication removed instead, so the shielded
hostname keeps inheriting the rest.

The new adapt test gives the wildcard policy protocols and alpn in
addition to client_auth and asserts the shield carries both while
dropping only client authentication; the existing test (client_auth
only) is unchanged, which is exactly why it could not catch this.

* caddyhttp: shield each covering wildcard policy separately (@sillyzir)

The hoisting loop stopped at the first client-auth policy covering any of a
site block's hostnames and gave the shield that block's whole SNI matcher, so
hostnames covered by a *different* wildcard matched it too. Because connection
policies are first-match, those hostnames then took the wrong policy's
settings and lost their own client-authentication requirement entirely.

Map each hostname to the first covering policy individually and hoist one
shield per covering policy, matching only the hostnames it covers. The
existing policies' wildcard SNI names are decoded once up front, and the
shields are inserted back to front so an insertion cannot shift the index of a
covering policy still to be shielded.

* chore: fumpt (@steadytao)
* chore: fix master lint (@steadytao)

---------

Co-authored-by: SillyZir <269283839+SillyZir@users.noreply.github.com>
Co-authored-by: Zen Dodd <mail@steadytao.com>
2026-08-12 15:09:58 +10:00
Kévin Dunglas 947087cadd
Merge commit from fork
Caddy's underscore header filter (GHSA-f59h-q822-g45g) only checked
for `_`. PHP folds `.` to `_` when registering $_SERVER keys the same
way CGI/FastCGI folds `-` to `_`, so a dotted alias (e.g. Remote.User)
survived the filter and collided with the legitimate hyphenated
header once it reached a PHP/FastCGI backend, bypassing forward_auth
copy_headers the same way the underscore alias did.

Extends the filter to drop `.` symmetrically, adds an
`expected_dot_headers` allowlist mirroring `expected_underscore_headers`,
and handles header names containing both separators (only an exact
allowlist entry is honored there, since a prefix glob's free-form
suffix can't be vetted for an embedded second separator).

Root cause identified by @iliaal in the FrankenPHP advisory
GHSA-49wc-4hcv-v58q.
2026-08-11 08:00:15 -06:00
Ben Younes c290397cda
caddyhttp: log recovered handler panics at ERROR level (#7924)
Co-authored-by: Zen Dodd <mail@steadytao.com>
Signed-off-by: Zen Dodd <mail@steadytao.com>
2026-08-10 02:18:17 -04:00
SillyZir 54a6b8bc9e
caddyconfig: Register nested named-route invokes transitively (#7898) 2026-07-30 10:55:49 +00:00
a 873fac5fc0
caddyfile: treat quoted braces as literal arguments (#7875) 2026-07-12 10:33:32 +10:00
TowyTowy c1907df2ed
intercept: fix misleading handle_response error for extra matcher args (#7871)
Same obsolete two-argument check as the reverse_proxy one removed in
#7869: it referenced the long-removed inline status-code-replacement
syntax and pointed users at replace_status even when no status
replacement was involved. The generic check now rejects all
excess-argument cases with an accurate message.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 09:16:34 +10:00
TowyTowy 945d199724
reverseproxy: fix misleading handle_response error for extra matcher args (#7869)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 05:40:23 +00:00
Kévin Dunglas 13a4c3f43c
caddyhttp: add URL pattern request matcher (#7787)
* caddyhttp: add url_pattern request matcher

Match requests against a URLPattern (https://urlpattern.spec.whatwg.org/),
supporting named groups, wildcards and regexp components beyond the path
matcher. Relative patterns match any origin; absolute patterns or base_url
scope to scheme and host.

Exposes a url_pattern CEL function and publishes captured groups as
{http.url_pattern.<component>.<group>} placeholders.

* caddyhttp: add Caddyfile adapt test for url_pattern matcher
2026-06-22 10:25:43 -06:00
Luccin Masirika 39c9a85f80
fileserver: append repeated `hide` subdirectives instead of overwriting (#7817)
Multiple `hide` subdirectives in a file_server Caddyfile block silently overwrote each other, so only the last one took effect. Append to the list instead, so repeated entries accumulate and imported snippets can compose with site-specific hides.
2026-06-13 22:44:19 +10:00
long.black 16235cced5
intercept: fix replace_status being silently dropped (#7810)
* test: add failing tests for intercept replace_status (#7805)

Add integration tests that verify replace_status actually substitutes
the HTTP status code sent to the client. Currently these tests fail
because replace_status is silently a no-op due to value-receiver
boxing and shouldBuffer returning false.

Tests added:
- TestInterceptReplaceStatusWithMatcher: 500 -> 200 with @err matcher
- TestInterceptReplaceStatusWithoutMatcher: 403 -> 200 unconditionally
- TestInterceptReplaceStatusNotMatched: 200 passes through unchanged

* fix: make intercept replace_status actually substitute the status code

Fix #7805: replace_status was silently a no-op because:
1. shouldBuffer returned false when a replacement status was set,
   causing the original status to be streamed directly to the wire
2. The value-receiver WriteHeader method operated on a stale copy

The fix:
- shouldBuffer now returns true when replace_status matches, so
  the response is buffered instead of streamed
- After next.ServeHTTP returns, if routes are nil (replace_status
  only), write the substituted status and buffered body to the client

The interceptedResponseHandler.WriteHeader substitution branch is no
longer needed for this path since substitution happens post-ServeHTTP.

* refactor: remove dead WriteHeader method and resolved TODO

The value-receiver WriteHeader on interceptedResponseHandler was
unreachable dead code — the substitution is now handled post-ServeHTTP
via buffering. Remove it along with the TODO comment that noted
status code replacement was unfinished.

* style: apply nit suggestions from dunglas code review

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:50:58 -06:00
Rhul 0f7f8e9cf6
forwardauth: error on duplicate uri subdirective (#7814) 2026-06-10 23:31:04 -04:00
Rhul 55b3397a2d
reverseproxy: validate on weighted_round_robin loadbalancing policy (#7807)
* reverseproxy: validate on weighted_round_robin policy

Validate that weighted_round_robin has a non-zero total weight.
This prevents configurations such as:
    weighted_round_robin 0 0
from being accepted and causing a divide-by-zero panic during request handling.

* test: validation test on zero weight upstreams.

* test: provision called instead of totalweight setting

* reverseproxy: validate on negative upstream weights

* test: regression test on weighted_round_robin selection policy
2026-06-08 02:18:20 +10:00
Rhul 3b7bde8f25
httpcaddyfile: error on duplicate named_routes (#7800)
* fix: error on duplicate named_routes

Fixes issue #7798

Validate named route names before inserting them into the
named route map.

This prevents later definitions from overwriting existing
named routes and returns an error when a route name is
defined more than once.

* test: add test for duplicate named_routes
2026-06-04 14:55:08 -04:00
WeidiDeng fcc7860d03
reverseproxy: replace placeholders specified for sni while using http3 (#7737)
* reverseproxy: replace placeholders specified for sni while using http3

* add test for placeholder

* reverseproxy: replace placeholders specified for sni while using http3

* add test for placeholder

* reverseproxy: test HTTP/3 SNI host placeholder

---------

Co-authored-by: Zen Dodd <mail@steadytao.com>
2026-06-02 21:49:00 -06:00
Kévin Dunglas 3eb8e48ff0
Merge commit from fork
* feat: drop headers with underscore in their names

* feat: Caddyfile binding and tests for underscore-in-header drop

Add the `allow_underscore_in_headers` global server option, refine the
doc comment, and cover the filter end-to-end: server-level unit tests
(drop, opt-out, debug log, RFC-7230 space rejection), a fastcgi unit
test for the trimmed header name replacer, and forward_auth integration
tests for both the default-drop and opt-out paths.

* remove allow_underscore_in_headers option for now
2026-05-29 11:37:17 -06:00
Zen Dodd 88037f1666
chore: clean up wording and typo fixes (#7745)
* chore: clean up wording and typo fixes
* chore: ASCII -> alphanumeric in lexer for heredoc marker
2026-05-20 16:36:30 +10:00
tomholford 0780d4489c
httpcaddyfile: accept duration strings for log sampling interval (#7694)
Co-authored-by: tomholford <tomholford@users.noreply.github.com>
2026-05-07 18:32:20 +00:00
Kévin Dunglas 2a3ed96f8c
metrics: Implement pushing via OLTP (#7664) 2026-04-25 06:52:08 -04:00
Matt Holt 441d5eb062
caddyhttp: prefer port 443 in auto-HTTPS and add tests (#7666) 2026-04-23 17:29:03 +10:00
Daniil Sivak aed1af5976
reverseproxy: add `lb_retry_match` condition on response status (#7569) 2026-04-21 14:59:31 -04:00
prettysunflower 7dedd1486c
fix(caddyfile): {block} in snippet (#7558)
* fix(caddyfile): {block} in snippet

Resolve issue #7557

So, here is the situation:
- Pull request #7206 included some changes to the doImport's function of
  Caddyfile's parser. What it does is that if there is no token within a
  block that follows the import, and the import contains `{block}`, then
  the `{block}` token is discarded.
- After this pull request:
  - Issue #7518 noticed that in cases that `{block}` was not imported,
    a runtime error was raised due to the assumption that tokens were
    always added to `tokensCopy` on every iteration of `importedTokens`.
    This was fixed by pull request #7543.
  - Issue #7557 notices that {block} can be ignored when imported from a
    certain file. There, it's again an issue with how the import works.
    When `import snippets` is called, this import instruction doesn't
    contains any nested blocks. And when the argument replacer that is
    the `importedTokens` loop is called and finds `{block}`, it uses the
    block from the file's import (which in this case is nothing),
    `{block}` is erased, and unavailable when the import directive is
    called for the imported snippet.

The changed in this commit addresses the second issue by checking before
replacing `{block}` if we're currently in a snippet definition, and
appending the `{block}` token to `tokensCopy` if we are.

With this changes, when importing those snippets, the `{block}` token
will be available to be replaced by the nested blocks in `tokensToAdd`
if needed, or erased if there are no nested blocks and `tokensToAdd` is empty.

Tests added in pull requests #7206 and #7543 passes with this new
implementation, confirming that unused `{block}` are accepted if nothing
is passed to `import`, as well as the other usual tests.
A new test was also added based on issue #7557 reporting, and also passes.

Signed-off-by: prettysunflower <me@prettysunflower.moe>

* caddyfile: add imported snippet block placeholder coverage

---------

Signed-off-by: prettysunflower <me@prettysunflower.moe>
Co-authored-by: Zen Dodd <mail@steadytao.com>
2026-04-15 02:58:53 -04:00
Zen Dodd 5f44ea0748
logging: add journald encoder wrapper (#7623) 2026-04-10 17:09:12 -06:00
Zen Dodd ca0ca67fbd
reverseproxy: make stream copy buffer size configurable (#7627) 2026-04-10 14:49:32 -06:00
Harsh Patel d7834676aa
tls: add `system` and `combined` CA pool modules (#7406)
* feat: add system and combined CA pool modules

* fix: combining pools using `CertificateProvider`

* fix: lint issue

* chore: caddyfiletests
* doing it for first time, so not sure if its right.

* fix: use `x509` native addCert

* chore: explicit err handling

* Apply suggestion from @mohammed90

---------

Co-authored-by: Mohammed Al Sahaf <mohammed@caffeinatedwonders.com>
2026-04-06 01:13:34 +03:00
Sam Ottenhoff 7a630f2910
encode: make zstd checksum configurable (#7586)
* http: make zstd checksum configurable

* disable_checksum
2026-03-28 13:07:21 -06:00
Marc 62e9c05264
root: introduce down-propagating Helper.BlockState for other directives/plugins to use (#7594)
* add 'root' key to Helper.State for access in frankenphp's `php_server` directive

* clone state before passing it to child directives, but keep sharing it among sibling directives

* propagate named route state from children to parent

* use BlockState to set "root" instead

* gofmt -w .

* go fmt ./...

* here we go
2026-03-28 17:44:42 +00:00
Matt Holt acf8d6a1ae
caddytls: Consolidate empty APs more smartly (#7567)
* caddytls: Consoldate empty APs more smartly (fix #7559)

* Revise consolidation logic
2026-03-26 14:41:34 -06:00
newklei 2dbcdefbbe
forward_auth: `copy_headers` does not strip client-supplied identity headers (Fixes GHSA-7r4p-vjf4-gxv4) (#7545)
When using copy_headers in a forward_auth block, client-supplied headers with
the same names were not being removed before being forwarded to the backend.

This happens because PR #6608 added a MatchNot guard that skips the Set
operation when the auth service does not return a given header. That guard
prevents setting headers to empty strings, which is the correct behavior,
but it also means a client can send X-User-Id: admin in their request and
if the auth service validates the token without returning X-User-Id, Caddy
skips the Set and the client value passes through unchanged to the backend.

The fix adds an unconditional delete route for each copy_headers entry,
placed just before the existing conditional set route. The delete always runs
regardless of what the auth service returns. The conditional set still only
runs when the auth service provides that header.

The end result is:
  - Client-supplied headers are always removed
  - When the auth service returns the header, the backend gets that value
  - When the auth service does not return the header, the backend sees nothing

Existing behavior is unchanged for any deployment where the auth service
returns all of the configured copy_headers entries.

Fixes GHSA-7r4p-vjf4-gxv4
2026-03-03 23:30:49 -05:00
Salent Olivick 7b34e3107e
core: Check whether @id is unique (#7002)
* caddy.go: Check whether @id is unique(#6991)

* Alternate implementation, using Gemini 3.1

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2026-03-03 15:09:49 -07:00
Francis Lavoie a6acb3902c
proxyproto: Generated test coverage (#7540) 2026-03-03 15:08:09 -07:00
Francis Lavoie 45cf61b127
logging: Ensure `slog` error level logs don't print stack traces (#7512) 2026-03-03 14:44:42 -07:00
prettysunflower 2dd3852416
fix(caddyfile): Prevent parser to panic when no token were added by empty {block} (#7543) 2026-03-03 13:16:21 -05:00
Akın Demirci 11b56c6cfc
reverseproxy: Fix `health_port` being ignored in health checks (#7533) 2026-03-03 13:10:54 -05:00
Pavel Siomachkin f145bce553
tls: Add `tls_resolvers` global option for DNS challenge configuration (#7297)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2026-03-01 15:32:04 -05:00
Francis Lavoie 9873752978
logging: Support `zstd` roll compression (#7515) 2026-02-23 16:04:45 -07:00
Dean Ruina 294dfff443
logging: add DirMode options and propagate FileMode to rotations (#7335)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2026-02-23 07:27:27 +00:00
Paulo Henrique 76b198f586
http: Sort auto-HTTPS redirect routes by host specificity (fixes #7390) (#7502) 2026-02-21 21:42:40 -05:00
Matt Holt 95941a71e8
chore: Add nolints to work around haywire linters (#7493)
* chore: Add nolints to work around haywire linters

* More lint wrangling
2026-02-17 16:52:54 -07:00
Francis Lavoie 3adcafd4c1
admin: Fix tests locally, properly isolate storage (#7486)
* admin: Fix tests locally, properly isolate storage

* Fix flaky pki_test

* Drop testdata dir logic

* Safer temp dir

* Test handlers without a full server
2026-02-17 13:14:06 -07:00
Amirhf 091add5ae3
caddytest: make TestReverseProxyHealthCheck deterministic with poll instead of sleep (#7474)
Start lightweight backend servers before starting Caddy so active health checks
probe a ready backend instead of the same Caddy instance during provisioning.
This removes the startup race without fixed sleeps or polling.
2026-02-17 06:41:38 -05:00
Francis Lavoie d64c7e67a4
caddyhttp: Option to disable 0-RTT (#7485) 2026-02-16 10:20:47 -07:00
Aditya Bhargava d6a6b486db
httpcaddyfile: Override global `dns` with `acme_dns` (fix #7294) (#7458)
This brings the behaviour in line with what the documentation implies.
2026-02-15 09:04:59 +00:00
mehrdadbn9 929d0e502a
caddyfile: Add `renewal_window_ratio` global option and `tls` subdirective (#7473)
* caddyfile: Add renewal_window_ratio global option

Adds support for configuring the TLS certificate renewal window ratio
directly in the Caddyfile global options block. This allows users to
customize when certificates should be renewed without needing to use
JSON configuration.

Example usage:
    {
        renewal_window_ratio 0.1666
    }

Fixes #7467

* caddyfile: Add renewal_window_ratio to tls directive and tests

Adds support for renewal_window_ratio in the tls directive (not just
global options) and adds caddyfile adapt tests for both the global
option and tls directive.

* fix: inherit global renewal_window_ratio in site policies

* fix: correct test expected output for policy consolidation

* fix: properly inherit global renewal_window_ratio without removing other code
2026-02-13 16:47:02 -05:00
WeidiDeng 47f3e8f8dc
use math/rand/v2 instead of math/rand (#7413) 2026-02-11 09:15:51 -07:00
Kévin Dunglas 598b08f9ae
test: mark `Assert*` functions as test helpers (#7380) 2025-12-08 22:32:00 +00:00