SSL 인증서 만료 관리 - 자동 갱신은 조용히 실패한다
요즘 인증서는 대부분 자동 갱신됩니다. 그래서 실패해도 아무도 모릅니다.
발급 당시엔 멀쩡하니, 만료일 당일에 사이트가 죽고 나서야 알게 됩니다.
결론부터
# 만료일 확인 — 이거 하나만 주기적으로 돌리면 됩니다
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -subject -dates
subject=CN = example.com
notBefore=Sep 7 17:01:45 2026 GMT
notAfter=Dec 6 17:01:44 2026 GMT- Let's Encrypt 인증서는 유효기간이 90일입니다. 자동 갱신을 전제로 한 설계입니다
- 갱신은 만료 전에 미리 일어납니다.
notBefore가 최근 날짜로 바뀌어 있으면 갱신된 것입니다 -servername을 빼면 엉뚱한 인증서를 보게 됩니다. 반드시 붙이세요
갱신이 실패하는 이유
인증서를 발급받으려면 "이 도메인이 정말 당신 것인지" 를 증명해야 합니다.
이 확인 절차가 막히면 갱신이 실패합니다.
가장 흔한 방식은 HTTP 방식입니다. 발급 기관이 이런 주소로 접속해서 확인합니다.
http://example.com/.well-known/acme-challenge/무작위문자열여기에 도달하지 못하면 실패합니다. 막히는 경우는 대체로 이렇습니다.
| 원인 | 설명 |
|---|---|
| 앞단에 프록시/CDN을 붙임 | 확인 요청이 원래 서버에 도달하지 못한다 |
| 80 포트 차단 | 확인은 HTTP(80)로 온다. 443만 열어두면 실패 |
| DNS 변경 | 도메인이 다른 서버를 가리키면 엉뚱한 곳에서 확인을 받는다 |
| 리다이렉트 설정 | 모든 요청을 무조건 다른 곳으로 보내면 확인 경로도 같이 넘어간다 |
관리형 플랫폼(블로그 서비스 등)에 도메인을 연결한 경우,
그 앞에 CDN 프록시를 켜면 플랫폼이 인증서를 갱신하지 못하는 일이 생깁니다.
당장은 기존 인증서가 살아 있어 멀쩡해 보이지만, 만료일이 지나면 그때 사이트가 죽습니다.
프록시를 켜기 전에 인증서 발급 주체가 어디인지 확인하세요.
문제 신호 구분하기
for h in example.com www.example.com blog.example.com; do
printf "%-26s " "$h"
echo | openssl s_client -connect $h:443 -servername $h 2>/dev/null \
| openssl x509 -noout -subject -enddate 2>/dev/null | tr '\n' ' '
echo
done
| 출력 | 의미 |
|---|---|
CN = example.com + 미래 날짜 |
✅ 정상 |
CN = example.com + 지난 날짜 |
❌ 갱신 실패. 원인을 찾아야 한다 |
CN = *.어떤서비스.com |
⚠️ 전용 인증서가 발급되지 않은 상태 |
세 번째가 특히 헷갈립니다. 인증서가 "없는" 게 아니라 그 서비스의 공용 와일드카드가 응답하는 것입니다. 도메인 연결이 끊겼거나 아직 발급 전일 때 이렇게 나옵니다.
실제 방문자 입장에서 되는지는 이렇게 확인합니다.
curl -s -o /dev/null -w '검증ON %{http_code}\n' https://example.com/
curl -sk -o /dev/null -w '검증OFF %{http_code}\n' https://example.com/
ON에서 000, OFF에서 200이면 인증서 문제 확정입니다.
저는 5개월 동안 만료된 인증서를 방치한 적이 있습니다.
www 주소에 전용 인증서가 발급되지 않아 서비스의 기본 와일드카드가 응답하고 있었는데, 그게 이미 만료된 상태였습니다. 평소에 www로 접속할 일이 없으니 전혀 몰랐습니다. 나중에 도메인 전체를 점검하다가 발견했습니다.
반대로 자동 갱신이 잘 되는 걸 확인한 경험도 있습니다. 만료가 한 달 남은 인증서가 있어 걱정했는데, 며칠 뒤 다시 재보니 notAfter가 두 달 뒤로 밀려 있었습니다. 플랫폼이 알아서 갱신한 것이었습니다.
결국 중요한 건 "확인했는가"입니다. 갱신이 되든 안 되든, 주기적으로 재보지 않으면 어느 쪽인지 알 수 없습니다.
만료를 미리 잡는 법
1) 남은 일수 계산해서 경고
#!/bin/bash
for h in example.com www.example.com; do
end=$(echo | openssl s_client -connect $h:443 -servername $h 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
left=$(( ($(date -d "$end" +%s) - $(date +%s)) / 86400 ))
printf "%-26s %3d일 남음" "$h" "$left"
[ "$left" -lt 20 ] && echo " ⚠️ 확인 필요" || echo
done
크론에 걸어두면 됩니다.
0 9 * * 1 /home/user/check-cert.sh # 매주 월요일 오전 9시
2) 외부 감시 서비스
서버가 통째로 죽으면 그 안의 크론도 같이 죽습니다. 밖에서 보는 눈이 하나 있어야 합니다.
UptimeRobot 같은 무료 서비스에 HTTPS로 등록하면 다운 알림과 인증서 만료 알림을 같이 받습니다.
직접 갱신하지 않는 플랫폼(블로그 서비스 등)을 쓰더라도 감시는 해야 합니다.
갱신 주체가 남이어도 사이트가 죽는 건 내 문제입니다.
정리
- Let's Encrypt는 90일짜리입니다. 자동 갱신이 전제이고, 실패는 조용히 일어납니다
notBefore가 최근 날짜면 갱신된 것,notAfter가 지났으면 실패한 것입니다- 갱신 실패의 대부분은 확인 요청(
/.well-known/acme-challenge/)이 막힌 것입니다 - 프록시·CDN을 앞에 붙일 때 특히 주의하세요. 당장은 멀쩡하고 만료일에 터집니다
- 와일드카드 인증서가 나오면 전용 인증서 미발급 상태입니다
- 크론으로 남은 일수를 재고, 외부 감시도 하나 걸어두세요