2887 Commits
Author SHA1 Message Date
Kévin Dunglas 3d005442e4 fastcgi: reject paths whose escaped form selects another script (#8357) 2026-11-04 18:02:42 -05:00
Kévin Dunglas 70dca34a36 fastcgi: let configured env variables override request headers (#8358) 2026-11-04 18:02:29 -05:00
Julien Voisin c8492563ee caddyhttp: presize buffers in hot paths to reduce allocations (#7842)
* caddyhttp: presize buffers in hot paths to reduce allocations

Right-size three per-request allocations that previously grew from a
nil/empty backing array, causing repeated reallocation:

- CleanPath: pre-Grow the strings.Builder used when slash collapsing is
  disabled, instead of letting it reallocate as runes are appended
- headers: presize the rewritten value slice in HeaderOps.ApplyTo's Set
  loop instead of appending from nil
- reverseproxy: presize the weights slice in WeightedRoundRobinSelection
  .Select

This commit also adds benchmarks exercising each path. Measured with benchstat
with count=20 and GOMAXPROCS=1, with p=0.000:

                                    sec/op                  B/op              allocs/op
CleanPathNoCollapse       1013.4n -> 828.6n  (-18.23%)  120->64  (-46.67%)   4->1  (-75.00%)
HeaderOpsApplyToSet       1262.0n -> 955.8n  (-24.27%)  255->152 (-40.39%)   9->6  (-33.33%)
WeightedRoundRobinSelect   215.8n -> 200.3n  ( -7.20%)  112->96  (-14.29%)   2->2  (N/A)

* caddyhttp: presize strings.Builder in query rewrite and path matcher

Two builders whose output size is bounded by a known input length were
growing from empty, causing repeated reallocation. Seed each with a
capacity hint:

- rewrite.buildQueryString: pre-Grow to len(qs) (runs per request when
  rewrite query manipulation is configured)
- MatchPath.matchPatternWithEscapeSequence: pre-Grow to len(escapedPath)
  (reached only when a path pattern contains % escape sequences)

Adds a benchmark for buildQueryString, measured with benchstat:

BuildQueryString:
	- sec/op  965.2n -> 743.3n  (-22.99%)
	- B/op 248->80  (-67.74%)
	- allocs/op 5->1  (-80.00%)

The builder previously reallocated four times per call. The matcher path
is cold and not separately benchmarked.

* chore: add benchmark headers

---------

Signed-off-by: Julien Voisin <jvoisin@users.noreply.github.com>
2026-11-04 20:26:53 +00:00
Kévin Dunglas 7145e5eba8 fastcgi: reject Win 8.3 aliases and alt data streams paths (#8356) 2026-11-04 14:34:04 -05:00
Kévin Dunglas bd56cc1748 fastcgi: take HTTP_HOST from the routed host only (#8355) 2026-11-04 14:32:20 -05:00
Kévin Dunglas cf1b1121e7 httpcaddyfile: add Helper.IsNamedRoute (#8354) 2026-11-04 14:30:45 -05:00
Kévin Dunglas b5d67d6540 fileserver: only match a dir when the pattern ends by / (#8352) 2026-11-04 10:45:35 -05:00
Zen Dodd fbfc061e70 caddyhttp: detach streams at the server writer boundary (#8346) 2026-11-04 17:36:00 +10:00
Steffen Busch 644dbe695d fileserver: configure plain browse time format (#8084) 2026-11-04 17:05:11 +10:00
dependabot[bot] 7bb95a47b2 build(deps): bump github.com/go-jose/go-jose/v4 from 4.1.5 to 4.1.6 (#8299)
Bumps [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose) from 4.1.5 to 4.1.6.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Commits](https://github.com/go-jose/go-jose/compare/v4.1.5...v4.1.6)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v4
  dependency-version: 4.1.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-04 06:47:57 +00:00
dependabot[bot] ddd7475ed3 build(deps): bump the actions-deps group across 1 directory with 10 updates (#8340)
Bumps the actions-deps group with 10 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `7.0.0` | `7.0.1` |
| [step-security/harden-runner](https://github.com/step-security/harden-runner) | `2.19.4` | `2.22.3` |
| [actions/setup-go](https://github.com/actions/setup-go) | `6.5.0` | `7.0.0` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `7.0.1` | `7.0.2` |
| [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) | `7.2.3` | `7.3.0` |
| [golang/govulncheck-action](https://github.com/golang/govulncheck-action) | `1.0.4` | `1.1.0` |
| [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) | `4.1.0` | `4.1.2` |
| [anchore/sbom-action/download-syft](https://github.com/anchore/sbom-action) | `0.24.0` | `0.24.3` |
| [ossf/scorecard-action](https://github.com/ossf/scorecard-action) | `2.4.3` | `2.5.0` |
| [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) | `4.36.2` | `4.39.1` |



Updates `actions/checkout` from 7.0.0 to 7.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1)

Updates `step-security/harden-runner` from 2.19.4 to 2.22.3
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](https://github.com/step-security/harden-runner/compare/9af89fc71515a100421586dfdb3dc9c984fbf411...46b06cea07f9e64621fdf066ba77ac6915163d43)

Updates `actions/setup-go` from 6.5.0 to 7.0.0
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/924ae3a1cded613372ab5595356fb5720e22ba16...b7ad1dad31e06c5925ef5d2fc7ad053ef454303e)

Updates `actions/upload-artifact` from 7.0.1 to 7.0.2
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a...cf430e030ddbb5b0abf93d22962f4752f3646cd9)

Updates `goreleaser/goreleaser-action` from 7.2.3 to 7.3.0
- [Release notes](https://github.com/goreleaser/goreleaser-action/releases)
- [Commits](https://github.com/goreleaser/goreleaser-action/compare/f06c13b6b1a9625abc9e6e439d9c05a8f2190e94...a4f02ab5355a589de60d917a659ce617ed19b3de)

Updates `golang/govulncheck-action` from 1.0.4 to 1.1.0
- [Release notes](https://github.com/golang/govulncheck-action/releases)
- [Commits](https://github.com/golang/govulncheck-action/compare/b625fbe08f3bccbe446d94fbf87fcc875a4f50ee...032d45514ae346b1db93c04b0c90b841c370344f)

Updates `sigstore/cosign-installer` from 4.1.0 to 4.1.2
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](https://github.com/sigstore/cosign-installer/compare/ba7bc0a3fef59531c69a25acd34668d6d3fe6f22...6f9f17788090df1f26f669e9d70d6ae9567deba6)

Updates `anchore/sbom-action/download-syft` from 0.24.0 to 0.24.3
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](https://github.com/anchore/sbom-action/compare/e22c389904149dbc22b58101806040fa8d37a610...66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c)

Updates `ossf/scorecard-action` from 2.4.3 to 2.5.0
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](https://github.com/ossf/scorecard-action/compare/4eaacf0543bb3f2c246792bd56e8cdeffafb205a...fa7117db83c52d787ae5a89f350999701505d78e)

Updates `github/codeql-action/upload-sarif` from 4.36.2 to 4.39.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...9b3254085494b55a0f7c4c2b3d2d52cc712ce370)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: actions/setup-go
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: anchore/sbom-action/download-syft
  dependency-version: 0.24.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: github/codeql-action/upload-sarif
  dependency-version: 4.39.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
- dependency-name: golang/govulncheck-action
  dependency-version: 1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
- dependency-name: goreleaser/goreleaser-action
  dependency-version: 7.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
- dependency-name: ossf/scorecard-action
  dependency-version: 2.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
- dependency-name: sigstore/cosign-installer
  dependency-version: 4.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: step-security/harden-runner
  dependency-version: 2.22.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-04 16:42:18 +10:00
sam-kota c1c0ee5dad httpcaddyfile: reject conflicting client_auth when merging TLS policies (#8306)
* httpcaddyfile: reject conflicting client_auth when merging TLS policies
* httpcaddyfile: use host and host:port site addresses in client auth conflict test
* httpcaddyfile: enhance client auth consolidation test to handle conflicting configurations
* httpcaddyfile: remove unnecessary blank line in conflict error handling
2026-11-04 16:39:36 +10:00
D'Ali Ben Macha 2147da70f7 caddyhttp: lower-case path patterns with placeholders at request time (#8343)
* caddyhttp: lower-case path patterns with placeholders at request time

Patterns with placeholders were lower-cased at provisioning, which
changed case-sensitive placeholder names such as {env.FOO}, while the
replaced values were never lower-cased and so could not match the
lower-cased request path. Leave such patterns untouched at provisioning
and lower-case the expanded pattern at request time.

Fixes #6387

* caddyhttp: restore the encoded separator path matcher tests
2026-11-04 16:37:26 +10:00
468154121d ci: release changelogs since the previous published release, and workflow hardening (#8341)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2026-11-03 23:07:13 +00:00
bbea66f2ec reverseproxy: reduce lock contention in dynamic SRV and A upstreams (#7949)
* reverseproxy: reduce lock contention in dynamic SRV and A upstreams (#7946)

The shared SRV and A/AAAA caches held a global write lock across the DNS
lookup, so one slow or timing-out record blocked every unrelated
upstream source.

Lookups now run without the cache lock held, deduplicated per key with
singleflight. A per-key generation (stored on the cache entry, so it is
bounded by the cache cap) and a cache-wide epoch keep a lookup that was
in flight during ResetSRV/ResetAllSRV from writing its result back, and
are part of the singleflight key so callers after a reset do not join a
pre-reset lookup.

The shared lookup is detached from the request that started it and
bounded by its own 30s timeout; each caller still returns as soon as its
own context is done. Distinct lookups across both caches are capped by a
shared gate (64 running, 256 queued); beyond that they are shed, serving
a stale SRV entry if a grace period is set and one is cached.

SRV and A now share one cache implementation. AUpstreams also implements
CachingUpstreamSource (via new ResetA/ResetAllA), so retries purge its
cache entry like SRVUpstreams already did.

Rebased onto the move of this code into modules/internal/network (#7790,
#8252).

Fixes #7946

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* network: backdate cache entries in TestShedLookups instead of a 1ns refresh

On Windows two close time.Now() calls can report no elapsed time, so an
entry seeded with a 1ns refresh could still be fresh and skip the shed
path entirely.

* network: move the shared cache and lookup gate into cache.go

dynamicupstreams.go keeps the SRV/A API; the generic cache, its
singleflight/generation handling and the lookup gate are not specific to
either record type. No functional change.

* network: preserve cache reset and grace semantics

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Co-authored-by: steadytao <mail@steadytao.com>
2026-11-03 08:37:50 +00:00
Venkatesh Panda f89626aa55 caddyauth: Don't write into shared hash buffer when building cache key (#8278)
HTTPBasicAuth.correctPassword built its hash cache key with
append(account.password, plaintextPassword...). account.password is
shared by all requests and can have spare capacity, so short passwords
were written into the stored hash's backing array, causing a data race
between concurrent requests and potentially mixed-up cache keys.

Build the key in a fresh buffer instead, and add regression tests.
2026-11-03 08:21:45 +00:00
Kévin Dunglas 6cdffe9502 requestbody: enforce max_size early using Content-Length (#8337)
* requestbody: reject oversized Content-Length before the next handler

When max_size is set and the request declares a Content-Length above it, respond 413 right away instead of letting the next handler start and fail mid-read. Bodies of unknown length are still limited by MaxBytesReader. Both paths return the same error, so handle_errors sees no difference.

* caddytest: send unknown-length bodies in max_size placeholder tests

request_body now answers 413 for a declared Content-Length over max_size before the placeholder reads the body, so these tests no longer covered the read path and the direct respond one failed.

* requestbody: keep the body limited for error routes

Error routes get the same request, so wrap the body before the early 413 to stop them from reading past max_size.
2026-11-03 18:18:40 +10:00
Zen Dodd fc2236fe43 core: resolve systemd socket descriptors by name (#8123)
* core: resolve systemd socket descriptors by name
* core: reject overflowing systemd descriptor indices
* internal: isolate systemd resolution
* chore: move integration
* core: simplify systemd descriptor lookup
2026-11-03 08:16:26 +00:00
D'Ali Ben Macha 482f867395 caddyfile: reject negative import argument indices (#8316)
{args[-1]} and {args.-1} parsed as -1 and indexed the args slice
directly, panicking with index out of range. Treat negative indices as
out of bounds, like indices past the end.
2026-11-03 08:15:36 +00:00
Zen Dodd 43653cfb65 rewrite: treat only valid percent encodings as escapes (#8345) 2026-11-03 03:11:06 -05:00
D'Ali Ben Macha c168dbc4ca admin: authorize remote requests matching any permission entry (#8317)
* admin: authorize remote requests matching any permission entry

enforceAccessControls rejected a request as soon as one permission entry
did not match its method or path, so only the first entry was usable.
Allow the request if any entry permits both, and deny only after all fail.

* admin: check every access entry that holds the client key

A key listed in several access control entries was only checked
against the first one; deny only after all of them are tried.
2026-11-03 07:05:42 +00:00
D'Ali Ben Macha aeea18be86 reverseproxy: skip unavailable upstream with a single weighted_round_robin weight (#8320)
With fewer than two weights, WeightedRoundRobinSelection returned the
first upstream without checking that it is available.
2026-11-03 07:01:28 +00:00
D'Ali Ben Macha cce035d7ad fileserver: handle empty files in size try policies (#8322)
Use the chosen file info, not a zero size, to tell whether a candidate
was chosen, so smallest_size keeps an empty file and largest_size can
choose one.
2026-11-03 06:59:33 +00:00
D'Ali Ben Macha dccce45660 reverseproxy: close upstream body when handle_response status is invalid (#8318)
When a handle_response status code did not evaluate to an integer, the
handler returned a 500 without closing the upstream response body,
leaking the connection.
2026-11-03 06:58:00 +00:00
Nguyen Thanh Dat c645c2b439 rewrite: fix strip_path_suffix decoding a literal '%' as an escape (#8005) 2026-11-03 01:31:54 -05:00
Nguyen Thanh Dat 0759b29fe0 caddyhttp: correct path matcher doc on bare trailing wildcards (#8006) 2026-11-03 06:19:06 +00:00
Kévin Dunglas c4d8caa2e7 caddyhttp: reject invalid and dots-only hosts on every protocol (#8328) 2026-11-03 06:12:03 +00:00
Krish Singh Huraandsteadytao 053be179d8 ci: test and release with Go 1.27 (#8210)
* caddyhttp: add HTTP/1 body reuse regression tests

* ci: test and release with Go 1.27

---------

Co-authored-by: steadytao <mail@steadytao.com>
2026-11-02 21:41:02 -07:00
EINIER FREYRE CORONA f53b832c41 ci/cd: migrate to cosign v3 bundles and update installer to v4.1.0 (#7674)
* ci/cd: migrate to cosign v3 bundles for artifact signing

* ci/cd: update cosign-installer to v4.1.0 verified SHA

* ci/cd: use .sigstore.json extension for cosign bundles
2026-11-02 23:55:55 +00:00
a 98b0bbd477 go.mod: update x509roots/fallback (#8333) 2026-11-02 13:48:05 +03:00
Kévin Dunglas 1a870400e6 encode: use httpguts to look for Accept-Encoding in Vary (#8329) 2026-11-01 16:59:21 -05:00
Kévin Dunglas 84eaf50b7b admin: better Cache-Control handling (#8330) 2026-11-01 16:58:15 -05:00
Kévin Dunglas 9dd12fdb5d fileserver: hide the data directory when it is relative (#8327) 2026-11-01 15:57:49 -05:00
Kévin Dunglas aea046c978 fileserver: don't match regular file when path ends by slash (#8326) 2026-11-01 15:57:10 -05:00
Kévin Dunglas 094172da84 rewrite: escape file matcher remainder before rewriting (#8325) 2026-11-01 15:53:43 -05:00
Kartik K 9d2cb35c75 caddyauth: check basic_auth username uniqueness after placeholder expansion (#8315) 2026-11-01 09:04:24 +00:00
hsdfat 6bfeb4f825 caddyauth: support third-party hash modules (#8063)
* caddyauth: Support third-party http.authentication.hashes modules

basic_auth and hash-password only accepted the built-in bcrypt and
argon2id algorithms, even though the http.authentication.hashes
namespace is documented as extensible. Look up any other algorithm
name among the installed hash modules instead, and list the available
ones when the name is not recognized.

The built-in algorithms keep their own hash-password flags; other
modules are provisioned and must implement Hasher to generate hashes.

Closes #6670

* caddyauth: Base64-encode non-MCF hashes from hash-password modules

hash-password printed module hashes as raw bytes, but basic_auth
base64-decodes any password that isn't in Modular Crypt Format, so the
output couldn't be used as-is. Encode non-MCF hashes as base64 and add
a round-trip test that provisions HTTPBasicAuth with the command output.

* caddyauth: always base64-encode third-party hash output
2026-11-01 02:34:51 +00:00
Francis LavoieandClaude Opus 5.5 9e5d4d42b1 reverseproxy: test WebSocket upgrades over Unix sockets (#7657)
* reverseproxy: Test that WebSockets + unix-sockets works

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Close temp file before reusing its path for the socket

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Strengthen unix socket WebSocket test, reuse tempSocketName helper

Check that upstream bytes sent with the 101 are delivered, echo a payload larger than any buffer, and verify the upstream connection closes when the client disconnects. Reuse the existing tempSocketName helper for all unix socket tests, which also closes the temp file descriptor the older tests leaked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 12:22:28 +10:00
jasper 61e9b223da core: reject overlong ports in ParseNetworkAddress (#8277) 2026-10-31 21:57:28 -04:00
Kévin Dunglasandelee1766 a72377a10d encode: ignore request no-transform, vary identity responses on Accept-Encoding (#8301)
Co-authored-by: elee1766 <a@tuxpa.in>
2026-10-31 17:24:13 -04:00
tannevaledandZen Dodd f1bc314fbc reverseproxy: make the shared DNS discovery cache resolver-aware (#8252)
Co-authored-by: Zen Dodd <mail@steadytao.com>
2026-10-31 17:23:41 -04:00
Francis LavoieandClaude Opus 5.5 bb17cb631c ci: Use macos-latest runner instead of pinning macos-14 (#8312)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 22:13:39 +03:00
bwisn eb04ed52be httpcaddyfile: preserve automation policy precedence when merging (#8287)
* httpcaddyfile: Don't merge automation policies if it would shadow another

Signed-off-by: Bartlomiej Wisniowski <56162793+bwisn@users.noreply.github.com>

* httpcaddyfile: add reverse case for shadowing merge, use force_automate to match the real life usage and explain why we're not checking the inverse

Signed-off-by: Bartlomiej Wisniowski <56162793+bwisn@users.noreply.github.com>

* httpcaddyfile: Check overlap in both directions when merging policies

Signed-off-by: Bartlomiej Wisniowski <56162793+bwisn@users.noreply.github.com>

---------

Signed-off-by: Bartlomiej Wisniowski <56162793+bwisn@users.noreply.github.com>
2026-10-30 06:23:25 +00:00
Kévin Dunglas a28f061462 caddyhttp: let handler read deadlines cap the idle read timeout (#8284)
* caddyhttp: let handler read deadlines cap the idle read timeout

IdleTimeoutReader sets the read deadline before every body read, so a
deadline set by a handler with http.ResponseController.SetReadDeadline()
was replaced before it could take effect. Since #7913, handlers can't
bound a stalled request body more tightly than read_body.

A handler's read deadline is now routed to the IdleTimeoutReader, which
applies it right away and keeps it as a ceiling for every idle reset and
for the deadline put back between HTTP/2 reads. A handler can make the
idle timeout stricter but can't loosen it, and a zero deadline removes
the ceiling, as #8145 does for write deadlines.

* caddyhttp: keep the hard read deadline and the drain deadline with handler deadlines

SetReadDeadline installed the handler's deadline as is, so a later or zero
deadline lifted read_timeout. It now installs the earlier of the two.

HandlerDone cleared a deadline set by the handler before arming the HTTP/1
drain deadline, leaving the post-handler drain unbounded.

* caddyhttp: keep the idle deadline of a read in flight on handler deadlines

SetReadDeadline replaced the idle deadline armed for a blocked read with the
handler's ceiling, so a zero or later deadline lifted the idle timeout for
that read. It now installs the earlier of the two until the read returns.

* caddyhttp: keep the expired drain deadline of a timed-out body on handler deadlines

After a body read timed out on HTTP/1, a zero or later deadline set by the
handler replaced the expired one kept to bound net/http's post-handler drain.
2026-10-29 23:22:59 +10:00
Kévin DunglasandKağan Küçük 857d5731f3 caddyhttp: optimise header access and fix encoding negotiation (#8025)
* perf: bypass header key normalization for canonical literals

http.Header's Get, Set, Add and Del run textproto.CanonicalMIMEHeaderKey
on every call. When the key is a canonical literal the result is the key
itself, so the scan is pure overhead. Access the map directly instead.

Only hot, allocation-free paths are converted, and only where the key is
a canonical literal. Call sites taking a key from config, a template or
the request itself still go through the normalizing methods, since they
need it for correctness.

allHeaderValues and lastHeaderValue dropped their http.CanonicalHeaderKey
call for the same reason: all callers pass canonical literals.

benchstat, Apple M5 Pro, go1.27.1:

    PrepareRequest-15      659.4n ± 2%  ->  457.7n ± 2%  -30.58% (p=0.000 n=12)
    AcceptedEncodings-15   330.8n ± 5%  ->  309.4n ± 7%   -6.44% (p=0.019 n=10)

    ResponseWriterWriteHeader/event-stream-15    1007.5n ± 3%  ->  732.5n ± 1%  -27.30%
    ResponseWriterWriteHeader/informational-15     60.0n ± 3%  ->   48.0n ± 2%  -20.01%
    ResponseWriterWriteHeader/not-modified-15     214.3n ± 6%  ->  175.3n ± 4%  -18.20%
    ResponseWriterWriteHeader/connect-15           60.2n ± 3%  ->   51.9n ± 4%  -13.85%
    ResponseWriterWriteHeader/html-15             206.5n ± 4%  ->  197.9n ± 2%   -4.19%
    geomean                                                                    -14.38%

Allocations are unchanged.

* tests: verify http.Header map keys are canonical

Indexing http.Header directly skips the canonicalization Get/Set/Add/Del
perform, which also puts the key outside what CI checks. canonicalheader
only inspects method calls. staticcheck's SA1008 covers reads, but stops
descending as soon as an assignment writes to a header, so it misses the
write, the delete builtin and the read-modify-write append. A key that is
not canonical in those positions is a silent lookup miss, not a compile
error.

Walk the loaded packages and verify every string constant used as a key in
an http.Header map operation matches http.CanonicalHeaderKey. A helper that
takes the key from its caller would hide the literal again, so a parameter
named with a canonical prefix opts its arguments into the same check.

The WebSocket headers Caddy deliberately writes with RFC 6455 casing are
allowed explicitly; they are only ever written out, never looked up against
a parsed header map.

Covers 64 operations, runs in under a second, and promotes golang.org/x/tools
from an indirect to a direct dependency at the version already in the module
graph.

* headercheck: resolve aliases of http.Header

* headercheck: check http.Header composite literal keys

* headercheck: note overlap with canonicalheader

* headercheck: drop parameter-name check

Helpers now take the indexed values, so every key sits in an index expression the analyzer and the linter already check.

* headercheck: scope RFC 6455 exceptions to their call sites

A key-based allow-list also accepted reads such as h["Sec-WebSocket-Key"], which miss because parsed headers are canonical. Honor //nolint:canonicalheader on the line instead, which the linter will use too.

* encode: read every Accept-Encoding field line

AcceptedEncodings read only the first field line, so a client that split
the list across several lines had the rest ignored. RFC 9110 section 5.3
treats repeated list-field lines as one comma-joined list. Iterate over
the items of every line instead of joining them, and skip empty items,
which used to yield an empty encoding. file_server's precompressed
selection uses the same function and is fixed with it.

Fixes https://github.com/caddyserver/caddy/issues/8272

Co-authored-by: Kağan Küçük <kaankck@gmail.com>

* encode: read every Cache-Control and Content-Encoding field line

Only the first field line was read, but RFC 9110 section 5.3 makes
multiple lines of a list-based field equivalent to one comma-joined
value:

- Cache-Control: no-transform on a later line was ignored, so the
  request or response was compressed anyway.
- Content-Encoding: an empty first line followed by a real coding let
  the handler encode the body a second time.

* encode: parse Cache-Control no-transform per RFC 9111

The substring match missed No-Transform, though RFC 9111 section 5.2 compares directive names case-insensitively, and fired on text that is not the directive: longer tokens such as x-no-transform-ext, quoted arguments such as community="no-transform", and semicolon-separated values, which are not valid Cache-Control syntax. Split the field on commas outside quoted strings and match only a well-formed no-transform element.

* encode: keep accepted encoding preferences on the stack

AcceptedEncodings counted commas to size the preference slice, and the variable capacity put it on the heap. A constant capacity covers typical Accept-Encoding headers and lets the compiler keep the slice on the stack; append still moves it to the heap for longer ones. BenchmarkAcceptedEncodings: 151.8ns -> 120.5ns (-21%), 192 -> 64 B/op, 2 -> 1 allocs/op (n=8).

* encode: build the encoded ETag without fmt

fmt.Sprintf boxed both arguments and parsed the format on every encoded response with a strong ETag. Concatenation: 47ns -> 16.5ns, 56 -> 24 B, 3 -> 1 allocs.

* reverseproxy: access response Server and Via headers directly

finalizeResponse runs on every proxied response; skip the key normalization as prepareRequest already does on the request side.

* encode: access headers directly in Write, WriteHeader and Close

These run once per response; convert the remaining literal-key Get, Set and Add calls like the rest of the handler.

* encode: match Cache-Control no-transform with httpguts

httpguts.HeaderValuesContainsToken already compares list elements case-insensitively across field lines, so the hand-written list parser is unnecessary. A no-transform inside a comma-separated quoted argument now also disables encoding, which only errs toward not transforming.

* encode: inline the no-transform check

isEncodeAllowed only wrapped one httpguts call. Its unit test now drives the response directive through ServeHTTP instead.

---------

Co-authored-by: Kağan Küçük <kaankck@gmail.com>
2026-10-29 23:19:50 +10:00
Francis Lavoieandhikmetba-bit 4fce7c8bbc caddyhttp: document CEL raw strings for regexp patterns (#8280)
Co-authored-by: hikmetba-bit <hikmetba-bit@users.noreply.github.com>
2026-10-29 14:08:28 +01:00
dependabot[bot] 754078da65 build(deps): bump github.com/go-chi/chi/v5 from 5.3.2 to 5.4.0 (#8298)
Bumps [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) from 5.3.2 to 5.4.0.
- [Release notes](https://github.com/go-chi/chi/releases)
- [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-chi/chi/compare/v5.3.2...v5.4.0)

---
updated-dependencies:
- dependency-name: github.com/go-chi/chi/v5
  dependency-version: 5.4.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-29 19:20:40 +10:00
sam-kota 32bba15459 fastcgi: keep the php_fastcgi directory redirect on the same origin (#8285)
* fastcgi: keep the php_fastcgi directory redirect on the same origin
* fastcgi: build the directory redirect from the escaped original segment
2026-10-28 09:45:37 +00:00
fead9c3f24 caddytls: keep ACME issuer network proxy after RebuildCertMagic (#8288)
* caddytls: keep ACME issuer network proxy across re-provisioning

LoadModule clears NetworkProxyRaw, so when the HTTP app provisions an
ACME issuer a second time (fillInACMEIssuer), the rebuilt template has
no HTTPProxy. Since #7510 that template is used via SetConfig, and ACME
requests bypass the configured proxy.

Keep the loaded proxy func on the issuer, like the DNS solver.

Fixes #8283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 2366825f2380383dacd8adeb1508e34c3e25634d)

* caddytls: test that ACME requests use the proxy after RebuildCertMagic

Replace the provision-twice check with a regression for the path the
HTTP app takes: Provision, re-provision in fillInACMEIssuer, then
RebuildCertMagic. The test issues against a local proxy and asserts the
ACME request goes through it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: k4lleanka <257361691+k4lleanka@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:37:00 +00:00
Akinyemi Toluwalase 71c5812cfe caddytls: validate curve and protocol names, compare protocol IDs (#8266)
* caddytls: validate curve and protocol names, compare protocol IDs
* caddytls: Add final newline to tests and drop duplicate protocol comment
2026-10-26 20:14:59 +00:00