Rename --behind-proxy to --trusted-proxy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MREADME.md
@@ -59,7 +59,7 @@ Every flag can also be set by its environment variable. `otserver --help` lists
| `--web-dir` | `OT_WEB_DIR` | `web/dist` | built web UI |
| `--public-url` | `OT_PUBLIC_URL` | | the address browsers use, see below |
| `--retention-days` | `OT_RETENTION_DAYS` | `30` | days to keep points, `0` keeps them forever. Users can choose a shorter time. Each device keeps its newest point, so it stays on the map. |
| `--behind-proxy` | `OT_BEHIND_PROXY` | off | the server is reachable only through one reverse proxy, see below |
| `--trusted-proxy` | `OT_TRUSTED_PROXY` | off | the server is reachable only through one reverse proxy, see below |
`--public-url` matters behind a reverse proxy:
- Passkeys are bound to this address. Without it the server uses the request's Host header and assumes plain HTTP.
@@ -107,7 +107,7 @@ track.example.com {
HTTP/3 needs UDP 443 open next to TCP 443. `curl --http3-only -sI https://track.example.com/healthz` prints `HTTP/3 200` when it works.
Set `OT_BEHIND_PROXY=true`, so the password limits see the real client address. The server then takes the last `X-Forwarded-For` entry, the one the proxy wrote. Clients must not reach the server port directly, or they could set that header themselves. With several proxies in a row, the limits see the outer proxy's address.
Set `OT_TRUSTED_PROXY=true`, so the password limits see the real client address. The server then takes the last `X-Forwarded-For` entry, the one the proxy wrote. Clients must not reach the server port directly, or they could set that header themselves. With several proxies in a row, the limits see the outer proxy's address.
## API
Mcompose.yaml
@@ -6,7 +6,7 @@ services:
environment:
OT_PUBLIC_URL: https://track.example.com
# Only the reverse proxy reaches the published port, so its X-Forwarded-For header can be trusted.
OT_BEHIND_PROXY: "true"
OT_TRUSTED_PROXY: "true"
OT_RETENTION_DAYS: 30
volumes:
- ./data:/data
Mcrates/server/src/auth.rs
@@ -253,7 +253,7 @@ impl Limiter {
}
}
/// The client's address. With `--behind-proxy`, the last `X-Forwarded-For` entry.
/// The client's address. With `--trusted-proxy`, the last `X-Forwarded-For` entry.
pub struct ClientIp(pub IpAddr);
/// The last `X-Forwarded-For` entry, across all header lines. The proxy appends the address it saw,
@@ -283,7 +283,7 @@ impl FromRequestParts<AppState> for ClientIp {
.get::<ConnectInfo<SocketAddr>>()
.map(|c| c.0.ip())
.ok_or_else(|| Error::Internal("no connect info".into()))?;
Ok(ClientIp(match state.behind_proxy {
Ok(ClientIp(match state.trusted_proxy {
true => forwarded_ip(peer, &parts.headers),
false => peer,
}))
Mcrates/server/src/main.rs
@@ -289,8 +289,8 @@ struct Cli {
retention_days: i64,
/// Set when one reverse proxy forwards all traffic. Rate limits then use the last X-Forwarded-For entry.
/// Clients must not reach the server port directly, or they can set that header themselves.
#[arg(long, env = "OT_BEHIND_PROXY")]
behind_proxy: bool,
#[arg(long, env = "OT_TRUSTED_PROXY")]
trusted_proxy: bool,
#[command(subcommand)]
command: Option<Command>,
}
@@ -321,7 +321,7 @@ pub struct AppState {
public_url: Option<Url>,
/// 0 means forever.
max_retention_days: i64,
behind_proxy: bool,
trusted_proxy: bool,
}
impl AppState {
@@ -501,7 +501,7 @@ async fn serve(cli: Cli, db: Connection) {
pairings: Arc::default(),
public_url: cli.public_url,
max_retention_days: cli.retention_days.max(0),
behind_proxy: cli.behind_proxy,
trusted_proxy: cli.trusted_proxy,
};
tokio::spawn(cleanup(state.clone()));
@@ -609,7 +609,7 @@ pub fn test_state() -> AppState {
pairings: Arc::default(),
public_url: None,
max_retention_days: 0,
behind_proxy: false,
trusted_proxy: false,
}
}