Skip to content

Reverse proxy

The browser reaches the gateway through your existing LibreNMS vhost. One path needs proxying: /webterm/ws.

Three settings below are load-bearing. Omitting any of them produces a symptom that looks like a bug somewhere else, and between them they account for most reported problems.

librenms-webterm-setup can do this for you

It writes these stanzas to a file of its own and adds one include inside your LibreNMS server block, behind a # librenms-webterm (managed) marker comment. Your vhost is backed up first and restored if nginx -t or apachectl configtest rejects the result. To detach later, delete the marker and the include beneath it and reload.

Read on if you would rather place them yourself, or need to understand what each line is doing.

nginx

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    # ... your existing LibreNMS configuration ...

    location ^~ /webterm/ws {
        # The trailing /ws is load-bearing. Without a URI component nginx
        # forwards the original path, and the gateway -- which serves /ws, not
        # /webterm/ws -- answers 404.
        proxy_pass http://127.0.0.1:8377/ws;

        # HTTP/1.0 has no Upgrade semantics, and nginx talks 1.0 upstream by
        # default. Without this the handshake simply never completes.
        proxy_http_version 1.1;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_set_header Host       $host;
        proxy_set_header X-Real-IP  $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # A terminal is an interactive stream. Buffered, it arrives in bursts
        # and feels broken.
        proxy_buffering off;

        # THE DEFAULT IS 60 SECONDS. Without raising it, every idle terminal
        # dies after a minute and presents as an unexplained hang.
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_connect_timeout 10s;

        proxy_socket_keepalive on;
    }

    location ^~ /webterm/ui/ {
        proxy_pass http://127.0.0.1:8377/ui/;
        proxy_set_header Host $host;
    }
}

Use the map rather than a literal Connection "upgrade": a literal breaks keepalive for every non-WebSocket request routed through the same block.

# as root
nginx -t && systemctl reload nginx

Apache

# a2enmod proxy proxy_http proxy_wstunnel
ProxyPreserveHost On

ProxyPass        /webterm/ws  ws://127.0.0.1:8377/ws  upgrade=websocket timeout=3600
ProxyPassReverse /webterm/ws  ws://127.0.0.1:8377/ws

ProxyPass        /webterm/ui/ http://127.0.0.1:8377/ui/
ProxyPassReverse /webterm/ui/ http://127.0.0.1:8377/ui/

Order matters: these must come before any catch-all ProxyPass for LibreNMS itself.

Since httpd 2.4.47, mod_proxy_http handles the upgrade and mod_proxy_wstunnel hands over to it. Never use upgrade=ANY — the Apache documentation says plainly it is not recommended in production.

HTTP/2

HTTP/2 has no Upgrade header, and mod_proxy_wstunnel does not implement RFC 8441. Browsers negotiate WebSockets over HTTP/1.1, so this normally just works — but if something forces h2 for the upgrade request, it fails confusingly. Keep http/1.1 in your Protocols list.

Traefik

labels:
  - traefik.http.routers.webterm.rule=Host(`librenms.example.com`) && PathPrefix(`/webterm/`)
  - traefik.http.services.webterm.loadbalancer.server.port=8377
  - traefik.http.middlewares.webterm-strip.stripprefix.prefixes=/webterm
  - traefik.http.routers.webterm.middlewares=webterm-strip

Traefik handles WebSocket upgrades without extra configuration. Its default respondingTimeouts.readTimeout of 0 means no timeout, which is what you want here.

Verifying

Open the browser network tab and look at /webterm/ws. A working proxy shows 101 Switching Protocols. Anything else is diagnosed in gateway troubleshooting.