# urfave/negroni: idiomatic HTTP middleware for Go without the framework

> Negroni is a thin middleware chain for net/http, not a framework. It suits Go developers who want logging, panic recovery and static file serving without adopting a router or an opinionated application structure.

**urfave/negroni** — Idiomatic HTTP Middleware for Golang

- Repository: https://github.com/urfave/negroni
- Stars: 7,529 · Forks: 572
- Language: Go
- License: MIT
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/urfave-negroni

## The problem negroni solves: middleware without a framework

Go's net/http package gives you handlers and a ServeMux, but nothing for the cross-cutting work every HTTP service eventually needs: request logging, panic recovery, serving files from disk. The usual answer is to pick a framework, and frameworks tend to bring routing, dependency injection and a project layout along with them. Negroni takes the other position. It is, in its own words, "not a framework", but a middleware-focused library designed to work directly with net/http. The README frames the target reader precisely: someone who likes the idea of Martini but thinks it contains too much magic. If you have already chosen a router, or you plan to use the standard library's ServeMux, negroni sits in front of it and does not compete with it. The audience is Go developers building services who want the middleware layer to be explicit and small, and who are willing to wire routing themselves.

## How the bidirectional handler chain actually works

Negroni's mechanism is a single interface. A middleware implements ServeHTTP with an extra argument: the next handler in the chain.

```go
type Handler interface {
  ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
```

The README states that if a middleware has not already written to the ResponseWriter, it should call next to yield to the following handler. That single rule produces a bidirectional flow: code before the next call runs on the way in, code after it runs on the way out. A middleware can therefore time a request, mutate the response, or recover from a panic raised further down the chain. Middlewares are collected on a Negroni value with Use, and a router or mux is attached last with UseHandler. The README's Gorilla Mux example shows exactly that ordering, with the comment "router goes last". Plain http.Handler values can be mapped too, which is what makes the library interoperable with anything that satisfies the standard interface. The With method returns a new Negroni combining the receiver's handlers with additional ones, which is how the README suggests sharing a common middleware set across two configurations without mutating the original.

## Installing negroni and running a first server

The README requires Go 1.1 or later (the module file in the repository declares go 1.17) and the current major module path is github.com/urfave/negroni/v3. Older snippets you find online may still show the path without /v3, and the README explicitly recommends updating references for clarity. Install it with:

```bash
go get github.com/urfave/negroni/v3
```

The README's first example builds a standard mux, wraps it with negroni.Classic(), and listens on port 3000. Classic bundles three defaults: Recovery for panics, Logger for request and response logging, and Static for serving files from a public directory.

```go
package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni/v3"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  n := negroni.Classic()
  n.UseHandler(mux)
  http.ListenAndServe(":3000", n)
}
```

Run it with go run server.go and you have a net/http server on localhost:3000. There is also a Run convenience method: n.Run(":8080") takes an address string identical to http.ListenAndServe, and if no address is provided the PORT environment variable is used instead. The README notes that most projects should prefer passing negroni as a Handler to a configured http.Server, since that gives you ReadTimeout, WriteTimeout and MaxHeaderBytes. On Debian, the library is also packaged as golang-github-urfave-negroni-dev via apt, though the README says it was in the sid repositories at the time of writing.

## Route-specific middleware and the BYOR constraint

Negroni is BYOR: bring your own router. That is a deliberate boundary, not a missing feature, and it has consequences. Because the library has no notion of routes, there is no built-in way to attach middleware to a path pattern. The README's workaround is to construct a second Negroni for the group and mount it as the handler for a prefix, wrapping the sub-router with negroni.Wrap. For Gorilla Mux users it shows the same idea with a subrouter. This works, but it means middleware scoping is your responsibility and the composition lives in your routing code rather than in a declarative config. The second constraint is subtler. Negroni wraps the ResponseWriter so middlewares can observe status codes and bytes written, and the repository contains separate files for feature detection and for Pusher support. Any handler that needs to reach the underlying writer, for flushing a streaming response or hijacking a connection, is working through that wrapper. The README does not document those interactions, so if your service streams server-sent events or upgrades protocols, check the wrapper's behaviour before you build on Classic.

## Where negroni is the wrong choice

If you want a framework, negroni will feel like assembling furniture without instructions. Applications that need path parameters, nested route groups with per-group middleware, or built-in dependency injection are better served by a router that includes those features; negroni will not grow them, and pairing it with a router means you maintain two mental models. Teams that expect a documented upgrade path between major versions should also look carefully: the repository ships a CHANGELOG.md, but the README does not document rollback or a migration procedure, and the jump from the pre-v3 import path is handled by telling users to update their references rather than by an automated tool. Finally, if your middleware needs to terminate requests, short-circuit the chain, or inspect the response body, the bidirectional interface gives you the hook to do it, but the library provides no helpers for those patterns. You write them.

## How negroni compares with a routing framework

The nearest alternative in spirit is a full web framework in the Martini lineage, which the README itself names as the thing negroni is a reaction to. The difference is architectural. A framework owns the request lifecycle: it parses routes, resolves handler arguments, and injects dependencies, so a handler signature can ask for a parameter by name. Negroni owns nothing except the order in which your middlewares run. Your handlers keep the net/http signature, your router stays swappable, and the standard library remains the substrate. The trade is that you write the glue the framework would have written for you. If you already use a router like Gorilla Mux, adding negroni costs you one UseHandler call and gives you a consistent middleware chain. If you have no router and no strong opinion, the standard ServeMux plus negroni.Classic covers a surprising amount of ground before you need anything else.

## Maintenance, licensing and upgrade cost

The repository is not archived, and its last push was on 2026-07-26. The most recent tagged release is v3.1.1 from 2024-06-04, with v3.1.0 and v3.0.0 tagged the same day. The module path carries the /v3 major version suffix, which is the Go convention for a breaking major release, so a future v4 would arrive at a new import path rather than silently changing behaviour. The licence is MIT, which permits commercial and closed-source use provided the copyright notice and permission notice are retained; the LICENSE file at the repository root is the authoritative text, and this is a description of the licence, not legal advice. Upgrade cost is dominated by the import path change rather than by API churn: the README's notice about the former codegangsta path exists because copy-pasted snippets still circulate. Because the surface is one interface plus a handful of constructors, the practical migration burden is small, but the CHANGELOG is where you should look for behaviour changes in the bundled Logger, Recovery and Static middlewares.

## Conclusion

Adopt urfave/negroni if your service already speaks net/http and you want a composable chain for logging, recovery and static files without a framework owning your routing. Skip it if you need path parameters, route groups, or dependency injection out of the box; those are deliberately outside its scope. Before committing, verify that your imports use github.com/urfave/negroni/v3 rather than the pre-v3 path, and confirm how you intend to expose the wrapped ResponseWriter to handlers that need Flush or Hijack.

## FAQ

### How do I use negroni in a Go project?

Install it with go get github.com/urfave/negroni/v3, create a Negroni value, add middlewares with Use, attach your router or mux with UseHandler, and pass the result to http.ListenAndServe or an http.Server.

### Is negroni a framework?

No. The README states that negroni is not a framework but a middleware-focused library designed to work directly with net/http, and it brings its own router only in the sense that you supply one.

### What does negroni.Classic() include?

It bundles three default middlewares: negroni.Recovery for panic recovery, negroni.Logger for request and response logging, and negroni.Static for serving static files under the public directory.

### Which import path should I use for negroni?

The README says to use the current major module path github.com/urfave/negroni/v3, and notes that older docs and copy-pasted snippets may still show the path without /v3.

### Does negroni handle routing?

No. Negroni is BYOR, bring your own router, and the README shows integrating it with Gorilla Mux by attaching the router last with UseHandler.

## Sources

- [Issues](https://github.com/urfave/negroni/issues)
- [License: MIT](https://github.com/urfave/negroni/blob/master/LICENSE)
- [README](https://github.com/urfave/negroni/blob/master/README.md)
- [Releases](https://github.com/urfave/negroni/releases)
- [urfave/negroni on GitHub](https://github.com/urfave/negroni)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/urfave-negroni
