Which Go router should I use?
First published:
When you start to build web applications with Go, one of the first questions you'll probably ask is "which router should I use?".
It's not an easy question to answer, either. You've got http.ServeMux
in the Go standard library, and probably more than 100 different third-party routers also available — all with distinct APIs, features, and behaviors. Is http.ServeMux
going to be sufficient? Or will you need to use a different router? And if so, which one is the right choice?
For this blog post, I've evaluated 30 of the most popular third-party routers on GitHub (along with http.ServeMux
), created a shortlist of the best options, and made a comparison table you can use to help make your choice.
If you want, you can skip to the comparison table and summary.
Shortlisted routers
There are five routers which make the shortlist and that I recommend using. They are http.ServeMux
, httprouter
, chi
, flow
, and gorilla/mux
.
All the shortlisted routers are well-tested, well-documented, and actively maintained. They have stable APIs, and are compatible with http.Handler
, http.HandlerFunc
, and the standard Go middleware pattern.
There are a few common features that all five of these routers support:
Method matching: All let you register routes that require a matching HTTP method (
GET
,POST
etc).Path segment wildcards: All let you declare routes like
/movies/{id}/edit
where{id}
is a dynamic segment in the URL path.Automatic sending of 404 responses: All automatically send plaintext 404 responses when a matching route cannot be found.
Automatic sending of 405 responses: All automatically send 405 responses when a route is found with a matching URL pattern, but not a matching HTTP method. Please note though that
gorilla/mux
does not automatically include anAllow
header in 405 responses, andchi
will potentially include duplicate values in theAllow
header (there is an open issue about this here).
In terms of speed, all five routers are fast enough for (almost) every application. Unless you have profiling that confirms your router is a bottleneck in your application, I recommend choosing between them based on the specific features that you need rather than performance. I've personally used all five routers in production applications at different times and have been happy with them.
So with that out of the way, I'll start by saying…
Use the standard library if you can
If you can use http.ServeMux
, you probably should. As part of the Go standard library, it's very battle tested and well documented. Using it means that you don't need to import any third-party dependencies, and most other Go developers will also be familiar with how it works. The Go compatibility promise also means that you should be able to rely on http.ServeMux
working the same way in the long-term. All of those things are big positives in terms of application maintenance.
It also has some really nice features that don't always appear in the third-party routers.
Hostname matching:
http.ServeMux
lets you register routes that require a matching hostname, likeexample.com/post/{id}
andexample.org/post/{id}
. Hostname matching is also supported bygorilla/mux
, andchi
supports it via the additional hostrouter package.URL path sanitization:
http.ServeMux
will automatically sanitize request URL paths and redirect the client if necessary. For example, if a client makes a request to/foo/bar/..//baz
they will automatically be sent a301
redirect to/foo/baz
. URL sanitization is also done bygorilla/mux
andhttprouter
in the same way.Automatic handling of
HEAD
requests:http.ServeMux
automatically handlesHEAD
requests and sends the appropriate headers in the response. This is also supported byflow
in the same way.Overlapping routes: If you register the routes
/post/edit
and/post/{id}
, they overlap because a request to/post/edit
matches both route patterns. The way thathttp.ServeMux
matches overlapping wildcard routes is smart — the most specific matching route pattern wins and/post/edit
is more specific than/post/{id}
. This is nice because it means you can register patterns in any order and it won’t affect howhttp.ServeMux
behaves.chi
behaves in a similar-ish way tohttp.ServeMux
and will prioritize non-wildcard matches. In contrast,gorilla/mux
andflow
will dispatch requests to the first matching route, andhttprouter
simply disallows overlapping routes and will panic if you try to register them.
If you need additional features
While I recommend using http.ServeMux
as your go-to router, there may be times where you need a feature or a behavior that http.ServeMux
doesn't provide or easily support. These include:
Subsegment wildcards:
chi
is the only shortlisted router to support more than one wildcard within a single URL path segment, like/articles/{month}-{year}-{day}/{id}
.Regexp wildcards:
gorilla/mux
,chi
andflow
support regexp wildcards, like/movies/{[a-z-]+}
, where[a-z-]+
is a required regexp pattern in the URL path.Header matching:
gorilla/mux
is the only shortlisted router to easily support routing to different handlers based on the value of a request header (likeAuthorization
orContent-Type
).Custom matching rules:
gorilla/mux
is the only shortlisted router to support custom rules for matching requests (such as routing to different handlers based on IP address).Custom 404 responses: With
http.ServeMux
it's simple to implement a 'catch all' route"/"
which will send a custom 404 response, but doing this will inhibit the automatic sending of 405 responses. There's an open issue about this, and hopefully it will get resolved soon. In contrast,httprouter
,chi
,gorilla/mux
, andflow
all allow you to set custom handlers for sending 404 response without this problem.Custom 405 responses: With
http.ServeMux
there is no simple way to send custom 405 responses. Whereashttprouter
,chi
,gorilla/mux
, andflow
all allow you to set custom handlers for sending 405 responses. But be aware that bothchi
andgorilla/mux
will not automatically set anAllow
header if you are using a custom 405 handler.Automatic handling of
OPTIONS
requests: Bothhttprouter
andflow
automatically send correct responses forOPTIONS
requests.One route, multiple methods: Both
gorilla/mux
andflow
support matching multiple HTTP methods in a single route declaration.Middleware groups: Both
chi
andflow
provide 'grouping' functionality that lets you batch routes into groups that use specific middleware. Note that you can also wraphttp.ServeMux
to do this (and I've written about how to do that here).Route reversing:
gorilla/mux
is the only shortlisted router to support route reversing (like you get in Django, Rails, and Laravel).Subrouters: Both
chi
andgorilla/mux
allow the creation of 'subrouters' that can be assigned to handle a subset of your application routes.Case sensitivity: All shortlisted routers require a case-sensitive match on non-wildcard parts of a route, apart from
httprouter
which is case-insensitive.Trailing slashes: All shortlisted routers treat trailing slashes as significant (i.e.
/foo
is a different route to/foo/
). But...gorilla/mux
has an optionalStrictSlash
setting where requests to/foo
can automatically be redirected to/foo/
. In contrast,chi
has an optionalRedirectSlashes
middleware which will automatically redirect requests from/foo/
to/foo
. Andhttprouter
will automatically redirect requests from/foo
to/foo/
and vice-versa if a matching route exists — this can be disabled via theRedirectTrailingSlash
setting.
Comparison table
Summary
If the comparison table is too overwhelming, or you don't yet know what your full requirements will be, I suggest falling back to the following guidelines:
- If you know you're going to have routes with complex matching requirements (i.e. more than just simple method, wildcard segment, and hostname matching), then opt for
gorilla/mux
orchi
. - If you're building something that requires custom 404 and 405 responses, and it's important that it adheres correctly to the HTTP specs (such as a JSON API for public use), opt for
httprouter
orflow
. - If you know that your application will require a lot of route-specific middleware, opt for
chi
orflow
because of their middleware grouping functionality. - Otherwise, start with
http.ServeMux
, and refactor to use a third-party router only if there is a specific feature or behavior that you need.
Other routers
For completeness, the other routers that I evaluated are listed below, along with a short note to explain why they didn't made the shortlist.
Repository | Notes |
---|---|
celrenheit/lion | Currently unmaintained. |
claygod/Bxog | Currently unmaintained. |
clevergo/clevergo | Uses custom handler signature (not http.Handler or http.HandlerFunc). |
dimfeld/httptreemux | Doesn’t fully support http.Handler. Requires middleware for setting custom 404/405 handlers. |
donutloop/mux | Currently unmaintained. |
gernest/alien | Currently unmaintained. |
go-ozzo/ozzo-routing | Uses custom handler signature (not http.Handler or http.HandlerFunc). |
go-playground/lars | Currently unmaintained. |
go-zoo/bone | Good, but has similar use case to chi (which offers more). Incomplete tests. |
go101/tinyrouter | Verbose route declarations. Doesn’t automatically send 405 responses. |
gocraft/web | Currently unmaintained. |
goji/goji | Slightly unusual, but flexible, API which supports custom matchers. Requires middleware for setting custom 404/405 handlers. Good, but I think gorilla/mux offers similar features and is easier to use. |
goroute/route | Uses custom handler signature (not http.Handler or http.HandlerFunc). |
gowww/router | Good, but has similar use case to chi (which offers more). No way to set custom 405 handler. |
GuilhermeCaruso/bellt | No way to set custom 404 or 405 handlers. |
husobee/vestigo | Currently unmaintained. Only supports http.HandlerFunc. |
naoina/denco | Currently unmaintained. |
nbari/violetear | Good, but has similar use case to chi (which offers more). Wraps http.ResponseWriter with own custom type, which may cause problems in some cases. |
nbio/hitch | Lacking documentation. |
nissy/bon | Currently unmaintained. |
razonyang/fastrouter | Currently unmaintained. |
rs/xmux | Currently unmaintained. Uses custom handler signature (not http.Handler or http.HandlerFunc). |
takama/router | Currently unmaintained. |
vardius/gorouter | Good, but has similar use case to chi (which offers more). Four major versions in 5 years suggests the API may not be reliable. |
VividCortex/siesta | Good, but has similar use case to chi (which offers more). No way to set custom 405 handler. |
xujiajun/gorouter | Currently unmaintained. |