Skip to content
elrefai99Public

About

Lightweight of Proxy service exposes a small HTTP reverse proxy that forwards requests to configured upstream servers.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Leoxy (LX-2)

LX-2 is a lightweight reverse proxy built in Go. It routes HTTP requests to configurable upstream servers based on path prefixes, and can also forward raw TCP and UDP traffic through dedicated listeners.

Deployment architecture

LX-2 is designed to run behind Nginx:

Client -> Nginx -> LX-2 -> Application servers

Nginx is responsible for public HTTPS termination and forwarding requests to LX-2 on a private network. LX-2 applies request-body and rate limits, then routes approved requests to the configured application servers.

Do not expose LX-2 directly to the public Internet when Nginx is the TLS terminator. Bind it to a private interface or firewall its port so only Nginx can reach it.

Request-body limits

Set server.security.max_body for a global maximum request-body size in megabytes. Set upstream[].body or upstream[].security.max_body to apply a limit to one upstream. Requests above the configured limit are rejected with 413 Request Entity Too Large before they reach the application server.

Rate limits

LX-2 supports per-IP, per-route, and global rate limits. Configure them under server.security for proxy-wide limits and upstream[].security for route-specific limits. Rates are requests per minute and burst values define the initial request capacity.

When Nginx forwards traffic, ensure LX-2 receives client-IP headers only from that trusted Nginx instance. Do not allow clients to connect directly to LX-2 and submit forwarded-IP headers.

Features

  • Reverse proxy – forwards requests to multiple backend services using httputil.ReverseProxy.
  • Load balancing – round-robin routing across multiple servers with connection-failure failover for bodyless or replayable requests.
  • Path-based routing – each upstream is mapped to a configurable path prefix; the prefix is stripped before forwarding.
  • Health checks – exposes liveness and ping endpoints for monitoring.
  • Request logging – logs method, path, remote address, and latency for every request.
  • CLI – start, stop, and run the service as a background process with cobra-based subcommands.
  • YAML configuration – upstreams and server settings are defined in leoxy/config.yaml via viper.
  • Timeouts – explicit read/write/idle timeouts and max header size for safety.

Leoxy supports HTTP/1.1 and upstream HTTP/2, WebSocket upgrades, and GraphQL over HTTP. TCP and UDP use separately configured raw listeners. HTTP/3 is not currently supported.

Configuration

Create leoxy/config.yaml:

server:
  port: "8081"

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"

  - name: "Backend"
    path: /api
    ip: true
    servers:
      - "http://localhost:7000"
      - "http://localhost:7001"

  - name: "Frontend"
    path: /
    server_url: "http://localhost:3000"
Key Description
server.port Listen address (defaults to :8080; a bare number gets : prepended).
upstream[].name Identifier for the upstream.
upstream[].path Route prefix that triggers this upstream (defaults to /).
upstream[].server_url Absolute URL of the backend; validated at startup.
upstream[].servers List of backend URLs for round-robin load balancing. Failed connections are retried when the request can be safely replayed.
upstream[].ip Flag to capture and forward the client IP.
upstream[].protocol http (default), http2, graphql, websocket, tcp, or udp. HTTP application protocols use the HTTP listener. TCP and UDP use raw listeners.
upstream[].listen Local bind address for TCP or UDP upstreams, such as :7001.

Security

Security settings can be placed under server.security for all routes or under an individual upstream[].security block. Rate limits are requests per minute; burst values are the initial token capacity. The RATE_LIMIT environment variable overrides the global per-IP rate_limit value.

server:
  security:
    rate_limit: 100
    rate_limit_burst: 20
    global_rate_limit: 1000
    global_rate_burst: 100
    max_body: 10
    allow_cidrs: ["10.0.0.0/8"]
    deny_cidrs: ["10.10.0.0/16"]
    allowed_methods: [GET, POST]
    allowed_paths: ["/api/*"]
    # Set API_KEYS, JWT_SECRET, and REDIS_PASSWORD in the environment.
    require_mtls: false
    redis_addr: "127.0.0.1:6379"
    redis_password: "replace-me"
    redis_db: 0

API keys are accepted in X-API-Key or as a bearer token. JWT authentication validates HS256 signatures and requires exp; JWT_ISSUER and JWT_AUDIENCE can enforce issuer and audience. mTLS requires the server to be deployed behind TLS with verified client certificates. Request bodies are rejected before proxying when they exceed max_body or the upstream body limit.

When redis_addr is configured, the global limit is also enforced through Redis so multiple LX-2 instances share a minute window. Redis failures return 503 rather than silently bypassing the limit.

Routes are registered only for upstreams with valid, parseable URLs.

CLI usage

See the CLI guide for build instructions, command usage, help flags, and runtime file locations.

PM2

Build the binary and start two Leoxy instances on ports 8080 and 8081:

go build -o leoxy ./app/cli
pm2 start ecosystem.config.cjs

PM2 must use fork mode because Leoxy is a Go server. Put Nginx or another load balancer in front of ports 8080 and 8081. The PORT environment variable overrides server.port for each instance.

On Windows, build leoxy.exe and change the ecosystem script value to ./leoxy.exe.

Endpoints

Ping

GET /ping

Returns PONG.

Liveness

GET /health/live

Returns {"status":"ok"}.

Readiness

GET /health/ready

Returns {"status":"ok"} when at least one server in every configured HTTP upstream group responds without a server error. Returns 503 when any group has no available server. TCP and UDP upstreams are not included.

Proxy

Each configured upstream is available at its path prefix:

/<prefix>/*  ->  upstream server_url

The prefix is stripped before forwarding. For example, with the config above, GET /api/tasks is forwarded to http://localhost:7000/tasks.

About

Lightweight of Proxy service exposes a small HTTP reverse proxy that forwards requests to configured upstream servers.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages