← 전체 글로 돌아가기

웹 개발

배포 후 관리자만 401을 받았던 쿠키 속성 점검 체크리스트

로컬에서는 로그인되는데 HTTPS 운영 환경에서 관리자 요청만 401이 된 원인을 쿠키 속성별로 확인한 체크리스트입니다.

로그인 성공과 인증 유지 성공은 달랐다

로그인 API의 응답은 200이었고 브라우저에도 성공 알림이 떴다. 그런데 관리자 페이지에서 글 목록을 요청하면 401이 반복됐다. 로컬 HTTP 환경에서는 같은 코드가 동작해서 서버 비밀번호나 세션 저장 로직부터 의심했지만, 실제 문제는 브라우저가 세션 쿠키를 다음 요청에 보내지 않는 것이었다.

운영 도메인이 바뀐 날 쿠키 설정을 직접 확인하지 않은 것이 시작이었다. 개발자 도구 Network 탭에서 로그인 응답의 Set-Cookie와 다음 API 요청의 Cookie 헤더를 나란히 보는 방식으로 범위를 좁혔다.

속성을 한 번에 하나씩 확인했다

내가 확인한 순서는 다음과 같다.

  1. DomainPath가 관리자 요청 경로와 맞는가
  2. 운영이 HTTPS인데 Secure가 빠지거나, 반대로 개발 HTTP에서 무조건 켜져 있지 않은가
  3. 프론트와 API의 사이트 관계에 맞는 SameSite 값인가
  4. 쿠키의 만료 시간이 서버 시간과 엇갈리지 않는가
  5. 응답을 받는 fetch에 필요한 credentials 설정이 있는가

동일 사이트의 브라우저 요청에서는 우선 명시적으로 기본값을 고정했다.

response.cookies.set('session', token, {
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'lax',
  path: '/',
  maxAge: 60 * 60 * 24 * 7,
})

다른 origin에서 호출하는 구조라면 SameSite만 바꾸는 것으로 끝나지 않는다. 서버의 CORS 허용 origin과 Access-Control-Allow-Credentials: true, 클라이언트의 credentials: 'include'가 함께 맞아야 한다. Access-Control-Allow-Origin: *와 credentials를 동시에 쓸 수 없다는 것도 테스트에서 다시 확인했다.

fetch('/api/admin/posts', {
  method: 'GET',
  credentials: 'include',
})

운영 전 체크박스

  • 로그인 응답에 세션 쿠키가 실제로 존재한다
  • 쿠키가 HttpOnly라서 페이지 JavaScript가 토큰을 읽지 못한다
  • HTTPS 환경에서 Secure가 적용된다
  • Path=/로 관리자 API에도 전송된다
  • 프록시가 Set-Cookie의 속성을 지우지 않는다
  • 만료된 쿠키와 오래된 도메인 쿠키를 브라우저에서 제거했다
  • 로그아웃 응답의 삭제 쿠키가 생성 쿠키와 같은 path/domain을 사용한다

마지막으로 남긴 규칙

401을 보면 곧바로 세션 생성 코드를 고치지 않고, 먼저 “쿠키가 저장됐는가 → 해당 요청에 전송됐는가 → 서버가 읽었는가”를 세 단계로 나눈다. 이 순서를 지키면 로컬과 운영의 차이를 훨씬 빨리 찾을 수 있고, 인증 토큰을 로그에 출력하는 위험한 디버깅도 피할 수 있다.