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!

Which Go router should I use?

Last updated:
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 an Allow header in 405 responses, and chi will potentially include duplicate values in the Allow 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, like example.com/post/{id} and example.org/post/{id}. Hostname matching is also supported by gorilla/mux, and chi 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 a 301 redirect to /foo/baz. URL sanitization is also done by gorilla/mux and httprouter in the same way.

  • Automatic handling of HEAD requests: http.ServeMux automatically handles HEAD requests and sends the appropriate headers in the response. This is also supported by flow 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 that http.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 how http.ServeMux behaves.

    chi behaves in a similar-ish way to http.ServeMux and will prioritize non-wildcard matches. In contrast, gorilla/mux and flow will dispatch requests to the first matching route, and httprouter 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 and flow 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 (like Authorization or Content-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, and flow 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. Whereas httprouter, chi, gorilla/mux, and flow all allow you to set custom handlers for sending 405 responses. But be aware that both chi and gorilla/mux will not automatically set an Allow header if you are using a custom 405 handler.

  • Automatic handling of OPTIONS requests: Both httprouter and flow automatically send correct responses for OPTIONS requests.

  • One route, multiple methods: Both gorilla/mux and flow support matching multiple HTTP methods in a single route declaration.

  • Middleware groups: Both chi and flow provide 'grouping' functionality that lets you batch routes into groups that use specific middleware. Note that you can also wrap http.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 and gorilla/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 optional StrictSlash setting where requests to /foo can automatically be redirected to /foo/. In contrast, chi has an optional RedirectSlashes middleware which will automatically redirect requests from /foo/ to /foo. And httprouter will automatically redirect requests from /foo to /foo/ and vice-versa if a matching route exists — this can be disabled via the RedirectTrailingSlash 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 or chi.
  • 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 or flow.
  • If you know that your application will require a lot of route-specific middleware, opt for chi or flow 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.