인증 · 미들웨어
HTTPS 세션 만료 시 무한 리다이렉트 — __Secure- 쿠키
세션이 무효해진 사용자가 프로덕션(HTTPS)에 접속하면 ERR_TOO_MANY_REDIRECTS로 사이트 자체가
안 떴다. 쿠키를 지우라고 했는데 브라우저가 그 명령을 통째로 무시하고 있었다. 코드가 아니라
RFC 쿠키 규칙(__Secure- prefix)을 알아야 풀리는 P0였다.
증상
- 프로덕션(HTTPS) 진입 시
ERR_TOO_MANY_REDIRECTS로 페이지가 아예 안 뜸 - 시크릿 창·로컬 dev(http)에선 정상 — 특정 사용자만 막힘
- 토큰 만료·권한 박탈·강제 로그아웃 등 BE가 401을 주는 모든 경우에 발생할 수 있는 시한폭탄 → P0
의도된 흐름
미들웨어가 모든 페이지 진입 전 토큰 유효성을 BE로 확인하고, 무효(401)면 세션 쿠키를 지우고
/signin으로 리다이렉트하도록 의도돼 있었다.
function buildSignOutRedirect(origin: string): NextResponse {
const res = NextResponse.redirect(`${origin}/signin`);
for (const name of SESSION_COOKIES) {
res.cookies.delete(name); // ← 이게 안 먹었다
}
return res;
}근본 원인 — 브라우저가 Set-Cookie를 무시
세션 쿠키 이름은 환경에 따라 둘이었다.
next-auth.session-token(http / dev)__Secure-next-auth.session-token(https / prod)
cookies.delete(name)이 만드는 Set-Cookie 헤더엔 Secure 플래그가 없다.
Set-Cookie: __Secure-next-auth.session-token=; Path=/; Max-Age=0
↑ Secure 없음RFC 6265bis엔 이런 규칙이 있다.
쿠키 이름이
__Secure-로 시작하면 Set-Cookie에 반드시Secure플래그가 있어야 한다. 없으면 그 헤더를 통째로 무시한다.
즉 cookies.delete()의 헤더는 스펙 위반이라 브라우저가 무시 → 쿠키가 안 지워짐 → 다음 요청도 같은
토큰 → BE 또 401 → 또 삭제 시도(또 실패) → 무한 리다이렉트 루프. http(dev)는 __Secure-가
아니라 정상 동작해서 로컬에선 재현이 안 됐다.
해결
delete 대신 Secure 옵션을 명시한 set으로 빈 값을 덮어썼다.
res.cookies.set(name, "", {
path: "/",
maxAge: 0,
secure: true, // __Secure- 접두사 규칙 충족
httpOnly: true,
sameSite: "lax",
});배운 점
cookies.delete()는 만능이 아니다. 쿠키를 지우는 Set-Cookie도 원래 설정과 같은 속성(Secure ·
Path · SameSite)을 맞춰야 브라우저가 받아준다. "로컬은 되는데 프로덕션만 안 되는" 버그는 http/https
차이(Secure · SameSite)를 먼저 의심한다.