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. The page's
HTTP request, which ui = function(req) receives, is the only place the real
client IP survives. See the ip argument of cc_ga_js().