Simple reverse proxy
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-11 18:43:46 +01:00
cmd/srp update module path and urls 2026-09-11 18:43:46 +01:00
api.go add live backend api 2019-09-13 08:31:40 -07:00
api_test.go remove API over WAN, add test 2019-09-14 14:28:18 -07:00
go.mod update module path and urls 2026-09-11 18:43:46 +01:00
go.sum update dependencies 2020-08-16 07:59:47 -07:00
LICENSE replace isc license with agpl 2026-09-11 18:19:10 +01:00
proxy.go fix deadlock on peristent http requests 2020-08-17 12:39:32 -07:00
proxy_test.go add ServeHTTP benchmark 2019-04-23 12:50:29 -07:00
README.md update module path and urls 2026-09-11 18:43:46 +01:00
security.go fix pledge, unveil issues 2020-04-04 18:53:23 -07:00
security_openbsd.go fix pledge, unveil issues 2020-04-04 18:53:23 -07:00

SRP

SRP stands for Simple Reverse Proxy. It does what it says on the tin, and not a whole lot more.

It's been used in production to distribute traffic through a ~35 server architecture for 2 years with 100% uptime. YMMV.

Features

  • Proxy requests from a host to one of many backend IPs/ports
  • Automate HTTPS with TLS termination
  • Load balance using a simple algorithm
  • Redirect to and from hosts
  • Check health automatically
  • Retrieve healthy services with an internal API
  • 0-downtime config file reloads

And nothing else.

Installing

go get evantann.com/srp/cmd/srp

Then run srp -h for usage help.

Config file format

The config file has two main parts:

  1. Services maps requests to backend services or redirects.
  2. API that restricts access via an IP subnet.
{
	"Services": {
		"www.example.com": {
			"HealthPath": "/health",
			"Backends": [
				"127.0.0.1:3001",
				"127.0.0.1:3002",
				"127.0.0.1:3003",
				"127.0.0.1:3004",
				"127.0.0.1:3005"
			]
		},
		"example.com": {
			"Redirect": {
				"URL": "https://www.example.com",
				"DiscardPath": true,
				"Permanent": true
			}
		}
	},
	"API": {
		"Subnet": "10.0.0.0/24"
	}
}

Automatic healthchecks

If you provide a HealthPath in the config file, SRP will check the health of your servers every few seconds and stop sending requests to any that fail until the health checks start succeeding. Additionally, if any single request fails, SRP will try that same request again using a different backend (3 tries max).

API

SRP includes a simple API to retrieve each services' healthy backends. Combined with something like evantann.com/lanhttp, the API enables your apps to communicate over an internal network, rather than through the public internet, without re-configuring your servers or modifying DNS.

By default the API is disabled. When configured with Subnet, the API responds to /services over the appropriate subnet with JSON resembling the following:

{
	"www.example.internal": {
		"HealthPath": "/health",
		"Backends": [
			"10.0.0.1:3000",
			"10.0.0.2:3000"
		]
	}
}

Only the healthy IPs will be returned in the API.

lanhttp or similar can help you periodically call this API to update healthy IPs and route *.internal traffic directly to the live IPs, skipping SRP entirely, to keep chatty internal networks from impacting the performance of SRP.

Why build SRP?

Complexity doesn't belong in the infrastructure layer. When something goes wrong at this level, it can be catastrophic. You need to diagnose the issue quickly and deliver a fix in minutes to meet your SLAs. A small, simple and well-tested codebase is the only way to achieve that.

HAProxy, Nginx, and Apache are very complex, which is far beyond the need of most projects. Several new Go-based reverse proxies that use autocert, such as Traefik and Caddy, are very large and complex as well, with plenty of opportunity for bugs.

Instead, SRP keeps it simple. There's a much smaller surface for bugs. It's easier and faster to debug if issues occur (especially nice when they occur in production and you need to roll out a fix quickly).