# Proxy
Chevereto relies on the visitor IP address for flood protection, IP bans, login and signup throttling, upload attribution and stats. When Chevereto runs behind a reverse proxy, CDN or tunnel, the TCP peer (REMOTE_ADDR) is the proxy rather than the visitor, so you need to tell Chevereto which header holds the visitor IP and which proxies are allowed to set it.
# Environment variables
| Variable | Default | Description |
|---|---|---|
CHEVERETO_HEADER_CLIENT_IP | (empty) | Header that carries the visitor IP. When empty, REMOTE_ADDR is used and all headers are ignored. |
CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES | (empty) | Comma-separated IPs, CIDR ranges and keywords for the proxies allowed to set that header. |
CHEVERETO_HEADER_FORWARDED_HOST | HTTP_X_FORWARDED_HOST | Header that carries the original host. Only used when the router secret matches. |
CHEVERETO_HEADER_ROUTER_SECRET | HTTP_X_ROUTER_SECRET | Header that carries the router secret. |
CHEVERETO_ROUTER_SECRET | (empty) | Shared secret that enables the forwarded host header. Empty disables it. |
CHEVERETO_HTTPS | Detected | Set to 1 to force HTTPS URLs, 0 to force HTTP. |
# Header name
CHEVERETO_HEADER_CLIENT_IP accepts the header name in any of these forms, which all resolve to the same header:
X-Forwarded-Forx_forwarded_forHTTP_X_FORWARDED_FOR
Common values are X-Forwarded-For, X-Real-IP, CF-Connecting-IP and True-Client-IP.
# Trusted proxies
CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES takes a comma-separated list. Each entry can be:
| Entry | Example | Meaning |
|---|---|---|
| IPv4 address | 203.0.113.7 | A single proxy. |
| IPv6 address | 2001:db8::7 | A single proxy. |
| CIDR range | 172.16.0.0/12, fd00::/8 | Any proxy in the range. |
private | private | Loopback and private network ranges (below). |
cloudflare | cloudflare | Cloudflare's published edge ranges (below). |
When the variable is empty, Chevereto uses a default based on the header:
| Header | Default trusted proxies |
|---|---|
CF-Connecting-IP | cloudflare |
| Any other header | private |
When the variable is set, it replaces the default entirely. For example, CHEVERETO_HEADER_CLIENT_IP=CF-Connecting-IP with CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES=127.0.0.1 no longer trusts Cloudflare's edge. Add cloudflare to the list to keep trusting it.
# private ranges
| Range | Covers |
|---|---|
127.0.0.0/8 | IPv4 loopback |
::1 | IPv6 loopback |
10.0.0.0/8 | RFC 1918 (Kubernetes pod networks, many VPCs) |
172.16.0.0/12 | RFC 1918 (Docker bridge and user-defined networks) |
192.168.0.0/16 | RFC 1918 (LANs) |
fc00::/7 | RFC 4193 IPv6 unique local addresses |
# Setups
# No proxy
Visitors connect to the web server that runs Chevereto.
CHEVERETO_HEADER_CLIENT_IP=
Leave the header empty. Chevereto uses REMOTE_ADDR and ignores any client IP header, including CF-Connecting-IP and X-Forwarded-For.
# Reverse proxy on the same host or private network
Nginx, Apache, Caddy, HAProxy, Traefik or nginx-proxy in front of Chevereto, either on the same machine or in the same Docker, Kubernetes or private network.
CHEVERETO_HEADER_CLIENT_IP=X-Forwarded-For
The default private trust covers loopback, Docker networks, Kubernetes pod networks and LANs. To trust only your proxy, set its address or range:
CHEVERETO_HEADER_CLIENT_IP=X-Forwarded-For
CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES=172.18.0.0/16
Make sure the proxy appends the peer address to X-Forwarded-For. With Nginx:
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
Apache mod_proxy, Caddy reverse_proxy, HAProxy option forwardfor and Traefik add X-Forwarded-For by default.
# Using X-Real-IP
If your proxy sets a single-value header instead:
proxy_set_header X-Real-IP $remote_addr;
CHEVERETO_HEADER_CLIENT_IP=X-Real-IP
The proxy must overwrite this header on every request, never pass a visitor-supplied value through.
# Cloudflare in front of the origin
Cloudflare proxies visitors straight to the server that runs Chevereto.
CHEVERETO_HEADER_CLIENT_IP=CF-Connecting-IP
The default trust for CF-Connecting-IP is Cloudflare's edge ranges, so the header is only read when the request comes from Cloudflare. Requests that bypass Cloudflare and hit the origin IP directly get REMOTE_ADDR.
Enterprise plans that use True-Client-IP work the same way, but you need to set the trust explicitly because the default for other headers is private:
CHEVERETO_HEADER_CLIENT_IP=True-Client-IP
CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES=cloudflare
# Cloudflare Tunnel
cloudflared runs on the same host or in the same Docker network and connects to Chevereto locally. REMOTE_ADDR is the tunnel, not Cloudflare, so you need to trust the tunnel's address:
CHEVERETO_HEADER_CLIENT_IP=CF-Connecting-IP
CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES=127.0.0.1
If cloudflared runs in a container, use its network range instead, such as 172.16.0.0/12.
If the origin is also reachable from Cloudflare's edge (a proxied DNS record as well as the tunnel), add cloudflare:
CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES=cloudflare,127.0.0.1
# Cloudflare before a reverse proxy
Cloudflare sends traffic to a reverse proxy such as Traefik, Caddy or Nginx, which forwards it to Chevereto. REMOTE_ADDR is the reverse proxy.
There are two ways to set this up. Pick one based on whether the reverse proxy can be reached without going through Cloudflare:
# Using CF-Connecting-IP
This needs no reverse proxy configuration, because proxies pass CF-Connecting-IP through unchanged. Only use it when the reverse proxy accepts connections from Cloudflare's ranges alone.
CHEVERETO_HEADER_CLIENT_IP=CF-Connecting-IP
CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES=172.16.0.0/12
Replace 172.16.0.0/12 with your reverse proxy's address or range.
[!WARNING] Chevereto takes
CF-Connecting-IPas-is from the trusted reverse proxy, and can't tell whether the request went through Cloudflare first. If anyone can connect to the reverse proxy directly, they can sendCF-Connecting-IPwith any IP they want.Restrict the reverse proxy to Cloudflare's ranges with a firewall or an IP allow list. For multi-tenant Traefik, see Multi-tenant Traefik.
# Using X-Forwarded-For
This works whether or not the reverse proxy is locked down, because Chevereto checks every hop in the chain.
CHEVERETO_HEADER_CLIENT_IP=X-Forwarded-For
CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES=cloudflare,172.16.0.0/12
Replace 172.16.0.0/12 with your reverse proxy's address or range.
A request arrives with a chain like X-Forwarded-For: 1.1.1.1, 9.9.9.9, 173.245.48.1:
173.245.48.1was appended by the reverse proxy. It's a Cloudflare edge address.9.9.9.9was appended by Cloudflare, so it's the visitor. Chevereto uses it.1.1.1.1was sent by the visitor and is ignored.
If someone bypasses Cloudflare and connects to the reverse proxy directly, the chain ends with their own address, for example 6.6.6.6, 173.245.48.1, 8.8.8.8. Chevereto uses 8.8.8.8, so faking the earlier hops gets them nothing.
This needs two things:
- The
cloudflarekeyword. Without it, the walk stops at the Cloudflare edge address and every visitor shows up as a Cloudflare IP. - The reverse proxy keeps Cloudflare's
X-Forwarded-Forand appends to it. Some proxies discard the incoming header from peers they don't trust and start a new one with just the peer address. Chevereto then sees only the Cloudflare edge IP. Configure the proxy to trust Cloudflare's ranges:
# Multiple proxy tiers
For example, a load balancer, then an ingress controller, then Chevereto. List every tier, or use private when all of them have private addresses:
CHEVERETO_HEADER_CLIENT_IP=X-Forwarded-For
CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES=private
Each tier must append to X-Forwarded-For. Chevereto skips every trusted hop from the right and uses the first untrusted one.
# Proxies with public addresses
Cloud load balancers with public IPs, CDNs other than Cloudflare (Fastly, Bunny, CloudFront, Akamai) and proxies reached over CGNAT or Tailscale are not covered by any default. List their published ranges:
CHEVERETO_HEADER_CLIENT_IP=X-Forwarded-For
CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES=203.0.113.0/24,2001:db8:100::/48,private
Keep private in the list if a local proxy also sits between the CDN and Chevereto.
# Multi-tenant Traefik
In tenants mode, Chevereto generates Traefik dynamic configuration at /_/api/4/config/traefik. These variables shape that configuration:
| Variable | Description |
|---|---|
CHEVERETO_PROXY_ENTRYPOINT | Traefik entrypoint for the generated routers. |
CHEVERETO_PROXY_IP_ALLOW_LIST | Source ranges for Traefik's ipAllowList middleware. cloudflare expands to Cloudflare's ranges. |
CHEVERETO_SERVICE_NAME | Service name of the Chevereto container (default php). |
CHEVERETO_SERVICE_PORT | Port of the Chevereto container (default 8080). |
CHEVERETO_PROXY_IP_ALLOW_LIST controls who Traefik lets in. Traefik loads the generated configuration from Chevereto itself, so the list must include the internal network Traefik uses to reach the Chevereto container, not only the public ranges:
command:
- --providers.http.endpoint=http://php:8080/_/api/4/config/traefik
For example, to admit only Cloudflare's edge plus Traefik's internal requests:
CHEVERETO_PROXY_IP_ALLOW_LIST=192.168.65.0/24,172.16.0.0/12,cloudflare
Replace 192.168.65.0/24 and 172.16.0.0/12 with the networks of your Docker host and containers. Leaving them out locks Traefik out of its own configuration.
The allow list doesn't affect client IP detection. Set CHEVERETO_HEADER_CLIENT_IP and CHEVERETO_HEADER_CLIENT_IP_TRUSTED_PROXIES as well, following Cloudflare before a reverse proxy or Reverse proxy on the same host or private network.
# HTTPS detection
Chevereto generates HTTPS URLs when any of these is true:
HTTPSserver variable isonX-Forwarded-Protoheader ishttpsCF-Visitorheader containshttps
When TLS ends at the proxy, make sure it sends X-Forwarded-Proto. If URLs still come out as http://, or you want to stop relying on headers, force it:
CHEVERETO_HTTPS=1
# Forwarded host
By default Chevereto uses the Host header as received. If a proxy rewrites Host (for example, routing many hostnames to one internal name), Chevereto can read the original host from a forwarded header instead. This only happens when the proxy also sends a shared secret:
CHEVERETO_ROUTER_SECRET=a-long-random-string
CHEVERETO_HEADER_ROUTER_SECRET=HTTP_X_ROUTER_SECRET
CHEVERETO_HEADER_FORWARDED_HOST=HTTP_X_FORWARDED_HOST
The proxy must send X-Router-Secret: a-long-random-string and X-Forwarded-Host: images.example.com. When CHEVERETO_ROUTER_SECRET is empty, the forwarded host header is ignored.
Unlike CHEVERETO_HEADER_CLIENT_IP, these two header variables take the server variable form (HTTP_ prefix, upper case, underscores).
# IP-restricted endpoints
Some internal endpoints are restricted by IP. These checks use REMOTE_ADDR, not the resolved client IP, so behind a proxy they match the proxy's address.
| Endpoint | Allowed from |
|---|---|
/_/api/4/config/traefik | private ranges |
Tenants API (/_/api/4/tenants*) | CHEVERETO_TENANTS_API_IP_ALLOW_LIST (comma-separated). Empty allows any IP. |