본문 바로가기
IT/보안

[보안] Let's Encrypt 갱신 오류, 흔한 문제와 해결 전략

by 수누다 2026. 6. 25.

[보안] Let's Encrypt 갱신 오류, 흔한 문제와 해결 전략

운영 중인 서비스에서 Let's Encrypt 갱신 오류가 한 번이라도 터지면 진짜 식은땀이 납니다. 평소엔 조용히 돌아가다가 어느 날 갑자기 HTTPS 오류가 보이고, 브라우저에서 보안 경고가 뜨면 그때부터는 마음이 급해지거든요. 저도 홈랩이랑 소규모 서비스 환경에서 SSL 인증서 갱신이 자동으로 될 줄만 알았다가, 새벽에 인증서 만료 알림 보고 삽질 좀 했습니다 ㅎㅎ 처음엔 이게 뭔가 싶었는데, 결국 원인은 늘 비슷한 데 있더라고요.

이번 글에서는 제가 직접 점검할 때 쓰는 흐름대로 Certbot(서트봇, Let's Encrypt 인증서 발급 도구) 문제 해결 방법을 정리해보겠습니다. 단순히 명령어만 던지는 게 아니라, 왜 실패하는지, 어디부터 봐야 하는지, 그리고 다시는 같은 문제를 반복하지 않으려면 어떻게 운영해야 하는지까지 같이 보시죠.

Let's Encrypt 갱신 오류 흐름을 설명하는 웹 서버와 DNS 개요 다이어그램

인증서 갱신이 실패하는 지점을 한눈에 보여주는 전체 흐름도죠.

1. 왜 Let's Encrypt 갱신 오류가 자주 생길까요?

쉽게 말해 Let's Encrypt는 "이 도메인을 정말 당신이 제어하고 있나요?"를 주기적으로 다시 확인합니다. 이때 Challenge(챌린지, 도메인 소유 확인 절차)가 정상 처리되지 않으면 갱신이 실패합니다. 겉으로는 인증서 문제처럼 보여도, 실제론 웹 서버 설정, DNS, 방화벽, 리버스 프록시, 컨테이너 라우팅 중 하나가 어긋난 경우가 많습니다.

실제로 써보니까 실패 원인은 대체로 아래 범주에 들어갔습니다.

  • 포트 80 접근 불가: HTTP-01 검증이 막히는 경우
  • DNS 레코드 불일치: 도메인이 다른 IP를 가리키는 경우
  • 웹루트 경로 오류: challenge 파일이 예상 위치에 생성되지 않는 경우
  • 프록시/Ingress 설정 충돌: Nginx, Apache, Ingress(인그레스, 외부 트래픽 진입점)에서 경로를 가로채는 경우
  • 자동 갱신 스케줄 미동작: systemd timer(시스템디 타이머) 또는 cron(크론) 문제

2. 먼저 알아야 할 핵심 개념: HTTP-01과 DNS-01

여기서 중요한 포인트! 어떤 방식으로 검증하느냐에 따라 점검 포인트가 완전히 달라집니다.

검증 방식 동작 방식 주로 막히는 지점 언제 적합한가
HTTP-01 도메인의 80/tcp로 challenge 파일 확인 방화벽, 리버스 프록시, 웹루트 경로 일반적인 웹 서버 환경
DNS-01 DNS TXT 레코드로 소유권 검증 DNS 전파 지연, 자동화 스크립트 와일드카드 인증서, 외부 포트 제한 환경

저도 처음엔 무조건 Certbot만 다시 돌리면 되는 줄 알았는데, 사실 중요한 건 갱신 명령이 아니라 검증 경로였습니다. HTTP-01이라면 웹 서버 응답을 먼저 봐야 하고, DNS-01이라면 DNS 제공자 쪽 자동화가 제대로 되는지 봐야 하거든요.

3. Let's Encrypt 갱신 오류가 났을 때 가장 먼저 확인할 1차 점검

문제 생기면 바로 재발급부터 하지 마시고, 아래 순서대로 보시는 게 훨씬 빠릅니다. 제가 직접 해보니 이 순서가 제일 덜 헤맸습니다.

  1. 현재 인증서 만료일 확인
  2. 갱신 테스트 실행
  3. 도메인 DNS 확인
  4. 포트 80/443 외부 접근 확인
  5. 웹 서버 로그와 Certbot 로그 확인

3-1. 인증서 상태 확인

sudo certbot certificates

이 명령으로 현재 인증서 이름, 연결된 도메인, 만료 예정 시점을 먼저 확인합니다. 여러 도메인을 한 서버에서 운영하면 어떤 인증서가 실패했는지 여기서 감이 옵니다.

3-2. 갱신 시뮬레이션 실행

sudo certbot renew --dry-run

dry-run(드라이런, 실제 반영 없이 테스트 실행)은 거의 필수입니다. 실제 갱신 전 검증 흐름을 테스트해주기 때문에, 운영 중인 인증서를 건드리지 않고 문제를 확인할 수 있습니다. 이 단계에서 에러 메시지가 꽤 직설적으로 나오더라고요.

3-3. DNS 확인

dig +short example.com

dig +short www.example.com

도메인이 현재 서비스 중인 공인 IP를 정확히 가리키는지 보셔야 합니다. 의외로 예전 서버 IP가 남아 있거나, CDN/프록시 설정이 중간에 꼬여 있는 경우가 있습니다.

3-4. 외부 접근 확인

curl -I http://example.com/.well-known/acme-challenge/test-file

여기서 404는 나올 수 있어도, 최소한 example.com까지 정상적으로 도달해야 합니다. 연결 자체가 안 되거나 엉뚱한 리다이렉트가 걸리면 그때부터 원인을 좁히면 됩니다.

Let's Encrypt 갱신 오류 점검을 위한 Certbot dry-run 터미널 이미지

dry-run 테스트, DNS 확인, 외부 접근 확인 순서를 시각적으로 정리한 이미지입니다.

4. 실전 구현: 웹루트 방식으로 SSL 인증서 갱신 점검하기

가장 흔한 시나리오라서 webroot(웹루트, 웹 서버가 문서를 제공하는 디렉터리) 기준으로 설명드리겠습니다. Nginx를 예로 들지만, Apache도 핵심 원리는 비슷합니다.

4-1. challenge 경로 테스트 파일 배치

sudo mkdir -p /var/www/html/.well-known/acme-challenge

echo test | sudo tee /var/www/html/.well-known/acme-challenge/health-check

그 다음 외부에서 아래처럼 접근해봅니다.

curl http://example.com/.well-known/acme-challenge/health-check

여기서 test가 보여야 합니다. 안 보이면 Certbot 문제가 아니라 웹 서버 라우팅 문제일 가능성이 큽니다.

4-2. Nginx 설정 예시

server {
    listen 80;
    server_name example.com www.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
        try_files $uri =404;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

처음엔 저도 모든 HTTP 요청을 바로 HTTPS로 넘기면 끝이라고 생각했었는데, challenge 경로는 예외 처리가 필요할 때가 있습니다. 특히 리버스 프록시를 여러 단으로 쌓아두면 여기서 꼬이기 쉽더라고요.

4-3. Certbot 갱신 테스트

sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com

sudo certbot renew --dry-run

새 발급이 아니라 갱신 점검이 목적이라면 기존 인증서 구조를 해치지 않는 선에서 테스트하시는 게 좋습니다. 운영 서버에선 무턱대고 옵션을 바꾸기보다 현재 어떤 플러그인 방식으로 인증서가 관리되는지 먼저 확인하세요.

5. ⚠️ 흔한 문제와 해결 전략

이 섹션이 핵심입니다. 실제로 가장 자주 보는 Let's Encrypt 갱신 오류 패턴을 정리해보겠습니다.

5-1. 포트 80이 막혀 있는 경우

HTTP-01은 기본적으로 80/tcp 응답이 필요합니다. 보안을 이유로 80을 닫아둔 환경이 종종 있는데, 이러면 갱신이 안 돼요.

  • 클라우드 보안 그룹(Security Group, 보안 그룹) 확인
  • 서버 로컬 방화벽(UFW, firewalld) 확인
  • 공유기 포트 포워딩 확인
sudo ss -lntp | grep ':80'

sudo ufw status

홈랩에서는 특히 공유기 NAT(나트, 사설망 주소 변환) 때문에 삽질하는 경우가 많습니다. 저도 한동안 서버 설정만 봤다가, 알고 보니 공유기 포트 포워딩이 빠져 있던 적이 있었네요.

5-2. DNS는 맞는 줄 알았는데 다른 IP를 보는 경우

DNS TTL(Time To Live, 캐시 유지 시간) 때문에 예전 값이 남아 있을 수 있습니다. A 레코드, AAAA 레코드가 같이 있을 때 IPv6 경로만 잘못된 경우도 꽤 흔합니다.

dig example.com A +short

dig example.com AAAA +short

특히 IPv6를 의도적으로 운영하지 않는데 AAAA 레코드가 남아 있으면, 일부 환경에서 엉뚱한 경로로 검증이 시도될 수 있습니다.

5-3. 컨테이너와 프록시 체인에서 challenge 파일이 안 보이는 경우

Docker, Kubernetes, NAS 내장 프록시 환경에서 많이 만납니다. 애플리케이션 컨테이너는 뜨는데 .well-known 경로만 별도 처리되지 않아서 실패하는 패턴이죠.

  • 호스트와 컨테이너의 웹루트 경로가 같은지 확인
  • Ingress 또는 reverse proxy(리버스 프록시) 규칙 충돌 확인
  • 리다이렉트 규칙이 challenge 경로를 덮어쓰지 않는지 확인
Let's Encrypt 갱신 오류의 원인이 되는 리버스 프록시와 컨테이너 구조 다이어그램

프록시 체인과 컨테이너 환경에서 challenge 요청이 어디서 끊기는지 설명하는 다이어그램입니다.

5-4. 자동 갱신은 설정했는데 실제로는 안 도는 경우

이건 꽤 허탈합니다. 저는 예전에 certbot 명령은 잘 되는데, 정작 자동 갱신 스케줄이 비활성화되어 있던 적이 있었습니다.

systemctl list-timers | grep certbot

systemctl status certbot.timer

환경에 따라 cron을 쓰는 경우도 있으니 둘 다 확인하셔야 합니다. SSL 인증서 갱신은 "설정했다"가 아니라 "주기적으로 실제 실행되는지 검증했다"가 되어야 합니다.

5-5. 갱신 후 웹 서버 재적용이 안 되는 경우

인증서는 갱신됐는데 서비스에는 옛날 인증서가 계속 보이는 경우가 있습니다. 이럴 땐 reload(리로드, 설정 재적용)가 누락된 경우를 의심해보세요.

sudo nginx -t

sudo systemctl reload nginx

Apache라면 서비스 이름만 다를 뿐 흐름은 비슷합니다. 설정 검증 없이 바로 재시작하면 더 크게 터질 수 있으니, 저는 항상 문법 체크부터 합니다.

6. 운영 팁: 갱신 실패를 미리 막는 습관

문제 생기고 나서 대응하는 것보다, 애초에 안 터지게 만드는 게 훨씬 낫습니다. 실제로 써보니까 아래 습관이 꽤 효과적이었습니다.

  1. 월 1회 dry-run 실행으로 검증 경로 이상 여부 확인
  2. 인증서 만료 알림 메일을 놓치지 않도록 별도 라벨링
  3. 웹 서버 설정 변경 후 challenge 경로 재테스트
  4. DNS 변경 직후 외부에서 A/AAAA 레코드 재확인
  5. 프록시 구조 변경 시 HTTP-01 대신 DNS-01 검토

💡 팁 하나 드리면, 구조가 자주 바뀌는 환경이라면 HTTP-01만 고집하지 않는 게 좋습니다. 와일드카드가 필요하거나 외부 포트 정책이 빡빡하면 DNS-01이 더 안정적일 때도 많거든요.

7. 검증과 결과 확인

이제 마지막 확인입니다. 갱신이 성공했다고 바로 끝내지 마시고, 실제 서비스에 반영됐는지 확인해야 합니다.

sudo certbot renew --dry-run

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject

저는 보통 dry-run 성공 후 실제 서비스 인증서의 시작일과 종료일을 확인합니다. 브라우저 자물쇠만 보는 건 조금 불안하더라고요. 명령행에서 직접 확인하면 훨씬 확실합니다.

✅ 정상이라면 다음과 같은 상태가 나와야 합니다.

  • dry-run이 오류 없이 종료됨
  • 도메인 443 응답이 정상
  • 웹 서버 reload 후 새 인증서 날짜 반영
  • 브라우저 보안 경고 없음
Let's Encrypt 갱신 오류 해결 후 SSL 인증서 갱신 검증 화면

갱신 성공 후 무엇을 확인해야 하는지 체크리스트 형태로 정리한 이미지입니다.

8. 자주 묻는 질문과 정리

Q1. HTTPS로만 서비스 중인데 80 포트가 꼭 필요한가요?

HTTP-01을 쓴다면 보통 필요합니다. 다만 DNS-01을 쓰는 방식으로 전환하면 80 포트 의존도를 줄일 수 있습니다.

Q2. Certbot 문제 해결은 로그를 어디부터 보면 좋을까요?

저는 먼저 certbot renew --dry-run 결과를 보고, 그 다음 웹 서버 접근 로그와 에러 로그를 같이 봅니다. Certbot 문제 해결은 단일 명령보다 흐름 전체를 보는 게 중요합니다.

Q3. 갱신은 성공했는데 브라우저에서 예전 인증서가 보입니다.

웹 서버 reload 누락, 로드밸런서 캐시, CDN 인증서 분리 운영 등을 차례로 의심해보시면 됩니다.

정리

Let's Encrypt 갱신 오류는 겉보기엔 인증서 이슈지만, 실제론 네트워크와 웹 서버 설정 문제인 경우가 많습니다. 제가 직접 해보니 가장 중요한 건 "재발급"보다 "검증 경로 추적"이었습니다. 포트 80, DNS, 웹루트, 프록시, 자동 갱신 타이머. 이 다섯 가지만 차분히 보면 대부분 풀립니다.

🎉 드디어 됐다! 싶은 순간이 오면, 거기서 끝내지 말고 재발 방지까지 해두시는 걸 추천드립니다. 다음 글에서는 Nginx 리버스 프록시 환경에서 HTTPS 오류를 줄이기 위한 설정 분리 방법도 다뤄볼 예정입니다. 이전 글에서 다뤘던 리버스 프록시 구조 점검 글이 있다면 같이 참고하시면 더 이해가 빠르실 거예요.

검증 방식 선택 기준과 운영 체크포인트를 한 장으로 요약한 인포그래픽입니다.