From ea457f27dab4b71ce26f663e0acb9283a4ba3557 Mon Sep 17 00:00:00 2001 From: Shadowbroker <43977454+BigBodyCobain@users.noreply.github.com> Date: Fri, 3 Apr 2026 21:08:00 -0600 Subject: [PATCH] Fix admin session cookie Secure flag breaking localhost access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip the Secure flag on the session cookie when the request comes from a loopback address (localhost, 127.0.0.1, ::1). The Docker image sets NODE_ENV=production which always enabled Secure, but browsers silently drop Secure cookies on plain HTTP — breaking the admin panel for self-hosted users accessing http://localhost:3000. Fixes #129 --- frontend/src/app/api/admin/session/route.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/api/admin/session/route.ts b/frontend/src/app/api/admin/session/route.ts index 3948854..4f1ddeb 100644 --- a/frontend/src/app/api/admin/session/route.ts +++ b/frontend/src/app/api/admin/session/route.ts @@ -12,11 +12,13 @@ const NO_STORE_HEADERS = { Pragma: 'no-cache', }; -function cookieOptions() { +function cookieOptions(req: NextRequest) { + const host = req.headers.get('host') ?? ''; + const isLoopback = /^(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/.test(host); return { httpOnly: true, sameSite: 'strict' as const, - secure: process.env.NODE_ENV === 'production', + secure: process.env.NODE_ENV === 'production' && !isLoopback, path: '/', maxAge: COOKIE_MAX_AGE, }; @@ -80,7 +82,7 @@ export async function POST(req: NextRequest) { } const sessionToken = createAdminSessionToken(adminKey, COOKIE_MAX_AGE); const res = NextResponse.json({ ok: true }, { headers: NO_STORE_HEADERS }); - res.cookies.set(COOKIE_NAME, sessionToken, cookieOptions()); + res.cookies.set(COOKIE_NAME, sessionToken, cookieOptions(req)); return res; } @@ -91,7 +93,7 @@ export async function DELETE(req: NextRequest) { } const res = NextResponse.json({ ok: true }, { headers: NO_STORE_HEADERS }); res.cookies.set(COOKIE_NAME, '', { - ...cookieOptions(), + ...cookieOptions(req), maxAge: 0, }); return res;