Commit Graph
180 Commits
Author SHA1 Message Date
Kévin Dunglas 0cf03d32f7 caddyhttp: mitigate slowloris via idle read/write deadlines (#7913)
* caddyhttp: mitigate slowloris via idle read/write deadlines

ReadTimeout and WriteTimeout previously applied as a single hard
deadline over the whole body/response through http.Server, so any
non-zero value also killed large transfers from legitimately slow
clients. Reset the deadline on every successful read/write instead
(via http.ResponseController), and give both a sane 1m default now
that doing so no longer penalizes slow-but-progressing clients.

* caddyhttp: split idle read/write timeouts from the existing hard ones

Reworking ReadTimeout/WriteTimeout's own semantics was an unwanted
behavior change for existing configs relying on the hard deadline.
Leave them untouched and add ReadIdleTimeout/WriteIdleTimeout instead,
reset on every successful read/write; both default to 1m since,
being new, no existing config could have depended on a different
value. Combining an idle timeout with its hard counterpart now gives
the same base+ceiling shape as Apache's mod_reqtimeout, for free.

* caddyhttp: cap idle-reset deadlines at the hard timeout ceiling

Deadlines are a single absolute value on the connection, not a min of
several: ReadTimeout/WriteTimeout's own hard deadline, set once by
net/http before the handler runs, was silently getting overwritten by
the first idle-reset Read/Write, voiding it entirely. Clamp the
idle-reset deadline to the hard one when both are set, so combining
them actually behaves like the advertised base+ceiling.

* caddyhttp: add ReadMinRate/WriteMinRate, Apache MinRate equivalent

Pure idle-reset alone doesn't bound a trickle that sends just enough
to never go idle. ReadMinRate/WriteMinRate (bytes/second) grow the
allowed deadline from a fixed start based on bytes transferred so far
instead of resetting to a flat window on every call, so a transfer
that doesn't sustain the configured rate falls behind real time and
gets cut, matching Apache mod_reqtimeout's MinRate. Zero (default)
keeps the existing flat idle-reset behavior unchanged.

* caddyhttp: use named return and consistent blank lines in idleDeadline

Matches the named-return style already used by ResponseWriterWrapper.ReadFrom.

* caddyhttp: chunk idleTimeoutWriter's Write/ReadFrom, cap at 64 KiB

SetWriteDeadline bounds the whole call it precedes, not just a stall
within it. net.Conn.Write loops internally until a buffer is fully
sent (unlike Read, which returns after one syscall), and
ResponseWriter.ReadFrom hands the entire remaining source to the
connection in one call. A single large Write, or any body copied via
io.Copy triggering the ReadFrom fast path (http.ServeContent, static
file serving), had its whole transfer bounded by one deadline,
silently truncating a slow-but-healthy transfer exactly like a hard
WriteTimeout would - the same bug found and fixed the same way in
FrankenPHP's go_ub_write (php/frankenphp#2574).

Cap each underlying call at 64 KiB and reset the deadline between
chunks instead. net/sendfile.go special-cases *io.LimitedReader, so
chunking ReadFrom still uses the sendfile fast path per chunk.

* caddyhttp: export idle-timeout types, add configurable MaxWriteChunk

Export IdleTimeoutReader/IdleTimeoutWriter/IdleDeadline so other
packages (request_body next) can reuse the same idle-reset mechanism
instead of reimplementing it, and turn the hardcoded 64 KiB write
chunk size into a configurable MaxWriteChunk field defaulting to the
same value - nginx's sendfile_max_chunk exists for the identical
reason and is admin-tunable rather than fixed.

* requestbody: idle-reset ReadTimeout/WriteTimeout, add MinRate/MaxWriteChunk

ReadTimeout/WriteTimeout set a single deadline once, so any transfer
running longer than the timeout got cut regardless of whether it was
actually stalled - the same bug the server-wide timeouts had before
switching to idle-reset. Reuse caddyhttp.IdleTimeoutReader/Writer here
too, giving per-route granularity nginx/Apache have via location/
directory scoping and Caddy's server-wide timeouts don't: a route
matching this handler can now set its own idle window independently
from the rest of the server block.

* caddyhttp: fold read/write min_rate into the idle-timeout directive

Two directives per rate (read_body_idle + read_body_min_rate) for a
value that's meaningless without the other. Fold min_rate into the
idle-timeout directive as an optional second argument instead.

* caddyhttp: split write pacing out of request_body into new timeouts handler

request_body is a request-body concern (max_size, set); ReadTimeout/
WriteTimeout/MinRate/MaxWriteChunk pace both directions, and write
pacing has nothing to do with the request body. Move all of it to a
dedicated http.handlers.timeouts module instead, mirroring the
server-wide timeouts option one level down.
2026-08-21 21:17:26 -06:00
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
Salynn c5b66cf8ea Set the QUIC InitialPacketSize to 1200 bytes (#7886)
quic-go defaults the `InitialPacketSize` to 1280 bytes. This is used for
the size of the payload plus UDP header. When quic-go creates its
initial handshake packet, it pads it to this size as an optimization
for the anti-amplification limit (which does not really apply to
Caddy's server connections).

Tailscale uses an MTU of 1280 (the minimum IPv6 MTU) because it is a
tunnel operating in unknown environments, possibly inside other
tunnels.

When wrapped in an IP header (either v4 or v6), the 1280 byte QUIC packet
constructed by quic-go exceeds the Tailscale MTU and gets dropped. This
makes it impossible to respond to an HTTP3 connection attempt over
Tailscale when using the default `InitialPacketSize`.

We explicitly set the `InitialPacketSize` to 1200 (its minimum) to support
HTTP3 connections over Tailscale and other low MTU connections.

Once the connection is established, quic-go can perform MTU discovery to
increase the packet size to the maximum supported by the connection, so
any throughput loss due to the lowered `InitialPacketSize` is
short-lived.
2026-07-26 08:48:25 +10:00
Bluegate Studio fcba554d65 caddyhttp: New expected_underscore_headers server option (#7809)
* caddyhttp: restore allow_underscore_in_headers server option (#7808)

* caddyhttp: mark insecure_allow_underscore_in_headers as EXPERIMENTAL

* caddyhttp: replace underscore bool with expected_underscore_headers allowlist

* fix gofmt alignment in serveroptions.go

* caddyhttp: drop repeated allowlisted underscore headers

* caddyhttp: add tests for repeated-value drop and variant-drop logging
2026-06-11 22:26:21 -06:00
Matthew Holt 4fd8c87f56 caddyhttp: Default max_header_bytes to 16 KiB 2026-06-09 12:07:27 -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 5e76b5ee43 tls: add alpn to managed HTTPS records (#7653)
* tls: add alpn to managed HTTPS records

* tls: centralise HTTPS RR ALPN defaults and registration

Reuse shared protocol defaults instead of repeating the default HTTP protocol list, unify server name registration to carry ALPN in one experimental API and reuse the TLS default ALPN ordering for HTTPS RR publication

* http: centralise effective protocol resolution for HTTPS RR ALPN
2026-05-10 13:10:29 +10:00
Zen Dodd d2172bea61 chore: Fix golangci-lint 2.12.1 findings (#7690) 2026-05-07 03:40:26 -04:00
Francis Lavoie 355c178213 chore: Use atomics where appropriate (#7648)
* chore: Use atomics where appropriate

* Use atomic for shutdownAt
2026-04-25 03:47:54 -04:00
Matthew Holt eac02ee98f caddyhttp: Limit empty Host check to HTTP/1.1 2026-02-27 10:22:39 -07:00
Francis Lavoie d64c7e67a4 caddyhttp: Option to disable 0-RTT (#7485) 2026-02-16 10:20:47 -07:00
Matthew Holt 7d24124430 caddyhttp: Reject invalid Host header (fix #7449) 2026-01-30 12:24:16 -07:00
Paulo Henrique 565c1c3054 autohttps: deterministic logic and strict bind checking on Linux (#7435)
* http: fix non-deterministic auto-https and improve Linux bind matching

* docs: restore historical context about Linux bind behavior
2026-01-16 08:51:23 -07:00
Francis Lavoie 90972fbebc chore: Dumb prealloc lint fix (#7430) 2026-01-13 14:13:43 -05:00
vnxmeandMatt Holt 31960dc998 Introduce packet conn wrappers (#7180)
* packet_conn_wrappers: Initial changes

* packet_conn_wrappers: Unwrap a packet conn only if there are no wrappers

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-12-04 14:15:56 -07:00
Francis Lavoie be5f49fbeb caddyhttp: Fix logging on wildcard sites when SkipUnmappedHosts is true (#7372) 2025-12-03 18:46:11 +00:00
Kévin Dunglas b3f2db233b core: custom slog handlers for modules (log contextual data) (#7346) 2025-11-12 13:29:47 -07:00
Marten Seemann f2199d48b2 chore: update quic-go to v0.56.0, enable qlog for HTTP/3 (#7345) 2025-11-08 00:41:15 -05:00
Kévin Dunglas 8285eba842 caddyhttp: allow customizing the Server header (#7338) 2025-11-03 21:00:27 -07:00
WeidiDeng 1ce2a13ad1 caddyhttp: wrap accepted connection to suppress tls.ConnectionState (#7247) 2025-10-16 03:13:40 +00:00
Chris Seufert d7185fd002 caddyhttp: Add trusted_proxies_unix for trusting unix socket X-Forwarded-* headers (#7265) 2025-10-16 02:47:32 +00:00
joshuamcbeth de6b78009b caddyhttp: Add server options keepalive_idle and keepalive_count (#7298)
* Add Server options KeepAliveIdle (keepalive_idle) and KeepAliveCount (keepalive_count)

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

* Add Server option KeepAliveDisable (keepalive_disable)

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

* Remove Server option KeepAliveDisable (keepalive_disable), disable when interval is negative

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

* Add keepalive parameters to caddyfiletest

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

---------

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>
2025-10-14 12:03:23 -06:00
WeidiDengandMatt Holt 14a63a26b9 caddyhttp: use the new http.Protocols to handle h1, h2 and h2c requests (#6961)
* use the new http.Protocols to handle h1, h2 and h2c requests

* fix lint

* keep ConnCtxKey for now

* fix handling for h2c

* check http version while reading the connection

* check if connection implements connectionStater when it should

* add comments about either h1 or h2 must be used in the listener

* fix if check

* return a net.Conn that implements connectionStater if applicable

* remove http/1.1 from alpn if h1 is disabled

* fix matching if only h1 is enabled

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-08-22 14:30:42 +00:00
joemickyandMatt Holt 5125fbed41 use a more modern writing style to simplify code (#7182)
Signed-off-by: joemicky <joemickychang@outlook.com>
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-08-20 11:41:21 -06:00
WeidiDeng 7590c9ca1b caddyhttp: Free up quic listener when stopping (#7177) 2025-08-13 12:35:06 -06:00
Matthew Holt 9b74a53e51 Revert "logging: Always set fields func; fix #6829"
This reverts commit 932dac157a.

Somehow the code I was looking at changed when I committed, without realizing it. This has already been fixed in #6777.
2025-02-07 06:23:43 -07:00
Matthew Holt 197c564f20 caddyhttp: Set default ReadHeaderTimeout (1 min)
Ref. #6663
2024-11-19 11:24:12 -07:00
Mohammed Al Sahaf 388c7e898c metrics: move metrics up, outside servers (#6606)
* metrics: move `metrics`  up, outside `servers`

This change moves the metrics configuration from per-server level to a single config knob within the `http` app. Enabling `metrics` in any of the configured servers inside `http` enables metrics for all servers.

Fix #6604

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* normalize domain name

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-10-18 09:54:21 -06:00
WeidiDengandMatt Holt c6f2979986 caddyhttp: Close http3 server gracefully (#6213)
* close http3 server gracefully

* update server field

* update from upstream

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-10-15 21:28:20 -04:00
Aaron Paterson 4b1a9b6cc1 core: Implement socket activation listeners (#6573)
* caddy adapt for listen_protocols

* adapt listen_socket

* allow multiple listen sockets for port ranges and readd socket fd listen logic

* readd logic to start servers according to listener protocols

* gofmt

* adapt caddytest

* gosec

* fmt and rename listen to listenWithSocket

* fmt and rename listen to listenWithSocket

* more consistent error msg

* non unix listenReusableWithSocketFile

* remove unused func

* doc comment typo

* nonosec

* commit

* doc comments

* more doc comments

* comment was misleading, cardinality did not change

* addressesWithProtocols

* update test

* fd/ and fdgram/

* rm addr

* actually write...

* i guess we doin' "skip": now

* wrong var in placeholder

* wrong var in placeholder II

* update param name in comment

* dont save nil file pointers

* windows

* key -> parsedKey

* osx

* multiple default_bind with protocols

* check for h1 and h2 listener netw
2024-09-30 10:55:03 -06:00
Alexander Stecher 22c98ea165 caddyhttp: Optimize logs using zap's WithLazy() (#6590)
* uses zap's .WithLazy with a cloned request

* fixes the cloning

* adds comment explaining why cloning is faster
2024-09-26 12:23:12 -06:00
Francis Lavoie 2faeac0a10 chore: Use slices package where possible (#6585)
* chore: Use slices package where possible

* More, mostly using ContainsFunc

* Even more slice operations
2024-09-25 14:30:56 -06:00
Marten Seemann ff67b97126 caddyhttp: enable qlog, controlled by QLOGDIR env (#6581) 2024-09-21 05:47:18 +02:00
Mohammed Al Sahaf 6ab9fb6f74 ci: update the linter action version (#6575)
* ci: update the linter action version

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* exclude rule `G115`; disable deprecated linter

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-09-16 07:50:26 -06:00
Kévin Dunglas f4bf4e0097 perf: use zap's Check() to prevent useless allocs (#6560)
* perf: use zap's Check() to prevent useless allocs

* fix

* fix

* fix

* fix

* restore previous replacer behavior

* fix linter
2024-09-13 11:16:37 -06:00
WeidiDeng a8b0dfa8da go.mod: update quic-go package (#6498) 2024-08-06 22:08:32 -06:00
Matt Holt 9ba999141b caddyhttp: Trace individual middleware handlers (#6313)
* caddyhttp: Trace individual middleware handlers

* Fix typo
2024-05-18 14:48:42 -06:00
Aziz RmadiandFrancis Lavoie 4356635d12 logging: Add support for additional logger filters other than hostname (#6082)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-05-11 13:31:44 +00:00
Matthew Holt 4af38e5ac8 caddyhttp: Log 4xx as INFO; 5xx as ERROR (close #6106) 2024-05-10 15:52:50 -06:00
Matthew Holt 399186abfc Second half of 6dce493
Not sure how it got unstaged
2024-05-10 15:51:28 -06:00
Matthew Holt 2fc620d38d caddyhttp: Fix linter warning about deprecation 2024-04-27 12:41:17 -06:00
Matthew Holt a46ff50a1c go.mod: Upgrade to quic-go v0.43.0 2024-04-27 12:01:30 -06:00
Francis Lavoie 726a9a8fde logging: Fix default access logger (#6251)
* logging: Fix default access logger

* Simplify logic, remove retry without port, reject config with port, docs

* Nil check
2024-04-22 06:33:07 -06:00
WeidiDeng e0daa39cd3 caddyhttp: record num. bytes read when response writer is hijacked (#6173)
* record the number of bytes read when response writer is hijacked

* record body size when not nil
2024-04-17 15:00:37 +00:00
Francis Lavoie 70953e873a caddyhttp: Support multiple logger names per host (#6088)
* caddyhttp: Support multiple logger names per host

* Lint

* Add adapt test

* Implement "string or array" parsing, keep original `logger_names`

* Rewrite adapter test to be more representative of the usecase
2024-04-16 22:26:18 +00:00
WeidiDeng 924010cd3d caddyhttp: close quic connections when server closes (#6202)
* close quic connections when server closes

* fix lint

* add comment about CloseGracefully
2024-03-29 11:51:46 -06:00
32f7dd44ae chore: Update quic-go to v0.42.0 (#6176)
* update quic-go to v0.42.0

* use a rate limiter to control QUIC source address verification

* Lint

* remove deprecated ListenQUIC

* remove number of requests tracking

* increase the number of handshakes before source address verification is needed

* remove references to request counters

* remove deprecated listen*

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Co-authored-by: WeidiDeng <weidi_deng@icloud.com>
2024-03-21 10:56:10 -06:00
Francis Lavoie 63d597c09d caddyhttp: Accept XFF header values with ports, when parsing client IP (#6183) 2024-03-21 10:54:25 -06:00
Francis LavoieandMatt Holt 0d44e3ecba logging: Implement log_append handler (#6066)
* logging: Implement `extra_log` handler

* Rename to `log_append`

* Rename `skip_log` to `log_skip`

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-03-05 17:03:59 -07:00