Behind a proxy
Point your proxy at the published WEB_PORT and let it terminate HTTPS. Two variables
must then follow in .env, and the stack restarted: PUBLIC_URL is the public address
you serve the app on, and COOKIE_SECURE=true marks the session cookie HTTPS-only.
What the proxy points at
Section titled “What the proxy points at”Where the proxy itself runs decides the upstream.
The published port is reached at 127.0.0.1:8080, the only address
BIND_ADDRESS=127.0.0.1 leaves it on and the setting that closes the instance to
everything but the proxy.
A containerised proxy has its own loopback, so 127.0.0.1 there reaches nothing: every
request answers 502 and the proxy log says connect() failed (111: Connection refused). Put the container on the stack’s network instead and reach the app by name:
docker network connect badlen_default my-proxy # before starting itThe upstream is then http://app:3001. Connect the container before it starts: nginx
resolves an upstream name once, at startup, and exits with
host not found in upstream "app" if it is not on the network yet. Nothing needs to be
published on the host in this arrangement.
Raise the body size
Section titled “Raise the body size”The application takes requests up to 25 MB, which is the size a JSON export of a full
instance reaches and has to be re-imported at. nginx accepts 1 MB by default and answers
anything larger with its own 413 Request Entity Too Large page. The request never
reaches Badlen, so the application log holds nothing about it. Put
client_max_body_size 25m; in the proxied location to match the two.
Tell the app how many proxies are in front
Section titled “Tell the app how many proxies are in front”Behind a proxy every request arrives from the proxy, and two things read an address off a request. Rate limits are counted per address, so the ten-a-minute limit on the routes that check a password becomes ten a minute for the whole household, and one person mistyping twice locks everybody else out. And each session under Settings → Security is shown at the address it was opened from, so every device in the house is listed at the same one.
TRUSTED_PROXY_HOPS=1 gives both the reader again. The number is one for a single nginx
or Caddy, two if something like a CDN sits in front of that. It is read as a count from
the end of the X-Forwarded-For header, because each proxy appends the address it
received the request from: with one proxy in front, the last address in that header is
the one your proxy wrote. A client inventing a header of its own only pushes that
invention further left, which is the whole reason the count comes from you and not from
the header. Too low and the outer proxy is what gets counted; too high and there is no
such address, so both fall back to the connecting one and the log says so, once a minute:
docker compose logs app | grep TRUSTED_PROXY_HOPSThe sign-in loop
Section titled “The sign-in loop”COOKIE_SECURE=true is the one setting that can send you back to the sign-in page for
ever. The browser keeps a cookie marked that way on an HTTPS origin and throws it away on
a cleartext one, localhost excepted. Reaching the same instance directly at
http://<lan-ip>:8080 while it is set signs you in and loses the session on the spot:
the sign-in answers 201, the app opens, the first navigation lands back on the sign-in
page, and nothing anywhere says why. Go through the proxy, or set COOKIE_SECURE=false
while there is no HTTPS in front.
One last thing the browser decides alone: a certificate it does not trust, a self-signed one included, costs the installable app. It refuses to register the service worker and prints an SSL error in the console. The interface and the data are unaffected.