Proxy protocol and getting original clientIP at backend

My backend application is running behind the traefik which acts as an main load balancer. for our use case our requst should be end to end encrypted so we enabled ssl pass through in traefik level and use tcp router for 443.below shows our configuration for reference. (we are running in AWS ECS)

Treafik entrypoint config:

"entryPoint": [
"traefik",
"--providers.ecs.clusters",
"agentmethods-aws-staging-cluster",
"--log.level",
"DEBUG",
"--providers.ecs.region",
"us-east-1",
"--api.insecure",
"--entrypoints.websecure.address=:443",
"--entryPoints.websecure.proxyProtocol.trustedIPs=0.0.0.0/0",
"--entrypoints.web.address=:80",
"--entrypoints.redis.address=:6379",
"--log.filePath=/var/log/traefik.log"
],

Backend web application label config:
"dockerLabels": {
"traefik.http.routers.my-router.rule": "PathPrefix(/)",
"traefik.http.services.my-service.loadbalancer.server.port": "80",
"traefik.tcp.routers.agm.rule": "HostSNI(*)",
"traefik.tcp.routers.agm.tls.passthrough": "true",
"traefik.enable": "true",
"traefik.tcp.routers.agm.entrypoints": "websecure",
"traefik.tcp.services.agm-service.loadbalancer.server.port": "443",
"traefik.tcp.routers.agm.service": "agm-service",
"traefik.http.routers.my-router.entrypoints": "web",
"traefik.http.routers.my-router.service": "my-service"
},

our main issue is we are not able to get the client ip in the backend nginx log which comes as 443 port but we can able to get if it pass through 80

kindly help me on this to get the client ip and our ttaefik version is 2.10.5

Use 3 backticks before and after code/config to make it more readable and preserve spacing.

You need to be aware that proxyProtocol can be used in two areas:

incoming -> Traefik -> target service
         ^1         ^2

You enabled proxyProtocol for entrypoint. If you want to enable proxyProtocol towards the target service, check the doc.

already tried setting the service proxy protocol version but we cannot able to get the client ip in nginx

is there is any way to get the client ip from backend

@bluepuma77 is there any way to get the client ip at backend plz guide on this

If you don’t get ProxyProtocol to work, you can try a different approach. Don’t use TLS passthrough, but terminate TLS at Traefik, then use new TLS to proxy/forward to a https target service. Then Traefik should add regular http headers like X-Forwarded-For or X-Real-Ip.

@bluepuma77 our application need to be an end to end encrypted so tls termination should happen at backend level

@bluepuma77 is there any way possible to pereserve the client IP to backend with tcp router and also ssl pass through enabled?

The TCP packets are routed by the load balancer, so the IP addresses will change. There are only two ways to preserve the original IP:

  1. Use ProxyProtocol to wrap full traffic in a special stream, prefix with source IP
  2. Use HTTP headers, which requires to decrypt requests and then encrypt again

I tested the internal ProxyProtocol connection from Traefik to a target service proxyprotocol client and it works for me. You can "tunnel" an encrypted TLS request through Traefik (without decrypting) and still see the original source IP in the target service:

services:
  traefik:
    container_name: traefik
    image: traefik:v3.3
    ports:
      - 80:80
      - 8000:8000
      - 8001:8001
      - 8002:8002
    networks:
      - proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ~/certificates:/certificates
      - /var/log:/var/log
    command:
      - --api.dashboard=true
      - --log.level=INFO
      - --accesslog=true
      - --providers.docker.network=proxy
      - --providers.docker.exposedByDefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.web.asDefault=true
      - --entrypoints.plaintcp0.address=:8000
      - --entrypoints.plaintcp1.address=:8001
      - --entrypoints.plaintcp2.address=:8002
    labels:
      - traefik.enable=true
      - traefik.http.routers.mydashboard.rule=Host(`traefik.example.com`)
      - traefik.http.routers.mydashboard.service=api@internal

  echo:
    container_name: echo
    build:
      context: .
      dockerfile: Dockerfile
    networks:
      - proxy
    labels:
      # plain http over TCP
      - traefik.enable=true
      - traefik.http.routers.myecho.rule=Host(`echo.example.com`)
      - traefik.http.routers.myecho.service=myecho
      - traefik.http.services.myecho.loadbalancer.server.port=3000

      # pure TCP passthrough, no proxy protocol
      - traefik.tcp.routers.myecho0.entrypoints=plaintcp0
      - traefik.tcp.routers.myecho0.rule=HostSNI(`*`)
      - traefik.tcp.routers.myecho0.service=myecho0
      - traefik.tcp.services.myecho0.loadbalancer.server.port=3000

      # pure TCP passthrough, proxy protocol v1
      - traefik.tcp.routers.myecho1.entrypoints=plaintcp1
      - traefik.tcp.routers.myecho1.rule=HostSNI(`*`)
      - traefik.tcp.routers.myecho1.service=myecho1
      - traefik.tcp.services.myecho1.loadbalancer.server.port=3000
      - traefik.tcp.services.myecho1.loadbalancer.proxyprotocol.version=1

      # pure TCP passthrough, proxy protocol v2
      - traefik.tcp.routers.myecho2.entrypoints=plaintcp2
      - traefik.tcp.routers.myecho2.rule=HostSNI(`*`)
      - traefik.tcp.routers.myecho2.service=myecho2
      - traefik.tcp.services.myecho2.loadbalancer.server.port=3000
      - traefik.tcp.services.myecho2.loadbalancer.proxyprotocol.version=2

networks:
  proxy:
    name: proxy

@bluepuma77

"entryPoint": [
"traefik",
"--providers.ecs.clusters",
"agentmethods-aws-staging-cluster",
"--log.level",
"DEBUG",
"--providers.ecs.region",
"us-east-1",
"--api.insecure",
"--entrypoints.websecure.address=:443",
"--entryPoints.websecure.proxyProtocol.trustedIPs=0.0.0.0/0",
"--entrypoints.web.address=:80",
"--entrypoints.redis.address=:6379",
"--log.filePath=/var/log/traefik.log"
],

Backend web application label config:
"dockerLabels": {
"traefik.http.routers.my-router.rule": "PathPrefix(/)",
"traefik.http.services.my-service.loadbalancer.server.port": "80",
"traefik.tcp.routers.agm.rule": "HostSNI(*)",
"traefik.tcp.routers.agm.tls.passthrough": "true",
"traefik.enable": "true",
"traefik.tcp.routers.agm.entrypoints": "websecure",
"traefik.tcp.services.agm-service.loadbalancer.server.port": "443",
"traefik.tcp.services.agm-service.loadbalancer.proxyprotocol.version": "2",
"traefik.tcp.routers.agm.service": "agm-service",
"traefik.http.routers.my-router.entrypoints": "web",
"traefik.http.routers.my-router.service": "my-service"
},```


enabled proxy protocol still connot get the client ip and sharing my config for your reference

If you want end-to-end encryption and the Traefik load balancer has no access to the certs, then you can only use a tcp router without any TLS active, with HostSNI(`*`). This enables only a single service per port, as Traefik can not read host or path from request.

@bluepuma77

means then we cannot able to get the client ip at backend right?

You can get the IP via ProxyProtocol, works for me, check simple Traefik ProxyProtocol example.

What do you want to achieve? I understand end-to-end encryption. But will Traefik and the target service share the same TLS cert?

@bluepuma77

our behaviour is our application will handle n no of user and each individual user has different ssl (1600 approx) that are maintained by our backend nginx level. and so we cannot able to set that ssl and cannot mention specific domain in the load balancer level here (traefik). so we moved to go for tcp router which will pass through the tls without need of placing the ssl cert for the n domains.

from the above scnerio traefik is working fine to the core but , one thing is we cannot able to get the client ip at the backend beceause of tcp router, instead we are getting traefik ip . we need client ip this is the case we need to achieve .

if we set the "traefik.tcp.services.agm-service.loadbalancer.proxyprotocol.version": "2" in labels we get the below error in nginx.

2025/02/16 04:41:42 [error] 85#85: *43 broken header: "�}�`,
�q&�-3���9E:�)}�3������ r�����<�R�f53K�X��M:�������9�v�/�+�0�,��'g�(k��̩" while reading PROXY protocol, client: 10.0.1.93, server: 0.0.0.0:443

event we set like

server{
    listen 443 ssl proxy_protocol;
}

kindly guide on this and our traefik and backend is running in ecs and the configurations are shown in the previous chat fyi.

Works for me, Traefik sending ProxyProtocol with encapsulated TLS-encrypted http requests to internal target service.

The received cryptic data looks very binary, neither proxy protocol v1 or v2. It could be TLS without ProxyProtocol around.

Make sure to not set tls.passthrough: true in Traefik config, as that activates Traefik TLS handling.