Body
Summary
When RELAY_URL is configured with a non-default port (e.g. wss://example.com:3443), the relay correctly derives and registers the deployment community using the full authority including the port (via relay_url_authority / normalize_host in crates/buzz-relay/src/main.rs). However, NIP-98 HTTP Auth verification for the /query bridge endpoint appears to validate the request URL against a different normalization of RELAY_URL that drops the port and/or forces a fixed scheme, causing every authenticated request to fail with a URL mismatch — even though the client is using the exact RELAY_URL the deployment was configured with.
This makes it impossible to self-host a Buzz relay behind a reverse proxy on any port other than the implicit default for the scheme (443 for wss/https, 80 for ws/http).
Environment
Relay: self-hosted, ghcr.io/block/buzz:main, deployed via deploy/compose/compose.yml
Reverse proxy: Nginx, terminating TLS (Let's Encrypt), forwarding WebSocket upgrade to the relay container
Desktop: v0.5.2
RELAY_URL=wss://example.com:3443 (a port other than 443, chosen because port 443 was already in use by another site sharing the same domain)
Reproduction
Deploy relay with RELAY_URL=wss://your-domain.com:3443 (any non-default port for the scheme).
Confirm the deployment community is registered with the port included:
INFO Deployment community ensured host="your-domain.com:3443" community=""
Point Buzz Desktop's community Relay URL to wss://your-domain.com:3443, matching exactly.
Desktop connects (WebSocket upgrade succeeds, 101 Switching Protocols), then attempts NIP-42 auth / NIP-98 bridge requests.
Observe:
relay returned 401 Unauthorized: NIP-98: NIP-98 HTTP Auth verification failed:
URL mismatch: event has https://your-domain.com:3443/query,
expected https://your-domain.com/query
The "expected" URL in the error has the port stripped, despite RELAY_URL (used successfully for community host derivation) including it.
Expected behavior
NIP-98 URL validation should use the same host/authority normalization as community derivation (relay_url_authority / normalize_host), so that a RELAY_URL with an explicit non-default port is honored consistently across both code paths.
Suspected cause
Two independent normalization paths for the same RELAY_URL config value:
Community/tenant resolution (crates/buzz-relay/src/tenant.rs, relay_url_authority) — preserves the port.
NIP-98 HTTP Auth verification (bridge /query endpoint) — appears to reconstruct the "expected" URL using a default scheme/port instead of the configured RELAY_URL authority verbatim.
Additional context
This was discovered while trying to run mobile pairing (/pair, NIP-AB) over a dedicated HTTPS port on a self-hosted relay that shares its domain with another web application on port 443. Related regression also observed independently: #3514 (Mobile 0.4.12 does not decode Desktop 0.5.0 styled pairing QR) — not the same bug, but encountered in the same troubleshooting session and may be worth cross-referencing if investigating mobile pairing reliability on self-hosted deployments generally.
Happy to provide relay logs, full Nginx config, or test further changes if useful for debugging.
Body
Summary
When RELAY_URL is configured with a non-default port (e.g. wss://example.com:3443), the relay correctly derives and registers the deployment community using the full authority including the port (via relay_url_authority / normalize_host in crates/buzz-relay/src/main.rs). However, NIP-98 HTTP Auth verification for the /query bridge endpoint appears to validate the request URL against a different normalization of RELAY_URL that drops the port and/or forces a fixed scheme, causing every authenticated request to fail with a URL mismatch — even though the client is using the exact RELAY_URL the deployment was configured with.
This makes it impossible to self-host a Buzz relay behind a reverse proxy on any port other than the implicit default for the scheme (443 for wss/https, 80 for ws/http).
Environment
Relay: self-hosted, ghcr.io/block/buzz:main, deployed via deploy/compose/compose.yml
Reverse proxy: Nginx, terminating TLS (Let's Encrypt), forwarding WebSocket upgrade to the relay container
Desktop: v0.5.2
RELAY_URL=wss://example.com:3443 (a port other than 443, chosen because port 443 was already in use by another site sharing the same domain)
Reproduction
Deploy relay with RELAY_URL=wss://your-domain.com:3443 (any non-default port for the scheme).
Confirm the deployment community is registered with the port included:
INFO Deployment community ensured host="your-domain.com:3443" community=""
Point Buzz Desktop's community Relay URL to wss://your-domain.com:3443, matching exactly.
Desktop connects (WebSocket upgrade succeeds, 101 Switching Protocols), then attempts NIP-42 auth / NIP-98 bridge requests.
Observe:
relay returned 401 Unauthorized: NIP-98: NIP-98 HTTP Auth verification failed:
URL mismatch: event has
https://your-domain.com:3443/query,expected
https://your-domain.com/queryThe "expected" URL in the error has the port stripped, despite RELAY_URL (used successfully for community host derivation) including it.
Expected behavior
NIP-98 URL validation should use the same host/authority normalization as community derivation (relay_url_authority / normalize_host), so that a RELAY_URL with an explicit non-default port is honored consistently across both code paths.
Suspected cause
Two independent normalization paths for the same RELAY_URL config value:
Community/tenant resolution (crates/buzz-relay/src/tenant.rs, relay_url_authority) — preserves the port.
NIP-98 HTTP Auth verification (bridge /query endpoint) — appears to reconstruct the "expected" URL using a default scheme/port instead of the configured RELAY_URL authority verbatim.
Additional context
This was discovered while trying to run mobile pairing (/pair, NIP-AB) over a dedicated HTTPS port on a self-hosted relay that shares its domain with another web application on port 443. Related regression also observed independently: #3514 (Mobile 0.4.12 does not decode Desktop 0.5.0 styled pairing QR) — not the same bug, but encountered in the same troubleshooting session and may be worth cross-referencing if investigating mobile pairing reliability on self-hosted deployments generally.
Happy to provide relay logs, full Nginx config, or test further changes if useful for debugging.