A dynamic HTTP/HTTPS reverse proxy written in Go that intelligently routes requests based on the incoming Host header or HTTP/2 :authority pseudo-header. Features dynamic SNI support, transparent proxying, a built-in dashboard UI, and Kubernetes-ready deployment.
- Dynamic Routing — Automatically routes requests based on
Hostheader (HTTP/1.1) or:authoritypseudo-header (HTTP/2) - Dynamic SNI — Automatic Server Name Indication based on request domain for proper TLS handshakes
- Transparent Proxying — Preserves original
Hostheaders for backend servers - Dashboard UI — Configurable HTML dashboard listing proxy endpoints
- Structured Logging — JSON-formatted logs with configurable levels and detailed request tracking
- Health Checks — Built-in
/healthendpoint for Kubernetes liveness/readiness probes - Graceful Shutdown — Proper signal handling (
SIGINT,SIGTERM) for clean shutdowns - Flexible Configuration — CLI flags and environment variables
- Distroless Docker Image — Minimal, secure container image
- Kubernetes Ready — Complete K8s manifests included
# Build
make build
# Run with defaults (listens on :8080, proxies to port 443 via HTTPS)
./dyn-proxy-go
# Run with custom settings
./dyn-proxy-go -target-port=443 -target-scheme=https -log-level=debug
# Or use environment variables
export TARGET_PORT=443
export TARGET_SCHEME=https
export LOG_LEVEL=debug
./dyn-proxy-go# Route to httpbin.org via Host header
curl -H "Host: httpbin.org" http://localhost:8080/get
# Route to different backends dynamically
curl -H "Host: api.github.com" http://localhost:8080/zen
curl -H "Host: httpbin.org" http://localhost:8080/ipmake docker-build
make docker-run
# With dev settings (debug logging, httpbin target)
make docker-run-devmake k8s-deploy # Deploy
make k8s-delete # TeardownAll options can be set via CLI flags or environment variables. CLI flags take precedence.
| Flag | Env Variable | Default | Description |
|---|---|---|---|
-port |
LISTEN_PORT |
8080 |
Proxy listen port |
-target-host |
TARGET_HOST |
example.com |
Fallback target host |
-target-port |
TARGET_PORT |
443 |
Target port to proxy to |
-target-scheme |
TARGET_SCHEME |
https |
Target scheme (http / https) |
-sni |
SNI |
(dynamic) | SNI hostname (auto-set from request) |
-log-level |
LOG_LEVEL |
info |
Log level (debug, info, warn, error) |
-skip-tls-verify |
SKIP_TLS_VERIFY |
false |
Skip TLS certificate verification |
-enable-tls |
ENABLE_TLS |
false |
Enable TLS for the proxy itself |
-tls-cert |
TLS_CERT_FILE |
/etc/certs/tls.crt |
TLS certificate file path |
-tls-key |
TLS_KEY_FILE |
/etc/certs/tls.key |
TLS private key file path |
-read-timeout |
READ_TIMEOUT |
30 |
Read timeout (seconds) |
-write-timeout |
WRITE_TIMEOUT |
30 |
Write timeout (seconds) |
-idle-timeout |
IDLE_TIMEOUT |
120 |
Idle timeout (seconds) |
-page-title |
PAGE_TITLE |
Dashboard page title | |
-sub-title |
SUB_TITLE |
Dashboard subtitle | |
-page-gradient |
PAGE_GRADIENT |
Dashboard CSS gradient | |
-page-title-icon |
PAGE_TITLE_ICON |
Dashboard title icon | |
-proxy-list |
PROXY_LIST |
YAML proxy list for dashboard |
The proxy operates in dynamic mode by default:
- Extracts the target domain from the incoming request's
Hostheader or:authoritypseudo-header - Routes requests to the extracted domain using the configured target port and scheme
- Automatically sets SNI to match the request domain for proper TLS handshake
- Preserves the original
Hostheader for the backend server
Configure the dashboard with a YAML proxy list via the PROXY_LIST environment variable:
proxyList:
- name: My API
path: /api
- name: HTTPBin
path: /curl http://localhost:8080/health{"status":"healthy","timestamp":"2026-04-05T10:30:00Z"}JSON-formatted logs with detailed request tracking:
{"time":"2026-04-05T10:30:00Z","level":"INFO","msg":"Starting dynamic proxy server","listen_port":8080,"target_port":443,"target_scheme":"https","mode":"dynamic_host_and_sni"}
{"time":"2026-04-05T10:30:01Z","level":"INFO","msg":"Request received","method":"GET","url":"/get","request_host":"httpbin.org","host_source":"Host"}
{"time":"2026-04-05T10:30:01Z","level":"INFO","msg":"Request completed","method":"GET","url":"/get","request_host":"httpbin.org","status_code":200,"duration_ms":150}host_source— Whether domain came fromHostheader or:authoritypseudo-headerrequest_host— Domain extracted from the incoming requestsni_hostname— Hostname used for SNI in TLS connectionstarget_host— Targethost:portused for the upstream connection
.
├── main.go # Entry point, signal handling, graceful shutdown
├── config.go # Configuration parsing (flags + env vars)
├── server.go # ProxyServer struct, Start/Stop, template loading
├── proxy.go # Dynamic reverse proxy creation with SNI
├── handlers.go # HTTP handlers (health, dashboard, proxy)
├── models.go # Data models (ProxyEntry, ProxyListConfig)
├── proxy_test.go # Tests
├── templates/
│ └── dashboard.html # Embedded HTML template for dashboard UI
├── Dockerfile # Multi-stage distroless build
├── Makefile # Build, test, Docker, K8s commands
├── k8s/ # Kubernetes manifests
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
└── LICENSE # MIT License
- Go 1.24+
- Docker / Podman (for containerization)
- kubectl (for Kubernetes deployment)
make help # Show all available commands
make build # Build binary
make test # Run tests
make test-coverage # Run tests with coverage report
make fmt # Format code
make vet # Run go vet
make lint # Run golangci-lint
make all # Format, vet, test, and build
make release # Cross-compile for linux/darwin/windowsThe k8s/ directory contains ready-to-use manifests. Customize the ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: dyn-proxy-go-config
data:
TARGET_HOST: "your-api.example.com"
TARGET_PORT: "443"
TARGET_SCHEME: "https"
LOG_LEVEL: "info"- Distroless base image — Minimal attack surface
- Non-root user — Runs as UID 65532
- Read-only filesystem — Container filesystem is read-only
- No privilege escalation — Prevented via security context
- Resource limits — CPU and memory limits configured
This project is licensed under the MIT License.
