← 모두의 툴

HSTS max-age=0의 함정 — 보안 헤더가 있어도 없는 셈인 경우

가이드 · 2026.08.21 최종 확인

보안 헤더를 점검할 때 가장 흔한 실수는 "헤더가 존재하는지"만 확인하고 끝내는 것입니다. 대부분의 헤더는 존재 여부만으로 충분하지만, Strict-Transport-Security(HSTS)는 예외입니다. 이 헤더는 값에 따라 정반대의 의미를 가질 수 있어서, "있으니까 안전하다"는 직관이 틀리는 대표적인 케이스입니다. 이 가이드는 왜 그런지, 그리고 이걸 자동으로 걸러내려면 무엇을 확인해야 하는지 정리합니다.

1. HSTS 헤더가 하는 일

HSTS는 Strict-Transport-Security: max-age=31536000; includeSubDomains처럼 응답에 포함시키는 헤더로, 브라우저에게 "앞으로 max-age초 동안 이 도메인은 무조건 HTTPS로만 접속하라"고 지시합니다. 사용자가 실수로 http://로 접속을 시도해도 브라우저가 서버에 요청을 보내기 전에 자체적으로 https://로 바꿔버리기 때문에, 중간자 공격(다운그레이드 공격)을 원천 차단하는 효과가 있습니다. max-age 값은 초 단위이며 1년은 31536000초입니다.

2. max-age=0의 진짜 의미

여기서 함정이 시작됩니다. HSTS 스펙인 RFC 6797max-age=0을 단순히 "짧은 유효기간"이 아니라 HSTS 정책을 즉시 해제하라는 명시적 신호로 정의합니다. 즉 서버가 Strict-Transport-Security: max-age=0을 보내면, 브라우저는 그 순간부터 해당 도메인을 더 이상 HTTPS 전용으로 취급하지 않습니다. 헤더 자체는 응답에 분명히 포함되어 있지만, 실질적인 보호 효과는 0입니다. 이 값은 실무에서 실수로 나오는 게 아니라, HSTS를 의도적으로 해제하고 싶을 때(예: 서브도메인 중 하나가 아직 HTTPS 인증서를 준비 못 했을 때) 공식적으로 쓰라고 만들어진 값입니다.

3. "헤더가 있다"는 착각이 위험한 이유

보안 점검 스크립트나 체크리스트가 헤더의 존재 여부만 grep으로 확인하는 방식이라면, max-age=0인 응답도 "HSTS 적용됨: 통과"로 오판합니다. 실제로는 그 사이트가 HTTPS 강제라는 보호를 전혀 받지 못하고 있는데도 말입니다. 이런 오판은 특히 다음 상황에서 실제 사고로 이어질 수 있습니다.

4. 값까지 검사하는 방법

HTTP 헤더 검사기는 이 문제를 피하기 위해 HSTS를 단순 존재 여부가 아니라 max-age 값까지 파싱해서 판정합니다. 정규식으로 max-age=숫자 부분을 추출한 뒤 그 숫자가 0보다 큰 경우에만 "존재(적용됨)"로 표시하고, max-age=0이거나 max-age 자체가 없으면 값이 아무리 존재해도 "누락"으로 분류합니다. 다른 보안 헤더(CSP, X-Frame-Options 등)는 값의 세부 내용과 무관하게 존재 자체만으로 통과 처리되지만, HSTS만큼은 이 예외 로직이 적용되는 것도 그래서입니다.

예시: 같은 "헤더 존재" 상태라도 값에 따라 판정이 갈립니다.
실제 응답 헤더헤더 존재?실질적 보호올바른 판정
Strict-Transport-Security: max-age=315360001년간 HTTPS 강제적용됨
Strict-Transport-Security: max-age=0없음(즉시 해제)누락과 동일
(헤더 자체가 없음)아니오없음누락

5. 점검 체크리스트

  1. curl -I 또는 브라우저 개발자도구에서 Strict-Transport-Security 값을 직접 확인하고, max-age 뒤 숫자를 반드시 읽습니다.
  2. 운영 환경이라면 max-age는 최소 6개월(15768000) 이상, 권장은 1년(31536000)입니다.
  3. 임시로 HSTS를 내려야 하는 상황이 끝나면 반드시 원래 max-age 값으로 복원하는 절차를 배포 체크리스트에 포함시킵니다.
  4. Apache/Nginx 설정에서 헤더를 하드코딩했다면 Apache 설정 생성기Nginx 설정 생성기로 표준 값(1년)을 다시 생성해 대조합니다.
  5. CSP 등 다른 보안 헤더도 함께 놓치지 않았는지 CSP 생성기로 교차 확인합니다.

자주 묻는 질문

Q. max-age=0을 일부러 써야 하는 경우도 있나요?

네. HTTPS 마이그레이션을 되돌려야 하거나 서브도메인의 인증서 문제로 일시적으로 HTTP 접속을 허용해야 할 때 RFC 6797이 공식적으로 정의한 해제 방법입니다. 다만 임시 조치이므로 원인이 해결되면 즉시 원래 값으로 되돌려야 합니다.

Q. max-age 값이 아예 없이 헤더만 있는 경우는요?

Strict-Transport-Security 헤더에 max-age 지시어가 없으면 스펙상 유효하지 않은 헤더로 취급되어 브라우저가 무시합니다. 이 경우도 실질적으로는 HSTS 미적용과 같습니다.

Q. 다른 보안 헤더도 값까지 확인해야 하나요?

X-Frame-Options나 X-Content-Type-Options처럼 대부분 헤더는 존재 자체가 곧 보호를 의미하지만, HSTS는 max-age라는 숫자 하나로 "켜짐/꺼짐"이 완전히 바뀌는 특수한 구조라 별도로 값을 확인해야 합니다.

Q. HSTS를 처음 적용할 때 주의할 점은?

includeSubDomains를 함께 켜면 모든 서브도메인이 HTTPS 준비가 되어 있어야 합니다. 준비 안 된 서브도메인이 있으면 그 서브도메인이 완전히 접속 불가 상태가 될 수 있으므로, 먼저 짧은 max-age로 테스트한 뒤 점진적으로 늘리는 것이 안전합니다.