들어가며
이번 글에서는 GCP Ubuntu 서버 환경에서 Nginx 리버스 프록시 + Next.js 애플리케이션을 구성한 뒤, Certbot을 이용해 무료 SSL 인증서를 발급하고 자동 갱신하는 과정을 정리해보려고 한다.
실습 환경은 다음과 같다.
- GCP Compute Engine (Ubuntu)
- Nginx
- Next.js
- Certbot (Let's Encrypt)
추가로, 해당 실습은 도메인이 꼭 필요하다
준비사항
- 아래 과정은 모두 서버에서 진행한다.
Nginx 설치

Node.js 설치
Next.js 프로젝트 생성
npx create-next-app@latest certbot-test-app
프로젝트 빌드 및 백그라운드 실행
- 실무에서는 PM2로 프로세스를 관리하겠지만 해당 글에서는 직접 실행한다.

- 3000 포트로 Nextjs 프로세스와, 80 포트로 nginx가 실행된것을 확인할 수 있다.
외부에서 http 기본포트(80)로 접근했을때 Nextjs 앱을 연결
Nginx 설정파일 추가
- 경로(
/etc/nginx/conf.d)에 아래 설정파일을 추가한다. - {도메인}은 작업하려는 도메인을 입력한다.
Nginx 설정 확인 및 재시작
도메인 DNS설정(DNS 레코드 등록)
- 도메인을 구매한 사이트에서 DNS 설정을 진행한다. 서버IP는 외부IP로 등록한다.
외부에서 확인
이제 curl로 외부에서 도메인으로 접근하면, nginx 인덱스 페이지가 아닌 Nextjs 애플리케이션의 인덱스 페이지가 응답으로 반환되게 된다.
curl http://{도메인}

Certbot으로 SSL 인증서 최초 발급
앞서 Nginx와 Next.js를 구성하고 도메인까지 연결했다면 이제 HTTPS 적용을 위한 SSL 인증서를 발급할 차례다.
이번 글에서는 Certbot의 webroot 방식을 사용하여 인증서를 발급한다.
Certbot 설치
sudo apt update
sudo apt install certbot -y
설치 확인
certbot --version
ACME Challenge 경로 설정
Let's Encrypt는 도메인 소유 여부를 확인하기 위해 아래 URL로 접근한다.
http://{도메인}/.well-known/acme-challenge/{파일명}
따라서 Nginx가 해당 경로에 대한 요청을 Next.js가 아닌 실제 파일 경로로 처리하도록 설정해야 한다.
먼저 인증서 검증용 디렉터리를 생성한다.
sudo mkdir -p /var/www/{도메인}/html/.well-known/acme-challenge
// ex
sudo mkdir -p /var/www/test.hwaneehwanee.app/html/.well-known/acme-challenge
Nginx 설정 추가
기존 설정에 아래 location 블록을 추가한다.
설정을 반영한다.
sudo nginx -t
sudo nginx -s reload
Challenge 경로 확인
테스트 파일을 생성한다.
외부에서 접근이 가능한지 확인한다.
curl http://{도메인}/.well-known/acme-challenge/test
// ex
curl http://test.hwaneehwanee.app/.well-known/acme-challenge/test
정상이라면 아래와 같이 응답한다.
hello certbot

이 단계가 정상 동작하지 않으면 인증서 발급도 실패한다.
인증서 발급 스크립트 작성
실행 권한 부여
chmod +x certbot-init.sh
인증서 발급
./certbot-init.sh {도메인}
// ex
./certbot-init.sh test.hwaneehwanee.app
정상적으로 발급되면 아래 경로에 인증서가 생성된다.
/etc/letsencrypt/live/{도메인}/fullchain.pem
/etc/letsencrypt/live/{도메인}/privkey.pem
발급 확인
인증서 정보를 확인할 수 있다.
또는 만료일만 확인하려면
인증서 발급에 실패한다면
Let's Encrypt는 인터넷을 통해 직접 서버에 접근하여 도메인 소유 여부를 검증한다.
따라서 아래 URL이 외부 인터넷 환경에서도 정상적으로 접근 가능해야 한다.
http://{도메인}/.well-known/acme-challenge/test
간혹 서버 내부에서는 접근되지만, 방화벽이나 네트워크 설정 때문에 외부에서는 접근되지 않는 경우가 있다.
특히 아래 항목을 확인해보자.
- 서버의 80 포트가 외부에 개방되어 있는지
- GCP, AWS 등의 보안 그룹(Security Group) 또는 방화벽 규칙이 허용되어 있는지
- 공유기 환경이라면 포트포워딩이 설정되어 있는지
- 서버 내부에서만 접근 가능한 사설 네트워크 환경은 아닌지
예를 들어 서버 내부에서는 정상적으로 응답하지만,
curl http://localhost
외부 네트워크에서는 접속이 불가능하다면 인증서 발급에 실패하게 된다.
따라서 인증서 발급 전에 반드시 스마트폰 LTE/5G 환경이나 다른 외부 네트워크에서 아래 주소가 정상적으로 열리는지 확인하는 것을 권장한다.
http://{도메인}/.well-known/acme-challenge/test
Let's Encrypt는 서버 내부가 아닌 외부 인터넷 환경에서 검증을 수행하기 때문이다.
이제 SSL 인증서 발급이 완료되었다. 다음 단계에서는 발급된 인증서를 Nginx에 적용하여 HTTPS 서비스를 구성해보자.
발급한 SSL 인증서를 Nginx에 적용하기
이전 단계에서 Certbot을 통해 SSL 인증서를 발급받았다.
이번에는 발급된 인증서를 Nginx에 적용하여 HTTPS로 서비스를 제공해보자.
발급된 인증서 확인
Certbot으로 발급한 인증서는 아래 경로에 저장된다.
/etc/letsencrypt/live/{도메인}/fullchain.pem
/etc/letsencrypt/live/{도메인}/privkey.pem
각 파일의 역할은 다음과 같다.
| 파일 | 설명 |
|---|---|
| fullchain.pem | 서버 인증서 + 중간 인증서 |
| privkey.pem | 개인키 |
Nginx HTTPS 설정 추가
기존 설정 파일을 수정한다.
Nginx 설정 반영
설정 파일 문법을 확인한다.
sudo nginx -t
정상이라면 설정을 재적용한다.
sudo nginx -s reload
HTTPS 접속 확인
브라우저에서 아래 주소로 접속한다.
https://{도메인}
// ex
https://test.hwaneehwanee.app
정상적으로 적용되었다면 주소창에 자물쇠 아이콘이 표시되고 HTTPS로 접속된다.

이제 서비스는 HTTPS를 통해 암호화된 통신을 사용할 수 있게 되었다. 다음 단계에서는 Certbot 인증서 자동 갱신 설정과 갱신 테스트를 진행해보자.
Certbot 인증서 강제 갱신 및 적용
Certbot으로 발급한 인증서는 기본적으로 만료일이 가까워졌을 때만 갱신된다.
따라서 인증서를 처음 구성하거나 자동 갱신 설정을 테스트하는 경우에는 갱신이 수행되지 않을 수 있다.
이번 글에서는 인증서를 강제로 재발급하여 갱신 프로세스와 Nginx 적용 여부를 확인해보자.
기본 갱신 명령
일반적으로 Certbot은 아래 명령을 통해 갱신한다.
sudo certbot renew
하지만 인증서 만료일이 충분히 남아있다면 아래와 같은 메시지를 출력하며 아무 작업도 수행하지 않는다.

이는 정상 동작이다.
강제 갱신 스크립트 작성
갱신 시점을 기다리지 않고 즉시 재발급하려면 --force-renewal 옵션을 사용할 수 있다.
실행 권한 부여
chmod +x certbot-force-renew.sh
강제 갱신 실행
./certbot-force-renew.sh {도메인}
정상적으로 완료되면 새로운 인증서가 발급되고 Nginx가 재적용된다.

주의사항
--force-renewal 옵션은 실제 인증서를 새로 발급받는 동작이다.
Let's Encrypt에는 인증서 발급 횟수 제한(Rate Limit)이 존재하므로 운영 환경에서 반복적으로 실행하는 것은 권장하지 않는다.
Certbot 자동 갱신 설정 (Cron)
이전 글에서는 Certbot 인증서를 강제로 재발급하여 갱신 프로세스를 확인했다.
실제 운영 환경에서는 주기적으로 Certbot을 실행하여 인증서 만료 전에 자동으로 갱신되도록 구성하는 것이 일반적이다.
이번 글에서는 Cron을 이용하여 매월 자동으로 인증서 갱신을 수행하도록 설정해보자.
Cron 등록
0 3 1 * * /home/{user}/certbot-force-renew.sh >> /var/log/certbot-force-renew.log 2>&1
마무리
이번 글에서는 Certbot 인증서를 발급받고 강제로 갱신해보았다.
지금까지 진행한 내용을 정리하면 다음과 같다.
- Nginx 설치 및 Next.js 애플리케이션 연결
- Certbot을 이용한 SSL 인증서 최초 발급
- Nginx HTTPS 설정 및 인증서 적용
- 인증서 강제 갱신 테스트
- Cron을 이용한 자동 갱신 설정
따라서 매월 한 번 정도 강제갱신을 실행하도록 설정해두면 별도의 수동 작업 없이 안정적으로 HTTPS 서비스를 운영해볼 수 있다.
