caddyserver/caddy
Listeners
Caddy's socket layer. Lives mostly in listeners.go (~25 KB), with platform shims in listen.go, listen_unix.go, listen_unix_setopt*.go, listen_reuseUnixSocket*.go.
Purpose
Provide a uniform way for apps to obtain net.Listener and net.PacketConn instances by address string, with:
- Cross-platform handling (
unix,tcp,udp— Linux, macOS, Windows, FreeBSD). - Process-level socket sharing via
SO_REUSEPORTso reloads can hand off without dropping connections. - A pluggable wrapper layer so listeners can be decorated with PROXY-protocol parsing, HTTP→HTTPS redirect, TLS termination, etc.
- HTTP/3 / QUIC (
net.PacketConn) support viaquic-go.
Directory layout
| Path | Role |
|---|---|
listeners.go |
NetworkAddress, ParseNetworkAddress, Listen/ListenPacket, listener wrapper interface |
listeners_test.go, listeners_fuzz.go |
Tests and a fuzz harness for address parsing |
listen.go |
Platform-agnostic Listen helpers |
listen_unix.go |
Linux/macOS/Unix SO_REUSEPORT setup |
listen_unix_setopt.go, listen_unix_setopt_freebsd.go |
OS-specific setsockopt |
listen_reuseUnixSocket.go, listen_reuseUnixSocket_windows.go |
Graceful Unix socket reuse during reload |
modules/caddyhttp/proxyprotocol/ |
caddy.listeners.proxy_protocol listener wrapper |
modules/caddyhttp/httpredirectlistener.go |
caddy.listeners.http_redirect |
Key abstractions
| Type | Where | Description |
|---|---|---|
NetworkAddress |
listeners.go |
Parsed <network>/<host>:<startPort>-<endPort> (and Unix paths) |
Listen(ctx, network, address) |
listeners.go |
Returns a net.Listener, internally pooled |
ListenQUIC / ListenPacket |
listeners.go |
UDP listener for HTTP/3 |
ListenerWrapper |
listeners.go |
Module interface; wraps a base listener (caddy.listeners.*) |
fakeCloseListener |
listeners.go |
Lets multiple consumers "close" the same shared listener |
How it works
graph TD
Server[Server.Provision] -->|caddy.Listen| Listeners
Listeners -->|consult cache| Pool[shared listener pool<br/>keyed by network+address]
Pool -->|reuse| Sock[OS socket]
Pool -->|miss| New[new listener via SO_REUSEPORT]
New --> Sock
Server -->|wraps| Wrappers
Wrappers --> ProxyProto[proxy_protocol]
Wrappers --> Redir[http_redirect]
Wrappers --> TLS[tls wrapper]
Wrappers --> ServeLoop[serve loop]Address parsing
ParseNetworkAddress accepts strings like:
:8080,127.0.0.1:8080,[::1]:8080tcp/:8080,tcp4/0.0.0.0:8080udp/:443(HTTP/3)unix//tmp/caddy.sock:8080-8090(port range — Caddy will listen on each)
The result is a NetworkAddress that knows its network type, host, and port range. Listen opens the socket; ListenAll spans the port range.
Shared listener pool
Apps that listen on the same address (different Servers in the HTTP app, multiple in-process consumers) get the same listener back, wrapped in a fakeCloseListener whose Close just decrements a refcount. The real socket is closed only when no consumer remains.
This is the basis for graceful reload: when a new config is loaded, both old and new servers can hold the same listener for a moment. Existing connections drain on the old server while new connections go to the new one. Combined with SO_REUSEPORT (where available), even cross-process reloads keep the port bound.
Listener wrappers
Server.ListenerWrappersRaw is loaded as a list of modules in the caddy.listeners.* namespace. Each implements:
type ListenerWrapper interface {
WrapListener(net.Listener) net.Listener
}Bundled wrappers:
caddy.listeners.proxy_protocol(modules/caddyhttp/proxyprotocol/) — strips PROXY-protocol headers, exposes the real client IP.caddy.listeners.http_redirect(modules/caddyhttp/httpredirectlistener.go) — peeks at the first bytes; redirects plaintext HTTP traffic on a TLS port.caddy.listeners.tls(registered bymodules/caddyhttp/caddyhttp.go'stlsPlaceholderWrapper) — placeholder slot for TLS termination (the actual TLS listener is wired by the HTTP app, but this exists so wrappers below it know their position).
HTTP/3 / QUIC
listeners.go exposes ListenQUIC which produces a quic.EarlyListener from quic-go. The HTTP app uses this when a server's experimental_http3 flag is set or HTTP/3 is auto-enabled on a TLS-bound port. QLog can be wired through h3qlog (also imported in listeners.go).
Rate limiting on accept
listeners.go includes a rate.Limiter-backed mechanism to slow down accept loops under attack (the golang.org/x/time/rate import). This is used to keep accept-loops from hard-spinning when the kernel rejects connections faster than userspace can re-arm.
Integration points
- HTTP app: every
Servercallscaddy.Listenfor each address inlisten. The HTTP app also wiresServer.AllowH2C, HTTP/3, and TLS connection policies on top. - Listener wrapper modules: any module under
caddy.listeners.*becomes available whereverlistener_wrappersis configured. - Reload:
caddy.Loadkeeps the pool alive across configs.
Entry points for modification
- Add a listener wrapper? Implement
caddy.ListenerWrapper, registercaddy.listeners.<name>. Look atmodules/caddyhttp/proxyprotocol/for a small example. - Tune accept-rate limiting? Edit
listeners.go. The limiter is intentionally generous; tightening it without measurement is risky. - Add a new transport network? Extend
ParseNetworkAddressand the dial/listen helpers.
Related pages
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.