Not sure how to structure your Go web application?
My new book guides you through the start-to-finish build of a real world web application in Go — covering topics like how to structure your code, manage dependencies, create dynamic database-driven pages, and how to authenticate and authorize users securely.
Take a look!
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,POSTetc).Path segment wildcards: All let you declare routes like
/movies/{id}/editwhere{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/muxdoes not automatically include anAllowheader in 405 responses, andchiwill potentially include duplicate values in theAllowheader (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.ServeMuxlets you register routes that require a matching hostname, likeexample.com/post/{id}andexample.org/post/{id}. Hostname matching is also supported bygorilla/mux, andchisupports it via the additional hostrouter package.URL path sanitization:
http.ServeMuxwill automatically sanitize request URL paths and redirect the client if necessary. For example, if a client makes a request to/foo/bar/..//bazthey will automatically be sent a301redirect to/foo/baz. URL sanitization is also done bygorilla/muxandhttprouterin the same way.Automatic handling of
HEADrequests:http.ServeMuxautomatically handlesHEADrequests and sends the appropriate headers in the response. This is also supported byflowin the same way.Overlapping routes: If you register the routes
/post/editand/post/{id}, they overlap because a request to/post/editmatches both route patterns. The way thathttp.ServeMuxmatches overlapping wildcard routes is smart — the most specific matching route pattern wins and/post/editis more specific than/post/{id}. This is nice because it means you can register patterns in any order and it won’t affect howhttp.ServeMuxbehaves.chibehaves in a similar-ish way tohttp.ServeMuxand will prioritize non-wildcard matches. In contrast,gorilla/muxandflowwill dispatch requests to the first matching route, andhttproutersimply 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:
chiis 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,chiandflowsupport regexp wildcards, like/movies/{[a-z-]+}, where[a-z-]+is a required regexp pattern in the URL path.Header matching:
gorilla/muxis the only shortlisted router to easily support routing to different handlers based on the value of a request header (likeAuthorizationorContent-Type).Custom matching rules:
gorilla/muxis 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.ServeMuxit'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, andflowall allow you to set custom handlers for sending 404 response without this problem.Custom 405 responses: With
http.ServeMuxthere is no simple way to send custom 405 responses. Whereashttprouter,chi,gorilla/mux, andflowall allow you to set custom handlers for sending 405 responses. But be aware that bothchiandgorilla/muxwill not automatically set anAllowheader if you are using a custom 405 handler.Automatic handling of
OPTIONSrequests: Bothhttprouterandflowautomatically send correct responses forOPTIONSrequests.One route, multiple methods: Both
gorilla/muxandflowsupport matching multiple HTTP methods in a single route declaration.Middleware groups: Both
chiandflowprovide 'grouping' functionality that lets you batch routes into groups that use specific middleware. Note that you can also wraphttp.ServeMuxto do this (and I've written about how to do that here).Route reversing:
gorilla/muxis the only shortlisted router to support route reversing (like you get in Django, Rails, and Laravel).Subrouters: Both
chiandgorilla/muxallow 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
httprouterwhich is case-insensitive.Trailing slashes: All shortlisted routers treat trailing slashes as significant (i.e.
/foois a different route to/foo/). But...gorilla/muxhas an optionalStrictSlashsetting where requests to/foocan automatically be redirected to/foo/. In contrast,chihas an optionalRedirectSlashesmiddleware which will automatically redirect requests from/foo/to/foo. Andhttprouterwill automatically redirect requests from/footo/foo/and vice-versa if a matching route exists — this can be disabled via theRedirectTrailingSlashsetting.
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/muxorchi. - 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
httprouterorflow. - If you know that your application will require a lot of route-specific middleware, opt for
chiorflowbecause 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. |
If you enjoyed this post...
You might like to check out my other Go tutorials on this site, or if you're after something more structured, my books Let's Go and Let's Go Further cover how to build complete, production-ready, web apps and APIS with Go.