SSL 인증서 만료일 확인하는 방법 — 브라우저·openssl·PowerShell
인증서 만료 사고는 대부분 “만료일을 몰라서”가 아니라 어디를 봐야 하는지 착각해서 발생한다. 브라우저가 보여주는 만료일과 서버에 설치된 파일의 만료일이 다를 수 있고, 만료가 임박한 것은 서버 인증서가 아니라 중간 인증서일 수도 있다.
여기서는 세 가지 확인 방법을 다루고, 각 방법이 실제로 무엇을 검사하는지 짚는다.
1. 브라우저 — 가장 빠르지만 가장 좁다
주소창 자물쇠 아이콘 → 인증서 정보에서 유효기간을 볼 수 있다.
- Chrome: 자물쇠 →
이 사이트에 대한 인증서가 유효합니다→ 세부 정보 - Firefox: 자물쇠 →
연결 보안→자세히 보기
문제는 브라우저가 접속에 성공한 하나의 경로만 보여준다는 점이다. 로드밸런서 뒤에 서버가 여러 대이고 그중 한 대만 예전 인증서를 들고 있으면, 브라우저는 그 사실을 알려주지 않는다. 브라우저 확인은 “지금 내 눈앞의 응답”에 대한 확인이지 서비스 전체에 대한 확인이 아니다.
2. openssl — 실제로 서버가 보내주는 것을 본다
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
출력:
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R11
notBefore=Aug 12 04:31:17 2026 GMT
notAfter=Nov 10 04:31:16 2026 GMT
-servername 을 빼면 안 된다. SNI 없이 접속하면 그 IP의 기본 vhost 인증서가 돌아오고,
확인하려던 도메인과 다른 인증서를 보게 된다. 실제로 “만료일이 왜 다르지”의 상당수가 이것이다.
만료까지 남은 일수만 필요하면:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -checkend $((30*86400)) \
&& echo "30일 이상 남음" || echo "30일 이내 만료"
-checkend 는 종료 코드로 답한다. 스크립트에 넣기 좋다.
파일로 가지고 있는 인증서 확인
openssl x509 -in /etc/ssl/certs/example.crt -noout -dates
서버가 실제로 서빙 중인 것과 디스크의 파일이 다를 수 있다. 갱신 후 리로드를 안 했다면 파일은 새 것, 서빙은 옛 것이다. 둘 다 확인해야 의미가 있다.
3. PowerShell — Windows 환경
$req = [Net.HttpWebRequest]::Create("https://example.com")
$req.GetResponse() | Out-Null
$req.ServicePoint.Certificate.GetExpirationDateString()
IIS에 설치된 인증서를 직접 보려면:
Get-ChildItem Cert:\LocalMachine\My |
Select-Object Subject, NotAfter, Thumbprint |
Sort-Object NotAfter
NotAfter 기준 정렬이면 곧 만료될 것이 맨 위에 온다. 인증서가 여러 장 설치된 서버에서
어떤 것이 실제 바인딩에 쓰이는지는 Get-WebBinding 으로 thumbprint를 대조해야 한다.
놓치기 쉬운 것 세 가지
중간 인증서도 만료된다. 서버 인증서만 갱신하고 체인 파일을 그대로 두면, 서버 인증서가
유효해도 검증이 깨진다. openssl s_client 출력에서 Certificate chain 섹션의 각 항목을
확인해야 한다.
만료일과 갱신 시점은 다르다. Let’s Encrypt는 90일 인증서를 60일 시점에 갱신하도록 설계돼 있다. “아직 30일 남았는데 왜 갱신 실패 알림이 오지”가 정상이다. 30일 남은 시점의 실패는 이미 두 번 이상 실패했다는 뜻이다.
서브도메인은 따로 만료된다. 와일드카드가 아니라면 www 와 api 는 별개 인증서다.
루트 도메인만 확인하고 넘어가면 놓친다.
여러 도메인을 한 번에
도메인이 열 개를 넘어가면 수동 확인은 유지되지 않는다. 최소한의 형태는 이렇다:
for d in example.com api.example.com www.example.com; do
end=$(openssl s_client -connect "$d:443" -servername "$d" </dev/null 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
printf '%-28s %s\n' "$d" "$end"
done
이 정도로도 엑셀보다는 낫다. 다만 도메인 자체의 만료(레지스트라 등록 만료)는 여기서 안 잡힌다.
만료일은 정상인데 브라우저가 오류를 내는 경우라면 체인 문제일 가능성이 높다 — 크롬은 되는데 파이어폭스만 인증서 오류가 나는 이유와 NET::ERR_CERT_AUTHORITY_INVALID 원인별 해결에서 이어진다. 확인 후 실제 교체 절차는 Nginx 인증서 교체와 무중단 리로드를 참고.