HTTP 301이 뜨는 이유 — ALB ssl-redirect

curl work-api.test.example.io/readyz를 날렸더니 200 대신 이게 돌아왔다.

<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
</body>
</html>

https://를 명시해서 치면 {"status":"ready"}. 같은 엔드포인트인데 왜 한쪽은 301이고 한쪽은 200인가.


301은 오류가 아니라 의도된 보안 설정이다

301 Moved Permanently는 리소스가 영구적으로 다른 URL로 이동했다는 의미다. 클라이언트에게 앞으로는 새 URL을 쓰라고 알리며, Location 헤더에 새 주소가 포함된다.

curl -v로 확인하면:

< HTTP/1.1 301 Moved Permanently
< Location: https://work-api.test.example.io/readyz

http://로 보낸 요청을 https://로 리다이렉트하고 있다. HTTP는 평문이기 때문에 인증 토큰·쿠키가 노출될 위험이 있어서, HTTP 요청을 HTTPS로 강제 전환하는 것이 보안 표준이다.


301을 보내는 주체는 ALB다

EKS + ALB + Ingress 구조에서 트래픽 경로는 다음과 같다.

Route53
  ↓
ALB (HTTP 80 → HTTPS 443 redirect)
  ↓
Ingress → Service → Pod

301을 보내는 후보는 세 곳이다: ALB Listener Rule, nginx ingress annotation, 또는 애플리케이션 코드. 실제 Ingress 설정을 보면 원인이 바로 보인다.

annotations:
  alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
  alb.ingress.kubernetes.io/ssl-redirect: '443'    # ← 이것

alb.ingress.kubernetes.io/ssl-redirect: '443' — AWS Load Balancer Controller는 이 annotation을 보고 ALB를 다음과 같이 구성한다.

리스너동작
80Redirect → 443, 상태 코드 HTTP_301
443TargetGroup으로 forward

301은 Ingress나 Pod가 아니라 ALB 레벨에서 발생한다. 이 annotation을 제거하면 301이 사라지고 HTTP 직접 접근이 가능해지지만, 보안상 권장하지 않는다.


브라우저에서는 301이 보이지 않는 이유

브라우저로 http://work-api.test.example.io/readyz를 열면 301이 뜨지 않고 바로 응답이 온다. 301이 사라진 게 아니라, 브라우저가 301을 받고 자동으로 따라가기 때문이다.

HTTP 스펙상 301은 클라이언트가 새 URL로 재요청해야 한다고 정의되어 있고, 브라우저는 이를 UX 차원에서 자동 처리한다. 추가로 브라우저는 301 결과를 캐싱하기 때문에, 같은 URL을 다시 열면 서버에 요청조차 보내지 않고 바로 https://로 간다.

curl은 기본적으로 리다이렉트를 자동으로 따라가지 않는다. API 클라이언트나 스크립트는 리다이렉트를 명시적으로 인지해야 하는 경우가 많아 기본 동작이 멈추는 것이다.

클라이언트301 수신 시 동작보이는 것
브라우저Location으로 자동 재요청최종 응답(200)만 보임
curl (기본)301 응답에서 멈춤301 HTML 그대로 출력
curl -L자동으로 따라감최종 응답(200)이 출력
# 301에서 멈춤 (기본)
curl http://work-api.test.example.io/readyz
 
# 브라우저처럼 자동으로 따라감
curl -L http://work-api.test.example.io/readyz

이 탐구가 남긴 것

ssl-redirect annotation 하나가 ALB 레벨에서 HTTPS 강제 전환을 구현한다. 301은 어떤 오류가 아니라 의도된 보안 설정이다. curl에서 예상치 못한 301을 만나면 -vLocation 헤더를 확인하고, 리다이렉트를 따라가려면 -L 옵션을 쓴다.


참고 자료