I replaced nginx with a reverse proxy I wrote in Go
I was learning Go and needed a project that wasn't a todo app or a REST API.
I was already running nginx on my VPS to proxy my site. So I thought: what if I just... wrote that myself?
Spoiler: it's now running in production at niixlabs.com.
Why nginx gets annoying
nginx works. It's been working for 20 years. But every time I touch it I have to re-read docs to remember why proxy_set_header Connection 'upgrade' is needed and where it goes. The config syntax feels like a language you only half-know.
I wanted something where the config looked like what it was doing.
What I built
Apex Proxy - a reverse proxy and load balancer written in Go.
Install:
go install github.com/niix-dan/apexproxy@latest
Config:
server:
http_port: 80
https_port: 443
auto_tls: true # Let's Encrypt, handled automatically
routing:
- host: niixlabs.com
path: /
strategy: single
priority: 20
targets:
- url: "http://127.0.0.1:3000"
That's it. apex start --config ./apex.yaml and it's running.
What it can do right now
- Host and path-based routing with priority ordering
- Wildcard subdomain matching (
*.domain.com) - Load balancing: weighted round-robin, ip-hash, single
- Automatic TLS via Let's Encrypt (no certbot needed)
- Per-route TLS cert support
- Real-time TUI dashboard -
apex statusshows live traffic, latency, bandwidth per route
What it can't do yet (honest list)
- Hot-reload without dropping connections
- Rate limiting (token bucket, it's next)
- Response compression
- Dynamic backend lookup via Redis
- Health checks that actively probe backends
It's a WIP. The core proxy engine works, TLS works, routing works. The rest is on the roadmap.
The part that surprised me
Writing a reverse proxy in Go is not that hard. net/http/httputil does the heavy lifting. The tricky parts were:
- TLS cert selection per hostname (SNI)
- Let's Encrypt not issuing certs for localhost (had to add a self-signed fallback for dev)
- Weighted round-robin needing a flattened target slice to work correctly with atomic counters
None of these were impossible. They just needed actual thinking.
Running it as a service
sudo nano /etc/systemd/system/apexproxy.service
[Unit]
Description=ApexProxy
After=network.target
[Service]
ExecStart=/home/ubuntu/go/bin/apexproxy start --config /home/ubuntu/go/bin/apex.yaml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl enable apexproxy
sudo systemctl start apexproxy
Done.
If you want to contribute
The codebase is small and readable. If you know Go and want to work on hot-reload, rate limiting, or the Redis dynamic lookup strategy - open an issue or a PR.
I'm still learning Go. Code review is welcome too.

Top comments (0)