chi adds composition without replacing net/http
chi's best feature is what it leaves alone. A router implements http.Handler, your endpoint can remain an ordinary http.HandlerFunc, and middleware uses the familiar function that wraps one handler with another. That keeps application code usable with the Go standard library and with packages that speak the same interface. The extra layer is routing: named parameters, wildcards, regular expressions, method dispatch, route groups, subrouters, and mounted handlers.
This middle position suits services that have outgrown a hand-written ServeMux tree but do not want a framework-specific context flowing through every function. Route and Mount split a large API into smaller routers. With applies middleware to one endpoint, while Group creates a local stack for a set of routes. Route traversal also lets documentation tools inspect the tree. You choose JSON handling, validation, database access, and error conventions separately.
A 0.4 MB checkout is the point
Our measured checkout held 105 files, roughly 12,543 lines of source, and occupied 0.4 MB. The core package has no external module dependencies. That small surface makes chi easy to read when routing behavior surprises you, and it reduces the amount of framework policy carried into a service. The repository also includes middleware and examples, so the project is larger than the router alone without turning into an application platform.
The supplied middleware covers common HTTP work such as request IDs, recovery, compression, timeouts, content-type checks, concurrency limits, and route headers. Some needs live in separate go-chi repositories, including CORS, JWT authentication, rate limiting, structured logging, and generated route documentation. This split is sensible for dependency control, though it means a production service still needs an explicit package list and ownership decisions.
What happened when we ran it
Our sandbox installed commit 167e1e3 in 7 seconds with 1 package installed. It used a fresh unprivileged Debian container with 3 CPUs, 8 GB of RAM, and the golang:1.24-bookworm image. No secrets or outside services were needed. The repository scan found one CI workflow file, no Dockerfile, and no dedicated tests directory.
The build succeeded in 28 seconds. Tests completed successfully in 39 seconds, with the Go test summary reporting 4 passed and 0 failed out of 4. Those are package-level results from the supplied lab run, not a request-throughput benchmark. We did not measure latency, allocations, maximum route count, or behavior inside a larger service.
This clean run matches the project's narrow scope. There is no database migration, code generator, asset compiler, or daemon to prepare. Add the module, create a router, and pass it to an HTTP server. The current go.mod declares Go 1.24 and says chi supports the four latest major Go versions, so older enterprise toolchains need a version check before upgrading to the current line.
Client IP handling now requires a trust decision
The README deprecates the older RealIP middleware and links it to three security advisories involving spoofed addresses. Its replacements make the operator choose where the client address comes from: the socket, one trusted header, a forwarded chain with known proxy CIDRs, or a fixed count of trusted proxy hops. That is more work than dropping RealIP into a stack, and it is the correct kind of work.
Choose the middleware from the actual network path. A service directly exposed to the internet should read the remote address. A service behind Cloudflare may trust its named header only when traffic cannot bypass Cloudflare. A multi-proxy setup is safer with explicit CIDRs than an assumed hop count. The router cannot infer this topology, and a wrong choice can poison logs, rate-limit keys, and access rules.
v5.3.2 is active, with a real regex edge case open
Release v5.3.2 shipped on August 20, 2026 with routing fixes, compression changes, and more client-IP guidance. GitHub showed 22,907 stars, 2,429 forks, and 109 combined open issues and pull requests. The repository was pushed on September 30, the date of this review. Current code activity and recent issue updates are better health signals than the size of an old issue queue alone.
Open issue 1188 still deserves attention if your routes mix regular-expression placeholders with literal suffixes. On v5.3.2, the report shows /re/{name:[^.]+}.json accepting a value that crosses a slash, despite the routing documentation saying slash will not match. Most APIs can avoid that shape or add a regression test. File-like routes and security rules that rely on one path segment should test the exact patterns they deploy.
Another open report says the compression middleware can mishandle an Accept-Encoding value with q=0. That issue was filed against code before the latest release, whose notes include compression work, so verify the behavior on your pinned version rather than assuming either the report or release resolved every case. chi is small enough that these edge conditions are practical to cover with table-driven tests in your service.
Choose chi when framework neutrality has value
chi gives a Go team a better vocabulary for structuring routes without changing the types at the boundary. That pays off in libraries, incremental migrations, and services whose owners have firm preferences for logging, validation, or persistence. Our 7-second install and fully passing 4-package test run support the low-friction claim. The recent release and same-day push support continued maintenance.
The cost is assembly. Gin or Echo provides more decisions up front, which can help a team move consistently when it does not want to design its own HTTP conventions. httprouter is narrower when routing alone matters. chi is the strong default between those poles: enough structure for a large API, small enough that net/http remains the architecture rather than an implementation detail.

