My task app, todo., is as self-hosted as it gets: an open-source backend on the server in my closet, a little PWA front-end I built, reachable only inside my private network, with exactly one user — me. When I set it up, I gave the login tokens a ten-year lifetime, because the one thing a single-user app should never do is ask its single user to log in again before 2036.
It asked me to log in roughly weekly.
The token was always fine
The folklore explanation was “the ten-year token thing must not actually work.” So I finally sat down and checked, instead of re-typing my password for the fortieth time. Decoded an actual token: expiry claim, ten years out, exactly as configured. The token was fine. The token was always fine.
One line of my own code
The real culprit was one line of my own front-end code. On any 401 response, the app deleted the stored token and bounced me to the login screen. Which is textbook-correct behavior — a 401 means your credentials are bad, right? Except a 401 isn’t only “your credentials are bad.” When the backend restarts, or the reverse proxy hiccups, or the phone version of the app wakes from the dead mid-request, you can catch a transient error on a token that’s perfectly valid. My app’s response to a network sneeze was to shred a decade of credentials and demand I prove who I am. Multiply by three devices, each with its own stored copy of the token, and “weekly login screen” stops being mysterious.
The doorman question
I started designing the fix — retry logic, smarter 401 handling, refresh-token care — and then stopped, because the whole frame was wrong. This is an app with one user, on a network with a perimeter, on hardware I own. Why does it have a login screen at all? The doorman was born from copying how public web apps work, not from any actual threat. Anyone who can reach the app is already inside my network, and if a stranger is inside my network, my task list is honestly the least of my problems.
Let the proxy vouch
So the architecture I landed on: the reverse proxy vouches for you. If a request arrives from inside the perimeter, the proxy injects the credentials server-side and the app never shows a login screen, because the front-end never touches a token it can lovingly destroy. The API keeps normal token auth for scripts and integrations. The 401 doesn’t get handled better — it gets made structurally impossible.
The bug left me with a rule for my own self-hosted, single-user world: every login screen deserves a second look. It may be a piece of someone else’s architecture I forgot to delete.