Reverse proxy
GeoMetrikks works behind a TLS-terminating reverse proxy. Serve it on its
own subdomain (for example geometrikks.example.com). The frontend is
built for the site root, so subfolder setups (example.com/geometrikks/)
do not work.
Recommended settings when proxied over HTTPS:
# The session cookie is only ever sent over HTTPS.APP_SESSION_SECURE=true# Trust X-Forwarded-For from your proxy so login logging records the real# client IP. Use the narrowest range that covers the proxy. With Docker# that is the network the proxy and GeoMetrikks share; docker network# inspect <name> shows its subnet.APP_TRUSTED_PROXIES=172.18.0.0/16Docker picks that subnet when it creates the network, so a docker compose down followed by up can land on a different one and the login log goes
back to showing the proxy’s container address. Pin it in your compose file
if you do not want to chase it:
networks: default: ipam: config: - subnet: 172.18.0.0/16With OpenID Connect, OIDC_REDIRECT_URI is the public https address of the
app, and APP_SESSION_SECURE=true is required alongside it.
X-Forwarded-For is a plain header any client can send, so GeoMetrikks
only honors it when the request arrives from an address listed in
APP_TRUSTED_PROXIES; otherwise it uses the connection’s own address. Keep
the range tight: everything inside it can put arbitrary addresses in the
header.
The list has to cover every hop that adds to X-Forwarded-For before the
request reaches the app, not just the last one, because the app reads the
chain from the right and stops at the first address it does not trust.
With Cloudflare connecting to Traefik directly (no Tunnel), that is the
Docker network Traefik shares with the app plus Cloudflare’s published
ranges (https://www.cloudflare.com/ips/), and Cloudflare’s ranges must
also be on Traefik’s entrypoint (forwardedHeaders.trustedIPs, see
docs/proxy-setup.md) or the visitor never enters the chain. nginx and
SWAG with real_ip configured rewrite the peer at the proxy and hand the
app a chain that already ends in the visitor, so there the Docker range
alone is enough. A Cloudflare Tunnel is different: the peer is
cloudflared, and the visitor arrives only in CF-Connecting-IP, which
the app does not read. There the login log shows the cloudflared
address unless nginx rewrites the peer with real_ip_header CF-Connecting-IP, as the Tunnel section of docs/proxy-setup.md shows.
APP_TRUSTED_PROXIES only affects the app’s own login logging; it has no
effect on how the log parser reads your proxy’s access log files. For
getting the real visitor address into those log files, see
docs/proxy-setup.md.
The WebSocket feeds (/ws/live, /ws/crowdsec, /ws/logs) work through
the standard Upgrade/Connection proxy headers, and idle connections
survive nginx’s default proxy_read_timeout without extra tuning.
Sample nginx configs
Section titled “Sample nginx configs”SWAG (linuxserver.io)
For linuxserver SWAG, drop
this into /config/nginx/proxy-confs/geometrikks.subdomain.conf (the
GeoMetrikks container must be named geometrikks and share a Docker
network with SWAG):
## Version 2025/07/18# make sure that your geometrikks container is named geometrikks# make sure that your dns has a cname set for geometrikks
server { listen 443 ssl;# listen 443 quic; listen [::]:443 ssl;# listen [::]:443 quic;
server_name geometrikks.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
# enable for ldap auth (requires ldap-location.conf in the location block) #include /config/nginx/ldap-server.conf;
# enable for Authelia (requires authelia-location.conf in the location block) #include /config/nginx/authelia-server.conf;
# enable for Authentik (requires authentik-location.conf in the location block) #include /config/nginx/authentik-server.conf;
# enable for Tinyauth (requires tinyauth-location.conf in the location block) #include /config/nginx/tinyauth-server.conf;
location / { # enable the next two lines for http auth #auth_basic "Restricted"; #auth_basic_user_file /config/nginx/.htpasswd;
# enable for ldap auth (requires ldap-server.conf in the server block) #include /config/nginx/ldap-location.conf;
# enable for Authelia (requires authelia-server.conf in the server block) #include /config/nginx/authelia-location.conf;
# enable for Authentik (requires authentik-server.conf in the server block) #include /config/nginx/authentik-location.conf;
# enable for Tinyauth (requires tinyauth-server.conf in the server block) #include /config/nginx/tinyauth-location.conf;
include /config/nginx/proxy.conf; include /config/nginx/resolver.conf; set $upstream_app geometrikks; set $upstream_port 8000; set $upstream_proto http; proxy_pass $upstream_proto://$upstream_app:$upstream_port; }}SWAG’s stock proxy.conf already sends the WebSocket upgrade and
X-Forwarded-For headers. Set APP_TRUSTED_PROXIES to the Docker network
SWAG shares with the app (for example 172.18.0.0/16).
Plain nginx
For a regular nginx install terminating TLS in front of the app:
# The Connection header must be "upgrade" for WebSocket handshakes and# "close" otherwise; this map picks the right value per request.map $http_upgrade $connection_upgrade { default upgrade; "" close;}
server { listen 443 ssl; listen [::]:443 ssl;
server_name geometrikks.example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem;
location / { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1;
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;
# WebSocket upgrade for /ws/live, /ws/crowdsec and /ws/logs proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }}Adjust proxy_pass to wherever the app runs (container IP, another host).
With nginx on the same machine as above, set
APP_TRUSTED_PROXIES=127.0.0.1.
Notes for exposing GeoMetrikks to the internet:
/schema(the interactive API docs) and/healthdo not require login. If you don’t want them public, protect them at the proxy (an nginxlocationrule or your auth portal).- The login endpoint has no built-in rate limiting. Run fail2ban or CrowdSec against your proxy’s access logs to stop brute force at the edge.
- Use
APP_AUTH_DISABLED=trueonly when an authenticating proxy (Authelia, Tailscale, …) sits in front of the app.