HSTS(HTTP Strict Transport Security)는 브라우저에게 항상 HTTPS로 연결하도록 지시하는 보안 헤더입니다 — 사용자가 http://를 입력하거나 HTTP 링크를 클릭해도 마찬가지입니다. 브라우저가 HSTS 헤더를 수신하면 해당 도메인에 대해 향후 모든 요청을 자동으로 HTTPS로 업그레이드하여 다운그레이드 공격과 안전하지 않은 연결을 방지합니다.
HSTS가 중요한 이유
HSTS 없이는 사이트에 대한 첫 번째 요청이 HTTP일 수 있습니다(301 리다이렉트가 작동하기 전에). 그 짧은 시간 동안 네트워크의 공격자가:
- HTTPS를 제거 — 리다이렉트를 가로채고 사용자를 HTTP에 유지 (SSL 스트리핑 공격)
- 쿠키 탈취 — 초기 HTTP 요청에서 전송되는 쿠키 가로채기
- 콘텐츠 주입 — 리다이렉트 응답 변조
HSTS는 이 취약 시간을 제거합니다. 첫 번째 HTTPS 방문 이후, 브라우저는 다시는 HTTP를 시도하지 않습니다.
HSTS 활성화 방법
Nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
HTTPS server 블록(포트 443) 안에 추가합니다, HTTP 리다이렉트 블록이 아닌.
Apache
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
mod_headers가 필요합니다 (sudo a2enmod headers).
Cloudflare
대시보드 → SSL/TLS → Edge Certificates → HTTP Strict Transport Security (HSTS) → Enable.
HSTS 매개변수
| 매개변수 | 예시 | 의미 |
|---|---|---|
max-age | 63072000 (2년) | 브라우저가 HTTPS를 사용하도록 기억하는 기간(초) |
includeSubDomains | — | 모든 서브도메인에도 HSTS 적용 |
preload | — | HSTS preload 목록에 등록 의사 표시 |
max-age 선택
| 단계 | max-age | 목적 |
|---|---|---|
| 테스트 | 300 (5분) | HTTPS가 작동하는지 확인 후 적용 |
| 확신 | 604800 (1주) | 롤백 필요 시 낮은 위험 |
| 프로덕션 | 31536000 (1년) | 표준 권장 |
| 장기 | 63072000 (2년) | preload 목록에 필수 |
작게 시작하고 점진적으로 늘리세요. 긴 max-age가 캐시된 상태에서 HTTPS가 중단되면 방문자가 사이트에 전혀 접속할 수 없습니다 — 브라우저가 HTTP 연결을 거부합니다.
HSTS preload
HSTS preload 목록은 첫 번째 방문에서도(HSTS 헤더를 수신하기 전에도) 항상 HTTPS를 사용하도록 브라우저에 하드코딩된 도메인 목록입니다.
preload 요구 사항
- 유효한 HTTPS 인증서 제공
- 같은 호스트에서 HTTP를 HTTPS로 리다이렉트
max-age≥ 63072000 (2년)인 HSTS 헤더includeSubDomains지시문 포함preload지시문 포함
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
preload 제출
hstspreload.org에서 도메인을 입력하고 제출합니다. 검토(몇 주에서 몇 달) 후 브라우저의 내장 목록에 추가됩니다.
preload에 대한 주의 사항
- 되돌리기가 어렵습니다. preload 목록에서 제거하는 데 몇 달이 걸리고 브라우저 릴리스 주기가 필요합니다. 그동안 도메인은 HTTPS로만 접속할 수 있습니다.
includeSubDomains가 필수 — 모든 서브도메인이 HTTPS를 지원해야 합니다.staging.example.com에 인증서가 없으면 접근 불가능해집니다.- 모든 현재 및 향후 서브도메인이 HTTPS를 지원할 것이 확실한 경우에만 preload하세요.
흔한 실수
HTTP 응답에 HSTS 설정
HSTS는 HTTPS를 통해서만 전송해야 합니다. HTTP 리다이렉트에서도 HSTS 헤더를 보내면 브라우저가 무시합니다(사양에서 HTTPS를 요구). 포트 443 블록에만 헤더를 설정하세요.
서브도메인 고려하지 않음
includeSubDomains는 모든 서브도메인에 HSTS를 적용합니다. internal.example.com에 HTTPS가 없으면 접근 불가능해집니다. 활성화 전에 모든 서브도메인을 감사하세요.
max-age를 너무 일찍 너무 높게 설정
HTTPS가 중단되면(만료된 인증서, 설정 오류) 방문자가 HTTP로 대체할 수 없습니다. 브라우저가 연결을 거부합니다. 5분으로 시작하고, 모든 것이 작동하는지 확인한 후 1년으로 늘리세요.
HSTS 활성 여부 확인
curl -sI https://yourdomain.com | grep -i strict-transport
# 예상: strict-transport-security: max-age=63072000; includeSubDomains
Chrome 개발자 도구: Network 탭 → 요청 클릭 → Headers → Strict-Transport-Security를 확인합니다.
자주 묻는 질문
HSTS가 HTTP→HTTPS 리다이렉트를 대체하나요?
아닙니다. HSTS와 리다이렉트는 다른 목적을 가집니다. 리다이렉트는 첫 번째 HTTP 요청을 잡습니다. HSTS는 첫 번째 HTTPS 방문 이후 브라우저에게 다시는 HTTP 요청을 하지 말라고 알립니다. 둘 다 필요합니다.
HSTS가 문제를 일으킬 수 있나요?
네, HTTPS가 중단되는 경우. 긴 max-age에서 브라우저가 대체 수단으로 HTTP 연결을 거부합니다. 그래서 짧은 max-age로 시작하고 HTTPS가 안정적인 후에만 늘려야 합니다.
Cloudflare를 사용하면 HSTS가 필요한가요?
Cloudflare는 방문자→Cloudflare 연결을 처리하지만, 오리진에서의 HSTS는 전체 체인을 보호합니다. 엣지용으로 Cloudflare 대시보드에서 HSTS를 활성화하고, Full (Strict) 모드를 사용하는 경우 오리진에서도 활성화하세요.
HSTS와 upgrade-insecure-requests CSP 지시문의 차이점은?
upgrade-insecure-requests는 브라우저에게 하위 리소스(이미지, 스크립트)를 HTTP 대신 HTTPS로 로드하라고 지시합니다 — 혼합 콘텐츠 해결. HSTS는 브라우저에게 전체 도메인에 대해 항상 HTTPS를 사용하라고 지시합니다. 서로 다른 문제를 해결하며 함께 사용할 수 있습니다.