Reverse Proxy

journex uses WebSockets for live log streaming, so the reverse proxy must forward WebSocket upgrade requests correctly. The examples below proxy journex to http://127.0.0.1:8080. Replace the host names and upstream address to match your environment.

By default, journex expects to run at the root of an HTTP origin, such as https://journex.example.com. To serve it below a path such as https://example.com/journex, set JOURNEX_BASE_PATH=/journex and configure the reverse proxy to forward that same path prefix to journex. Do not strip the prefix in the proxy when JOURNEX_BASE_PATH is set.

Set JOURNEX_TRUST_PROXY=1 when running behind a proxy. Login attempts are rate limited per client address, and without this flag every request appears to come from the proxy itself, so all clients share a single limit: one scanner can lock out your own logins. With the flag set, journex takes the client address from the rightmost X-Forwarded-For entry, which your proxy appends and a client cannot forge. Only enable it if a proxy really is in front of journex — otherwise clients can spoof the header and evade the limit.

Apache

Requires mod_proxy, mod_proxy_http, mod_proxy_wstunnel, mod_rewrite, and an SSL configuration such as Let’s Encrypt.

<VirtualHost *:443>
	ServerName journex.example.com

	SSLEngine On
	SSLCertificateFile /etc/letsencrypt/live/journex.example.com/fullchain.pem
	SSLCertificateKeyFile /etc/letsencrypt/live/journex.example.com/privkey.pem

	ProxyPreserveHost On
	RewriteEngine On
	RewriteCond %{HTTP:Upgrade} =websocket [NC]
	RewriteCond %{HTTP:Connection} upgrade [NC]
	RewriteRule ^/(.*) ws://127.0.0.1:8080/$1 [P,L]

	ProxyPass / http://127.0.0.1:8080/
	ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>

nginx

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

server {
	listen 443 ssl http2;
	server_name journex.example.com;

	ssl_certificate /etc/letsencrypt/live/journex.example.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/journex.example.com/privkey.pem;

	location / {
		proxy_pass http://127.0.0.1:8080;
		proxy_http_version 1.1;
		proxy_set_header Host $host;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection $connection_upgrade;
	}
}

Traefik

Traefik handles WebSocket upgrades automatically when proxying the router to journex.

http:
  routers:
    journex:
      rule: Host(`journex.example.com`)
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
      service: journex

  services:
    journex:
      loadBalancer:
        servers:
          - url: http://127.0.0.1:8080

Caddy

journex.example.com {
	reverse_proxy 127.0.0.1:8080
}

WebSocket Origins

journex only accepts WebSocket connections from a page served on the same origin it was reached on, so a site you happen to visit cannot open a stream against a journex on your machine and read your journal. This matters most with JOURNEX_NO_AUTH, where no token stands in the way.

The check compares the browser’s Origin against the request’s Host, so it needs your reverse proxy to pass Host through unchanged — Apache needs ProxyPreserveHost On; Caddy, Traefik and nginx (with proxy_set_header Host $host) do this by default. To allow other origins, such as a separate frontend dev server, list them in JOURNEX_ALLOWED_ORIGINS:

JOURNEX_ALLOWED_ORIGINS=http://localhost:5173,https://logs.example.com ./journex

An explicit list replaces the same-origin default rather than adding to it. Requests without an Origin header, such as curl or wscat, are always allowed: only browsers send it, and only browsers can be tricked into sending someone else’s.