본문 바로가기
서버 인프라 실무

무료 SSL(HTTPS) 적용: Certbot 자동 갱신 + 자주 나는 오류 해결(Ubuntu 24.04, Nginx)

by yamoojin83 2025. 10. 20.

무료 SSL(HTTPS) 적용: Certbot 자동 갱신 + 자주 나는 오류 해결(Ubuntu 24.04, Nginx)

도메인을 Nginx에 연결했다면 이제 HTTPS(SSL/TLS)로 암호화를 적용할 차례입니다. 이 글은 Ubuntu 24.04 + Nginx 환경에서 Certbot으로 무료 인증서 발급자동 갱신을 설정하고, 현장에서 가장 자주 겪는 오류 원인과 해결법을 한 번에 정리합니다. 명령어는 그대로 복붙해도 동작하도록 구성했습니다.


1) 사전 점검(필수 체크)

  • DNS: example.com, www.example.com 이 서버 IP(A/AAAA)로 정확히 향함
  • 방화벽: 80/tcp, 443/tcp 허용(UFW/보안그룹 모두)
  • Nginx 서버 블록: 도메인 server_name이 설정되어 있고 nginx -t 가 통과
# DNS & 포트 확인(외부 PC에서)
dig +short A example.com
curl -I http://example.com     # 200 또는 301/302 OK

2) Certbot 설치(Nginx 플러그인)

sudo apt update
sudo apt install -y certbot python3-certbot-nginx

Nginx 플러그인을 사용하면 서버 블록 수정·HTTP→HTTPS 리다이렉트를 자동으로 처리할 수 있습니다.


3) 첫 발급 & 자동 설정(가장 쉬운 방법)

sudo certbot --nginx -d example.com -d www.example.com
  • 이메일/약관 동의 → 리다이렉트(redirect) 옵션을 선택하면 HTTP→HTTPS가 자동으로 설정됩니다.
  • 성공 후 인증서 경로: /etc/letsencrypt/live/example.com/{fullchain.pem,privkey.pem}

검증

curl -I http://example.com     # 301 → https://example.com
curl -I https://example.com    # 200 OK, 인증서 유효

4) Webroot 방식(프록시/특수 구성용)

게이트웨이/리버스 프록시가 Nginx 외부에 있거나, Nginx 플러그인을 쓰기 어렵다면 webroot 모드가 안전합니다.

  1. Nginx 서버 블록에 ACME 경로 노출
# /etc/nginx/sites-available/example.com (HTTP 서버 블록)
location ^~ /.well-known/acme-challenge/ {
  root /var/www/certbot;
  default_type "text/plain";
}
sudo mkdir -p /var/www/certbot
sudo nginx -t && sudo systemctl reload nginx
  1. webroot로 발급
sudo certbot certonly --webroot -w /var/www/certbot \
  -d example.com -d www.example.com

이후 HTTPS 서버 블록(443)에 인증서 경로를 수동으로 지정합니다.

ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

5) 자동 갱신 확인(타이머·드라이런)

Certbot는 시스템 타이머로 자동 갱신합니다(보통 하루 2회 점검, 만료 30일 이내면 갱신 시도).

systemctl list-timers | grep certbot
sudo systemctl status certbot.timer
sudo certbot renew --dry-run   # 갱신 리허설(중요)

갱신 후 자동으로 Nginx를 reload해야 합니다. 대부분의 Debian/Ubuntu 패키지는 훅이 설정되어 있지만, 확실히 하려면 다음 훅을 사용하세요.

# Nginx reload 훅(갱신 시마다 실행)
echo -e '#!/bin/sh\nnginx -t && systemctl reload nginx' | \
  sudo tee /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh >/dev/null
sudo chmod +x /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh

6) HTTP→HTTPS(301)와 보안 헤더

Certbot 자동 구성으로도 되지만, 수동으로 작성한다면 다음을 참고하세요.

# HTTP → HTTPS 리다이렉트
server {
  listen 80;
  listen [::]:80;
  server_name example.com www.example.com;
  return 301 https://example.com$request_uri;
}

# HTTPS 서비스
server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name example.com;

  ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  # 보안 헤더(스니펫 활용 권장)
  add_header X-Content-Type-Options "nosniff" always;
  add_header X-Frame-Options "SAMEORIGIN" always;
  add_header Referrer-Policy "strict-origin-when-cross-origin" always;

  location / {
    proxy_pass         http://127.0.0.1:8080;
    proxy_set_header   Host $host;
    proxy_set_header   X-Real-IP $remote_addr;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_http_version 1.1;
    proxy_set_header   Connection "";
    client_max_body_size 20m;
  }
}

7) 자주 나는 오류와 해결

7-1) Connection refused 혹은 timeout (HTTP-01 챌린지 실패)

  • 원인: 80 포트가 닫혀 있거나 다른 서비스가 점유
  • 해결:
    • sudo ufw allow 80/tcp, 클라우드 보안그룹에서도 80 허용
    • ss -lntup | grep :80 로 점유 확인 → Nginx가 뜨는지 nginx -t && systemctl reload nginx

7-2) CDN/프록시(예: “오렌지 구름”)가 가로막는 경우

  • 원인: 인증 기관이 서버의 80로 직접 접근해야 하는데, 프록시 설정이나 방화벽이 챌린지를 가림
  • 해결:
    • 일시적으로 프록시 해제(회색 구름) 또는 “웹서버로 직접 라우팅” 설정
    • 가능하면 webroot 모드로 발급

7-3) Unauthorized / Invalid response (404)

  • 원인: /.well-known/acme-challenge/ 경로가 실제로 외부에서 접근되지 않음
  • 해결:
    • webroot 경로가 서버 블록에 정확히 매핑됐는지 확인(오탈자·중복 location 주의)
    • curl http://example.com/.well-known/acme-challenge/test 로 200 응답 확인

7-4) DNS/CAA 문제

  • 원인: 잘못된 A/AAAA 또는 CAA 레코드가 ACME 발급을 제한
  • 해결: A/AAAA가 실제 서버 IP인지 재확인, CAA를 쓰는 경우 letsencrypt.org 허용

7-5) 권한/소유권 문제

  • 원인: /var/www/certbot 또는 /etc/letsencrypt 권한 불일치
  • 해결: sudo chown -R www-data:www-data /var/www/certbot, 기본 권한 유지(/etc/letsencrypt는 root)

7-6) 갱신은 되는데 Nginx가 오래된 인증서를 계속 쓰는 경우

  • 원인: 갱신 후 nginx reload 미실행
  • 해결: 위 renewal hook 스크립트 적용, 또는 /lib/systemd/system/certbot.service의 --deploy-hook 확인

8) 운영 점검 루틴(월 1회 권장)

  • sudo certbot renew --dry-run : 갱신 리허설
  • sudo systemctl status certbot.timer : 타이머 정상 여부
  • sudo ls -l /etc/letsencrypt/live/example.com/ : 갱신일자 확인
  • curl -I https://example.com : 200/리다이렉트/헤더 확인
  • 만료 15일 이내 알람: 모니터링/채팅봇 알림 연동

9) 수동 복구/재발급 팁

  • 완전 초기화(비추): sudo rm -rf /etc/letsencrypt 후 재발급(이전 데이터 모두 삭제되니 주의)
  • 문제만 재현: sudo certbot -vv --nginx -d example.com --dry-run 로 디버그 로그 확보
  • 대체 챌린지: DNS-01(와일드카드 필요 시). 단, DNS 제공자 API 연동이 요구됨

10) 최종 체크리스트

  • DNS A/AAAA 정확, 80/443 방화벽 허용
  • Certbot 발급 성공, nginx -t 통과
  • HTTP→HTTPS 301 정상, 보안 헤더 적용
  • 자동 갱신 타이머 정상, renew --dry-run OK
  • 갱신 후 nginx reload 자동화(renewal hook)

여기까지 설정하면 무료 SSL자동 갱신이 안정적으로 동작합니다. 이후에는 HSTS, TLS 강제 버전 정책, 보안 헤더 스니펫 등을 더해 운영 보안을 한 단계 끌어올리세요.