Skip to main content
Version: 0.10 (Latest)

Auth on /metrics

/metrics is publicly accessible by default. The exporter ships with four optional auth methods that can be enabled via environment variables. Any combination is supported; the IP allowlist (if set) is enforced in addition to whichever token method authorizes the request.

MethodEnable withPrometheus / Alloy scrape side
Static bearer tokenMETRICS_BEARER_TOKENauthorization: { type: Bearer, credentials: <token> }
HTTP BasicMETRICS_BASIC_AUTH_USER + METRICS_BASIC_AUTH_PASSWORDbasic_auth: { username, password }
OAuth2 / OIDC JWTMETRICS_OAUTH2_{ISSUER,JWKS_URI,AUDIENCE,REQUIRED_SCOPES,ALGORITHMS}oauth2: { client_id, client_secret, token_url, scopes }
IP allowlistMETRICS_ALLOWED_IPS (comma-separated CIDRs)(network-layer; no scrape-side config)

If none of these env vars is set, /metrics stays open — current behavior, no breaking change.

Which method should I pick?

  • Static bearer token — simplest. One secret, no IdP needed. Fine for small deployments and CI scrapers.
  • HTTP Basic — compatible with older Prometheus setups that prefer basic_auth. Behavior is equivalent to static bearer.
  • OAuth2 / OIDC — best for enterprise setups with an IdP (Keycloak, Auth0, Okta, Azure AD). Tokens rotate; scopes are enforced; secrets never leave the IdP. The exporter validates the JWT against the issuer's JWKS (cached for 10 min, with automatic key rotation).
  • IP allowlist — defense in depth. Pair with a token method to require both network and credential checks, or use standalone when network isolation is your only requirement.

Prometheus scrape examples

Static bearer

scrape_configs:
- job_name: "librechat-exporter"
authorization:
type: Bearer
credentials: "your-shared-secret"
static_configs:
- targets: ["exporter:9087"]

HTTP Basic

scrape_configs:
- job_name: "librechat-exporter"
basic_auth:
username: prom
password: "your-password"
static_configs:
- targets: ["exporter:9087"]

OAuth2 (client_credentials against your IdP)

scrape_configs:
- job_name: "librechat-exporter"
oauth2:
client_id: "prometheus"
client_secret: "your-client-secret"
token_url: "https://idp.example.com/realms/main/protocol/openid-connect/token"
scopes: ["metrics:read"]
static_configs:
- targets: ["exporter:9087"]

The exporter side is then configured with:

METRICS_OAUTH2_ISSUER=https://idp.example.com/realms/main
METRICS_OAUTH2_AUDIENCE=librechat-prom-exporter
METRICS_OAUTH2_REQUIRED_SCOPES=metrics:read

Grafana Alloy

Alloy's prometheus.scrape component uses the same primitives (basic_auth, authorization, oauth2 blocks). See the Alloy prometheus.scrape reference and the Prometheus scrape_config reference.

Behind a reverse proxy

When running behind a reverse proxy (nginx, Caddy, Traefik, k8s Ingress, etc.), set TRUST_PROXY so the IP allowlist and rate limiter see real client IPs instead of the proxy:

TRUST_PROXY=true # single trusted proxy
TRUST_PROXY=10.0.0.0/8,172.16.0.0/12 # CIDR list
TRUST_PROXY=loopback # default

Separate /metrics port

For zero-trust setups, bind /metrics on a separate (typically internal-only) port via METRICS_PORT. /health stays on the main port for external load balancers; /metrics is reachable only from within the cluster.

PORT=9087 # /health, public
METRICS_PORT=9090 # /metrics, internal