WebSocker to different port

I have setup Traefik using docker and I'm trying to put my Network Video Recorder (Unifi NVR) behind reverse proxy. My current setup seems to work for the most part, except when the NVR tries to reach WebSocket through a different port. This is the docker compose for the Traefik setup:

traefik:
    image: traefik:2.3.2
    container_name: traefik
    environment:
      - NAMECHEAP_API_USER=${NAMECHEAP_API_USER}
      - NAMECHEAP_API_KEY=${NAMECHEAP_API_KEY}
    command:
    #### CLI commands that will configure Traefik (static) ####
      ## API Settings ##
      - --api.insecure=true # <== Enables insecure api
      - --api.dashboard=true # <== Enables the dashboard to view services, middlewares, routers, etc...
      - --api.debug=true # <== Enables additional endpoints for debugging and profiling
      ## Log Settings ##
      - --log.level=DEBUG # <== Setting the level of the logs
      ## Provider Settings ##
      - --providers.docker=true # <== Enables docker as a provider
      - --providers.docker.exposedbydefault=false # <== Don't expose every container to traefik, only expose enabled ones
      - --providers.file.filename=/etc/traefik/dynamic.yml # <== Reference to the dynamic configuration file
      ## Entrypoints Settings  ##
      - --entrypoints.web.address=:80 # <== Defining an entrypoint for port :80 named web
      - --entrypoints.web.http.redirections.entryPoint.to=websecure # <== Redirect web to websecure (http > https)
      - --entrypoints.web.http.redirections.entryPoint.scheme=https # <== Set HTTPS as redirection scheme
      - --entrypoints.websecure.address=:443 # <== Defining an entrypoint for https on port :443 named websecure
      ## Certificate Settings (Let's Encrypt) ##
      - --certificatesresolvers.default.acme.email=${EMAIL} # <== Setting email for certs
      - --certificatesresolvers.default.acme.storage=/etc/traefik/acme/acme.json # <== Defining acme file to store cert information
      - --certificatesresolvers.default.acme.dnschallenge=true # <== Enable DNS-01 ACME challenge  to generate and renew ACME certs
      - --certificatesresolvers.default.acme.dnschallenge.provider=namecheap # <== Set DNS-01 challenge provider
      - --certificatesresolvers.default.acme.dnschallenge.delayBeforeCheck=32 # <== Wait 32 seconds before checking propagation.
      - --certificatesresolvers.default.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53 # <== Use these to check for propagation.
      - --serversTransport.insecureSkipVerify=true # <== Disables SSL certificate verification between traefik and backend. Certificate does not need to be valid!
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ${DATADIR}/appdata/traefik/dynamic.yml:/etc/traefik/dynamic.yml
      - ${DATADIR}/appdata/traefik/acme.json:/etc/traefik/acme/acme.json
    labels:
      - "traefik.enable=true" # <== Enable traefik on itself to view dashboard and assign subdomain to view it
      - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)" # <== Setting the domain for the dashboard
      - "traefik.http.routers.traefik.service=api@internal" # <== Enabling the api to be a service to access
      - "traefik.http.routers.traefik.tls=true"
      - "traefik.http.routers.traefik.tls.certresolver=default"
      - "traefik.http.routers.traefik.tls.domains[0].main=example.com"
      - "traefik.http.routers.traefik.tls.domains[0].sans=*.example.com"
    ports:
      - 80:80
      - 443:443
      - 8080:8080
    restart: always

and this is the dynamic.yml I use to put non-docker services behind reverse proxy:

http:
  routers:
    nvr:
      rule: Host(`nvr.example.com`)
      service: nvr
      middlewares:
        - sslheader
        #- https-redirect
      tls: {}
  services:
    nvr:
      loadBalancer:
        servers:
          - url: "https://192.168.1.102:7443"
  middlewares:
    https-redirect:
      redirectScheme:
        scheme: https
        permanent: true
        port: "7443"
    sslheader:
      headers:
        customRequestHeaders:
          X-Forwarded-Proto: https

If I go to nvr.example.com I get to the web UI with valid certificate and all. But when I try to view a video stream from one of the cameras I get this error in browser console:
WebSocket connection to 'wss://nvr.example.com:7446/xxx' failed:

I assume the service tries to access WebSocket through different port than what is used to access the web UI (7446 vs 7443) and that is why it fails. How should I configure Traefik so accessing WebSocket over wss://nvr.example.com:7446 possible?

1 Like

I am having the same issue. If you find a solution please let me know.

You need to configure a new entrypoint that listens on port :7446, and add a new router that forwards to as service that listens on that port.

Could you provide an example of what you mean? I'm not sure I understand. Thanks.

Hello @shard,

An example would be to add another entrypoint listening on port 7446:

      ## Entrypoints Settings  ##
      - --entrypoints.web.address=:80 # <== Defining an entrypoint for port :80 named web
      - --entrypoints.web.http.redirections.entryPoint.to=websecure # <== Redirect web to websecure (http > https)
      - --entrypoints.web.http.redirections.entryPoint.scheme=https # <== Set HTTPS as redirection scheme
      - --entrypoints.websecure.address=:443 # <== Defining an entrypoint for https on port :443 named websecure
      - --entrypoints.video.address=:7446 # <== Defining an entrypoint for video on port :7446 named video

And then add a new router that forwards to a server that listens to that port:

http:
  routers:
    nvr:
      entrypoints:
        - "websecure"
      rule: Host(`nvr.example.com`)
      service: nvr
      middlewares:
        - sslheader
        #- https-redirect
      tls: {}
    nvrvideo:
      entrypoints:
        - "video"
      rule: Host(`nvr.example.com`)
      service: nvrvideo
      tls: {}
  services:
    nvr:
      loadBalancer:
        servers:
          - url: "https://192.168.1.102:7443"
    nvrvideo:
      loadBalancer:
        servers:
          - url: "https://192.168.1.102:7446"
  middlewares:
    https-redirect:
      redirectScheme:
        scheme: https
        permanent: true
        port: "7443"
    sslheader:
      headers:
        customRequestHeaders:
          X-Forwarded-Proto: https

I added those same changes to my files but I'm still getting that same error. I even added port mapping - 7446:7446 to the traefik container, but no luck.