| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| README.md | 2026-05-22 | 5.3 kB | |
| v5.3.0 source code.tar.gz | 2026-05-22 | 101.9 kB | |
| v5.3.0 source code.zip | 2026-05-22 | 142.4 kB | |
| Totals: 3 Items | 249.7 kB | 4 | |
What's Changed
- Use strings.ReplaceAll where applicable by @JRaspass in https://github.com/go-chi/chi/pull/1046
- Propagate inline middlewares across mounted subrouters by @LukasJenicek in https://github.com/go-chi/chi/pull/1049
- add go 1.26 to ci by @pkieltyka in https://github.com/go-chi/chi/pull/1052
- Remove last uses of io/ioutil by @JRaspass in https://github.com/go-chi/chi/pull/1054
- Simplify chi.walk with slices.Concat by @JRaspass in https://github.com/go-chi/chi/pull/1053
- Apply the stringscutprefix modernizer by @JRaspass in https://github.com/go-chi/chi/pull/1051
- Bump minimum Go to 1.23, always use request.Pattern by @JRaspass in https://github.com/go-chi/chi/pull/1048
- middleware: fix httpFancyWriter.ReadFrom double-counting bytes with Tee by @alliasgher in https://github.com/go-chi/chi/pull/1085
- Fix typo in Route doc comment by @gouwazi in https://github.com/go-chi/chi/pull/1073
- fix: set Request.Pattern from RoutePattern() by @leno23 in https://github.com/go-chi/chi/pull/1097
- feat: middleware.ClientIP, a replacement for middleware.RealIP by @VojtechVitek in https://github.com/go-chi/chi/pull/967
New Contributors
- @LukasJenicek made their first contribution in https://github.com/go-chi/chi/pull/1049
- @alliasgher made their first contribution in https://github.com/go-chi/chi/pull/1085
- @gouwazi made their first contribution in https://github.com/go-chi/chi/pull/1073
- @leno23 made their first contribution in https://github.com/go-chi/chi/pull/1097
SECURITY: middleware.ClientIP, a replacement for middleware.RealIP
@VojtechVitek submitted PR [#967], which introduces middleware.ClientIP — a replacement for middleware.RealIP that closes the three open spoofing advisories:
- GHSA-9g5q-2w5x-hmxf — IP spoofing via XFF in
RemoteAddrresolution (convto) - GHSA-rjr7-jggh-pgcp — RealIP allows IP spoofing via unvalidated XFF (rezmoss)
- GHSA-3fxj-6jh8-hvhx — IP spoofing in
middleware.RealIP(Saku0512, Critical / 9.3)
It also addresses issues outlined at:
- https://github.com/go-chi/chi/issues/708
- https://adam-p.ca/blog/2022/03/x-forwarded-for/
- https://github.com/go-chi/chi/issues/711
- https://github.com/go-chi/chi/issues/453
- https://github.com/go-chi/chi/pull/908
middleware.RealIP is deprecated in this PR with pointers to the new API.
The deprecation only adds a // Deprecated: doc comment; the function keeps working for backward compatibility.
Why a new middleware (not "fix RealIP in place")
RealIP has two unfixable design choices: it mutates r.RemoteAddr, and it tries to be a one-size-fits-all default by walking a hard-coded list of headers any client can supply. Per adam-p's "The perils of the 'real' client IP" (which calls chi out by name on this), there is no safe default — the user must pick their trust source explicitly.
The new API
Four middlewares, two accessors. Pick exactly one middleware based on your infrastructure, read the result with one of the two accessors:
:::go
// One of the four. There is no safe default — pick exactly one.
func ClientIPFromHeader(trustedHeader string) func(http.Handler) http.Handler
func ClientIPFromXFF(trustedIPPrefixes ...string) func(http.Handler) http.Handler
func ClientIPFromXFFTrustedProxies(numTrustedProxies int) func(http.Handler) http.Handler
func ClientIPFromRemoteAddr(h http.Handler) http.Handler
// Read the result.
func GetClientIP(ctx context.Context) string // for logs, rate-limit keys
func GetClientIPAddr(ctx context.Context) netip.Addr // for typed work
Example usage:
:::go
// Pick a single ClientIP middleware based on your deployment
// Cloudflare.
r.Use(middleware.ClientIPFromHeader("CF-Connecting-IP"))
// Nginx with ngx_http_realip_module.
r.Use(middleware.ClientIPFromHeader("X-Real-IP"))
// Apache with mod_remoteip.
r.Use(middleware.ClientIPFromHeader("X-Client-IP"))
// AWS CloudFront, or any proxy fleet with known CIDRs.
r.Use(middleware.ClientIPFromXFF(
"13.32.0.0/15", // CloudFront IPv4
"52.46.0.0/18", // CloudFront IPv4
"2600:9000::/28", // CloudFront IPv6
))
// Behind exactly 2 trusted proxies with dynamic IPs (autoscaling pools,
// ephemeral containers, dynamic CDN edges).
r.Use(middleware.ClientIPFromXFFTrustedProxies(2))
// Server directly on the public internet, no proxy in front.
r.Use(middleware.ClientIPFromRemoteAddr)
And in your handler or downstream middleware:
:::go
clientIP := middleware.GetClientIP(r.Context())
// log it, use it as a rate-limit key, etc.
Thanks to @adam-p, @c2h5oh, @rezmoss, @Saku0512, @convto, @Dirbaio, @jawnsy, @lrstanley, @mfridman, @n33pm, @pkieltyka for the prior discussions, detailed reviews, advisory reports, and test contributions that shaped this PR.
Full Changelog: https://github.com/go-chi/chi/compare/v5.2.5...v5.3.0