Issue Description
Hello,
I have a visibility issue in the logs.
Expected Behavior
remote IP should be logged and the x-forwarded-for as well , same should apply for the source IP when using proxyprotocol.
Actual Behavior
Currently I only see the Proxy IP (traefik in k8s ; 10.244.0.0/16) in the logs. And blocked IPs from x-forwarded-for but not for classic logs
HTTP listeners are configured without proxyprotocol ; but x-forwarded-for is enabled for them at global level.
While others are configured with ; at listener level, not global.
Relevant Log Output
2026-05-08T07:46:08Z INFO Authentication successful (auth.success) listenerId = “imaps”, localPort = 993, remoteIp = 10.244.4.156, remotePort = 36980, accountName = “test123”, accountId = 2, id = 1, details = “Authenticated with app password”
2026-05-08T04:30:33Z INFO Blocked IP address (security.ip-blocked) listenerId = “https”, localPort = 443, remoteIp = 10.244.2.233, remotePort = 52766, listenerId = “https”, remoteIp = 195.178.110.199
2026-05-08T09:06:44Z INFO Authentication successful (auth.success) listenerId = “http”, localPort = 8080, remoteIp = 10.244.4.149, remotePort = 38538, accountName = “[email protected]”, accountId = 1
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
PostgreSQL
Blob Storage
S3-compatible
Search Engine
PostgreSQL
Directory Backend
Internal
I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.
on
I have searched this support forum (open and closed topics) and confirm this is not a duplicate.
on
I understand that topics in this category are triaged by a bot first but a human reply will follow up. If you’d prefer a human-only reply, add the no-ai tag to your topic.
on
Based on the docs and the reported symptoms, there are two distinct issues here:
Why the proxy IP is shown instead of the real client IP
Stalwart only replaces the connection’s remoteIp with the real client IP when both of these are true:
- The Proxy Protocol header is actually being sent by Traefik on that connection.
- Stalwart is configured to trust that source address — i.e., Traefik’s pod IP/CIDR must appear in either:
proxyTrustedNetworks on the global SystemSettings singleton (Settings → Network → General in the WebUI), OR
overrideProxyTrustedNetworks on the specific NetworkListener (Settings → Network → Listeners).
Your logs show remoteIp = 10.244.4.156 etc., which are Traefik pod IPs. This means Stalwart is receiving the connection from Traefik but is not trusting that source as a proxy — so it never reads the Proxy Protocol header and just logs Traefik’s address. The fix is to add your pod CIDR (e.g. 10.244.0.0/16) to proxyTrustedNetworks or to overrideProxyTrustedNetworks on each mail listener.
HTTP listeners and X-Forwarded-For
For the HTTP/HTTPS listeners (where Traefik is doing HTTP-mode forwarding rather than TCP passthrough), Proxy Protocol is the wrong mechanism. Instead, enable useXForwarded: true on the Http singleton (Settings → Network → HTTP → General). From the docs:
The flag should not be enabled when the proxy protocol is in use.
So for HTTP listeners: use useXForwarded, not overrideProxyTrustedNetworks. For all TCP mail listeners (SMTP, IMAPS, etc.): use Proxy Protocol + trusted networks, not useXForwarded. Mixing the two on the same listener won’t work.
Summary of required changes
| Listener |
Mechanism |
Required config |
http / https |
X-Forwarded-For |
Http.useXForwarded = true |
smtp, imaps, etc. |
Proxy Protocol |
overrideProxyTrustedNetworks: ["10.244.0.0/16"] on each listener |
Also ensure Traefik is actually emitting Proxy Protocol v2 on those TCP entrypoints — the official Stalwart Traefik example uses traefik.tcp.services.<name>.loadbalancer.proxyProtocol.version=2 labels on the Stalwart container.
See the full Traefik configuration guide and Proxy Protocol docs for reference.
This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.
I confirm that 10.244.0.0/16 is set in the webui for imaps listener as **Override proxy networks.
For the proxyprotocol itself ; I have these manifests:
**
apiVersion: traefik.io/v1alpha1
kind: ServersTransportTCP
metadata:
name: proxyprotocol2
namespace: mailserver
spec:
terminationDelay: 100ms
proxyProtocol:
version: 2
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: ingressroutetcp-imaptls
namespace: mailserver
spec:
entryPoints:
- imap
routes:
-
match: HostSNI(`*`)
services:
You should not mix proxy protocol and X-Forwarded-For headers. If your reverse proxy supports the proxy protocol, then use that for all ports. If Stalwart is not seeing the real client IP it means that the proxy is not sending the proxy protocol header or that its IP is not in the trusted proxy network.