yarp (Yet Another Reverse Proxy) is a small HTTP reverse proxy written in Go. It listens on one or more addresses, matches each request to a route by host and path, and forwards it to a backend server. Responses can be cached on disk, following the HTTP caching standard (RFC 9111).
- Entry points: listen on several addresses at once.
- Routing: by host (exact, or a
*.example.comwildcard) and path prefix. The most specific route wins. - Load balancing: round-robin across a route's servers (HTTP or HTTPS).
- Proxy headers: sets
X-Forwarded-For,X-Forwarded-HostandX-Forwarded-Proto, and removes hop-by-hop headers. - HTTP cache (on by default, stored in
.cache/):- caches
GETandHEADresponses withs-maxage,max-ageorExpires, and serves them only while fresh; - supports
Varyand sends anAgeheader on cached responses; - answers clients'
If-None-Match/If-Modified-Sincewith304 Not Modified; - never stores
no-store,private,no-cacheorSet-Cookieresponses; - clears a URL's cached responses after a successful
POST,PUT,DELETEor other unsafe request; - adds a
Cache-Statusheader (hit,fwd=missorfwd=bypass) to every response.
- caches
- OpenTelemetry: when configured, yarp sends to a collector over OTLP/HTTP:
- its logs, at the same level as the console;
- metrics every 15 s: request duration and active requests (
http.server.*), backend call duration (http.client.request.duration), cache hits, misses and bypasses (yarp.cache.requests), whether the cache is on (yarp.cache.enabled), the time yarp itself adds before sending the response headers (yarp.request.overhead) and Go runtime metrics (go.*).
- Logging: structured logs on stderr, with a configurable level (
ERRORby default). - Strict config: unknown fields and invalid values are rejected at startup.
- Graceful shutdown on
SIGINT/SIGTERM.
Build:
go build -o yarp ./cmd/yarpCreate yarp.yml:
entryPoints:
web:
address: :8080
providers:
file:
filename: routes.yml
cache:
enabled: true # optional; defaults to true
log:
level: INFO # optional; DEBUG, INFO, WARN or ERROR; defaults to ERROR
otel: # optional; turns on OpenTelemetry
endpoint: http://localhost:4318 # OTLP/HTTP collectorCreate routes.yml:
routes:
api:
host: example.com # optional
pathPrefix: /api # optional, but host or pathPrefix is required
entryPoints:
- web
servers:
- url: http://127.0.0.1:5678
- url: http://127.0.0.1:5679Run:
./yarp -config yarp.ymlA request to http://example.com:8080/api/users is forwarded to one of the two servers. Requests that match no route get 404.
demo/ is a Docker Compose stack that runs yarp with everything around it. You only need Docker.
cd demo
docker compose up --buildIt starts:
- yarp, built from the
Dockerfileand listening onlocalhost:8080. Its config isdemo/yarp.ymlanddemo/routes.yml, mounted into the container, so you can edit them and rundocker compose restart yarp. - Servers behind two routes:
/appgoes round-robin toapp-1,app-2andapp-3;- the host
docs.localhostgoes todocs.
- Grafana LGTM (
grafana/otel-lgtm) at http://localhost:3000 (loginadmin/admin). yarp sends its metrics, traces and logs there, and the dashboard incontrib/grafana/yarp.jsonis already loaded. - A traffic generator that sends a few requests a second, so the dashboard has data right away.
Things to try:
# Load balancing: the reply cycles through app-1, app-2, app-3
curl localhost:8080/app
# Host routing
curl -H 'Host: docs.localhost' localhost:8080/
# No matching route: 404
curl -i localhost:8080/nopeIn Grafana, open Dashboards → yarp for traffic, status codes, yarp's own overhead and backends. Use Explore with Tempo for traces and Loki for logs.
Stop and remove everything with docker compose down. If port 8080 or 3000 is busy, stop what uses it; for Grafana you can instead run GRAFANA_PORT=3001 docker compose up --build.
contrib/grafana/yarp.json is a Grafana dashboard for yarp's OpenTelemetry data: traffic, status codes, cache results, yarp's own overhead, backends, Go runtime, traces and logs. It expects the metrics in Prometheus, the traces in Tempo and the logs in Loki, as in the grafana/otel-lgtm image.
Import it with Dashboards → New → Import, then pick the Prometheus, Loki and Tempo data sources at the top of the dashboard.