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.
| Method | Enable with | Prometheus / Alloy scrape side |
|---|---|---|
| Static bearer token | METRICS_BEARER_TOKEN | authorization: { type: Bearer, credentials: <token> } |
| HTTP Basic | METRICS_BASIC_AUTH_USER + METRICS_BASIC_AUTH_PASSWORD | basic_auth: { username, password } |
| OAuth2 / OIDC JWT | METRICS_OAUTH2_{ISSUER,JWKS_URI,AUDIENCE,REQUIRED_SCOPES,ALGORITHMS} | oauth2: { client_id, client_secret, token_url, scopes } |
| IP allowlist | METRICS_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