A lightweight reverse proxy built in Go.
LX-2 routes HTTP requests to configurable upstream servers based on path prefixes. It supports HTTP/1.1, HTTP/2 upstreams, WebSocket upgrades, GraphQL over HTTP, and raw TCP/UDP traffic through dedicated listeners.
LX-2 routes client requests to configured application servers.
Client -> Leoxy -> Application servers
docker pull mohamed0699/leoxy:latest
docker run --rm -p 80:80 -v "${PWD}/leoxy:/app/leoxy" mohamed0699/leoxy:latestservices:
leoxy:
image: mohamed0699/leoxy:latest
ports:
- "80:80"
volumes:
- ./leoxy:/app/leoxy
restart: unless-stoppeddocker compose up -dgo build -o leoxy ./app/cli
./leoxyLeoxy reads configuration from leoxy/leoxy_config.yaml. On first start, a default config is created. See the configuration guide for the full field reference and setup instructions.
server:
port: "80"
security:
global_rate_limit: 10000
global_rate_burst: 1000
upstream:
- name: "Backend"
path: /api
servers:
- "http://localhost:7000"
- "http://localhost:7001"
security:
rate_limit: 1000
rate_limit_burst: 100
route_rate_limit: 1000
route_rate_burst: 100
- name: "Frontend"
path: /
server_url: "http://localhost:3000"
security:
rate_limit: 1000
rate_limit_burst: 500global_rate_limit and global_rate_burst define one shared HTTP limit. An upstream's security.rate_limit and rate_limit_burst define its independent per-IP limit; route_rate_limit and route_rate_burst define an additional shared per-route limit. A root frontend with no per-IP fields is protected by the global limit only. For compatibility, a non-root upstream with a missing or zero per-IP rate inherits server-level security.rate_limit and rate_limit_burst; an upstream's positive per-IP rate takes precedence.
The global bucket is charged before route dispatch, so a request rejected by a later route policy still consumes one global token. A rejection inside the route policy does not consume tokens from its other per-IP or per-route buckets.
upstream:
- name: "GameTCP"
protocol: tcp
listen: ":7001"
server_url: "127.0.0.1:7000"
- name: "MetricsUDP"
protocol: udp
listen: ":7002"
server_url: "127.0.0.1:7003"- Reverse proxy with path-based routing
- Round-robin load balancing with failover
- Health checks (
/health/live,/health/ready) - Ping endpoint (
/ping) - Request/response metrics (
/metrics) - Rate limiting (per-IP, per-route, global)
- API key and JWT authentication
- mTLS support
- Request body size limits
- CIDR allow/deny lists
- WebSocket and GraphQL support
- TCP and UDP proxying
leoxy # Run in foreground
leoxy start # Start in background
leoxy stop # Stop background process
leoxy version # Print version- Rate limits are enforced per-minute with burst capacity
- Frontend and API per-IP limits are independent; the global HTTP limit remains shared
- Redis can be used for the distributed global token bucket;
redis_fail_open: truefalls back to the local global bucket when Redis is unavailable - API keys via
X-API-Keyheader or Bearer token - JWT authentication with HS256 (requires
exp) - mTLS support behind TLS terminator
- CIDR-based access control
- Request body limits
Set secrets via environment variables: API_KEYS, JWT_SECRET, JWT_ISSUER, JWT_AUDIENCE, REDIS_PASSWORD. Override port with PORT. RATE_LIMIT remains a compatibility override for the server-level legacy per-IP policy inherited by non-root upstreams; configure current per-route policies under each upstream's security block.