I've been battling with understanding this for months as well, and I don't think it's possible at all in a standalone setup of Docker + Traefik.
It's a limitation on how Docker works in stand-alone mode, and how port proxying is done. Your container has an IP in a specific private network, and Docker proxies (with NAT) the port from your computer to that private IP for the container, through a docker network proxy. This means that with the NAT, the source IP is changed to the IP of what does the NAT. Which means that the Traefik container can only see the source IP of what did the NAT...
You can read-up on that using the keywords "userland-proxy" or "docker-proxy".
That NATing is at the IP layer, meaning that they can't add "X-Forwarded-For" which is an HTTP header, way above in the stack.
The only ways I can see to get the real IP are:
Disable the userland-proxy option in the docker daemon to avoid this proxying/NATing
Using networking host mode, with those possible clashes with local ports
Use networking macvlan mode so the container has its own IP in your LAN
I use Nextcloud behind Traefik. You need to configure you entrypoint to trust X-Forwarded-* headers from ip address ranges e.g.
entryPoints:
web:
address: ":80"
http:
redirections:
entrypoint:
to: websecure
scheme: https
permanent: true
websecure:
address: ":443"
forwardedHeaders:
trustedIPs:
- "127.0.0.1/32" # localhost
- "10.0.0.0/8" # swarm mode ip range
- "192.168.0.0/16" # stand-alone after 172.16.0.0/12 is exhausted
- "172.16.0.0/12" # stand-alone
The problem is that OP did not trust 172.16.0.0/12, the range the traefik proxy and nextcloud server's ips will be assigned from, therefore the X-Forwarded-For header was not trusted
@norweeg, thank you so much for adding this follow up! Hours of debugging and searching for weird behaviour on a Calibre server through Traefik led me here and completely resolved my issues. I created this account just to say thank you for this post. For anyone that follows, the docker-compose version of this is: