Reads X-Forwarded-For (set by the Caddy reverse proxy the apps sit behind)
and falls back to the direct REMOTE_ADDR. Never errors.
Details
Pass the req of a ui function, not a session, when you can.
shiny-server does not proxy the websocket upgrade — it opens a fresh
localhost connection to the R worker — so a session's request carries no
X-Forwarded-For and its REMOTE_ADDR is always 127.0.0.1, no matter how
correctly Caddy is configured (verified on msens1: the page GET shows the real
address while the websocket handshake shows HTTP_HOST 127.0.0.1:<worker port>
and no forwarded header at all). The page's HTTP request, which
ui = function(req) receives, is the only place the real client IP survives.
See the ip argument of ga_js().
Behind Cloudflare (the signed-in preview host is proxied through its edge)
the connecting peer is a Cloudflare address, and the visitor is named by
CF-Connecting-IP; that wins when present, then the first X-Forwarded-For
hop, then REMOTE_ADDR. Analytics only – nothing here is trusted for policy.
Examples
ms_client_ip(list(request = list(HTTP_X_FORWARDED_FOR = "203.0.113.7, 10.0.0.1")))
#> [1] "203.0.113.7"
ms_client_ip(list(HTTP_X_FORWARDED_FOR = "203.0.113.7")) # a ui(req)
#> [1] "203.0.113.7"
ms_client_ip(list(HTTP_CF_CONNECTING_IP = "198.51.100.4",
HTTP_X_FORWARDED_FOR = "198.51.100.4, 172.70.0.1"))
#> [1] "198.51.100.4"