Problem: Not Authenticated (401 after login)
Symptoms
- You log in successfully and are redirected to the dashboard
- The dashboard immediately shows a 401 or redirects back to the login page
- Every API call returns
401 Unauthorized - The browser shows the login form in a loop
Why this happens
ProxyOS uses better-auth for session management. The session token is stored in an HTTP-only cookie. A 401 loop after a successful login almost always means the cookie was set but the browser is not sending it back. The three most common causes:
-
Secureflag + HTTP: The cookie hasSecure=truebut the dashboard is served over plain HTTP. The browser silently drops a Secure cookie on a non-HTTPS connection. -
Wrong
PROXYOS_SECRET: The session was signed with a different secret than the one currently configured. This happens ifPROXYOS_SECRETwas changed between the login attempt and the session verification. -
SameSite=Strictacross a redirect: If ProxyOS is behind a proxy that changes the effective origin, theSameSiteattribute may block the cookie on the first navigation after login. -
Cross-origin dashboard access: Accessing the dashboard at a different hostname than the one the cookie was issued for (e.g., IP address vs. hostname).
Diagnosis
Open browser DevTools → Application → Cookies, and find the ProxyOS session cookie.
Check:
- Is it present after the login redirect?
- Does it have the
Secureattribute set? - What domain and path does it have?
- Is the request to
/api/trpc/...sending the cookie (Network tab → request headers →Cookie:)?
Check the container logs:
docker compose logs proxyos | grep -i "createContext\|auth\|cookie"
Look for the log line [trpc] createContext called WITHOUT resHeaders — this indicates the resHeaders fallback is in use (expected in production) and does not itself cause auth failures.
Fix
Fix 1: Serve the dashboard over HTTPS
If you are accessing the dashboard over plain HTTP, either:
- Put ProxyOS itself behind a route with TLS enabled, or
- Use
http://and ensurePROXYOS_URLis alsohttp://(nothttps://)
The simplest production setup is to create a ProxyOS route for the dashboard domain itself, with TLS mode auto or internal.
Fix 2: Verify PROXYOS_SECRET is stable
docker compose exec proxyos env | grep PROXYOS_SECRET
Confirm the value matches what is in your .env file and has not changed since the session was issued. If you rotated the secret, all sessions are invalid — log in again and the new cookie will be issued correctly.
Fix 3: Access via consistent hostname
Always access the dashboard at the same hostname (e.g., always https://proxyos.yourdomain.com, not sometimes by IP). Cookie domain scope must match.
Prevention
- Always serve the dashboard over HTTPS in production
- Never change
PROXYOS_SECRETwithout understanding it logs everyone out (see Secret Rotation Logout) - Set
PROXYOS_URLto the canonical HTTPS URL of your dashboard