BareProxy: A Small Go Web Server and Reverse Proxy That Explains Every Routing Decision
Most sites run a thin slice of nginx in front of their application: TLS termination, serving static files from a folder, a couple of routes to a backend service, health checks, and the occasional reload. The configuration grows anyway, one regular expression at a time, until nobody is quite sure which block handles a given URL, and a change that looks safe to one person breaks something in production that another person didn’t know existed.
BareProxy keeps its core to that slice and makes it answer questions. The project is designed around a single insight: if you remove regular expressions and scripting from your routing config, you can reason about it mathematically. Every matcher becomes an exact value, a prefix, or a set. The requests your site can receive fall into a finite number of classes, and the server can enumerate them.
That foundation pays off in three commands. bareproxy explain takes a URL and shows which rule matched it, which file or backend would serve it, and why the rules above it didn’t match. This is the everyday tool: you’re adding a new route or you’re looking at an error, and you ask the running config to account for what it just did. The output is plain English, not a guess or a trace you have to parse.
bareproxy why tells the story of a request that already happened, starting from the ID it carried in a response header. You pull that header from your browser’s network tab, pass it to the tool, and the server walks back through exactly which rules matched, which decisions were made, and in what order. For developers debugging a confusing behavior in production, this turns hours of log digging into a direct answer.
The interesting one is bareproxy plan. Before a config goes live, it compares the new file with the running one and lists which existing requests would change hands. If you’ve written a rule that can never match because an earlier rule takes all its traffic, you get a warning. If a change would silently flip traffic between two backends, you see it. This works because there are no regular expressions to be ambiguous about.
For static sites the core serves a folder directly, with TLS certificates from Let’s Encrypt, so a Hugo build needs no second server in front of it. Beyond that, caching headers, rate limits, request inspection, and custom response headers are modules that compile in only when you want them. You start with 5,000 lines of Go and no dependencies from outside the Go project, and you add what you need.
BareProxy is at version 0.1. The numbers on the site are design budgets: under 5,000 lines in the core, no external dependencies, measured memory and speed against nginx coming next. The demo page shows each command’s output on a sample config you can read and modify, so you can see exactly what the tool would tell you about a request.
The appeal to site operators is confidence: you can change a routing rule and know what changes before it runs. The appeal to developers is simplicity: no regex to learn, no scripting context, just rules that say what they do. For teams running multiple static sites, or any site that serves a lot of different things from one server, a reverse proxy you can reason about and trust is a rare find.