TechFeedTechFeed
Cloud & DevOps

Nginx 리버스 프록시 + HTTPS 완전 설정 튜토리얼 — Let's Encrypt 자동 갱신까지

Nginx를 리버스 프록시로 설정하고 Let's Encrypt 인증서로 HTTPS를 적용하는 단계별 실전 가이드. WebSocket 지원, 보안 헤더, 다중 서브도메인 운영, 자동 갱신 설정까지 실제 명령어로 정리.

한 줄 요약: Nginx를 리버스 프록시로 설정하고, Let's Encrypt 인증서로 HTTPS를 적용한 뒤 자동 갱신까지 구성하는 실전 튜토리얼이다.

이 글이 필요한 사람
  • Node.js, Python, Go 앱을 서버에 직접 배포하고 도메인+HTTPS를 붙이고 싶은 개발자
  • 리버스 프록시 개념은 알지만 실제 설정 파일이 헷갈렸던 분
  • 인증서 갱신을 수동으로 하다가 만료 사고를 낸 적 있는 분

※ Ubuntu 22.04 LTS 기준. Nginx 1.18+, Certbot 2.x 환경에서 검증된 설정이다.

Nginx 리버스 프록시가 하는 일

리버스 프록시(Reverse Proxy)는 클라이언트의 요청을 받아 백엔드 서버로 전달하고, 응답을 다시 클라이언트에게 돌려주는 중간 레이어다. 클라이언트 입장에서는 Nginx가 애플리케이션 서버처럼 보인다.

Node.js 앱이 포트 3000에서 실행 중이라면, 직접 yourdomain.com:3000으로 접속하게 두는 것보다 Nginx가 80/443 포트를 받아 3000으로 포워딩하는 구조가 훨씬 안전하고 유연하다.

Nginx 리버스 프록시 아키텍처 다이어그램 — 클라이언트, Nginx, 백엔드 앱 서버 구조
리버스 프록시 구조: 클라이언트 → Nginx(80/443) → 백엔드 앱(3000/8000/...)

리버스 프록시를 사용하면 다음이 가능해진다:

  • 포트 숨기기: 백엔드가 어떤 포트에서 실행되든 외부에는 80/443만 노출
  • SSL 종단(TLS Termination): HTTPS 처리를 Nginx에서 일괄 담당, 앱 서버는 HTTP만 처리
  • 로드 밸런싱: 여러 백엔드 인스턴스에 트래픽 분산
  • 정적 파일 서빙: 이미지·CSS·JS는 Nginx가 직접 서빙해 앱 서버 부하 감소
  • 요청 버퍼링: 느린 클라이언트로 인한 백엔드 스레드 점유 방지

Nginx 설치 및 서비스 확인

Ubuntu/Debian 계열 서버에 Nginx를 설치하고 서비스 상태를 확인하는 과정이다. apt 패키지로 설치하면 systemd 서비스와 기본 설정 파일이 자동으로 구성된다.

Nginx 설치 (Ubuntu 22.04)
# 패키지 목록 업데이트 및 Nginx 설치 sudo apt update sudo apt install -y nginx # 서비스 시작 및 부팅 자동 시작 등록 sudo systemctl start nginx sudo systemctl enable nginx # 상태 확인 sudo systemctl status nginx # 방화벽 허용 (ufw 사용 시) sudo ufw allow 'Nginx Full' sudo ufw status

설치 후 브라우저에서 서버 IP 주소로 접속하면 "Welcome to nginx!" 페이지가 뜨면 정상이다. Nginx의 설정 파일 구조는 아래와 같다:

  • /etc/nginx/nginx.conf — 메인 설정 파일 (worker 프로세스 수, 이벤트 설정 등)
  • /etc/nginx/sites-available/ — 가상 호스트 설정 파일을 보관하는 디렉토리
  • /etc/nginx/sites-enabled/ — 활성화된 설정 파일의 심볼릭 링크
  • /var/log/nginx/ — access.log, error.log 위치
Nginx 설정 문법 검사 명령
설정 파일을 수정할 때마다 sudo nginx -t를 실행해라. 문법 오류가 있으면 reload 전에 잡아준다. nginx: configuration file /etc/nginx/nginx.conf test is successful이 나와야 안전하다.

리버스 프록시 가상 호스트 설정

Node.js 앱이 localhost:3000에서 실행 중이라고 가정하고, yourdomain.com으로 들어오는 HTTP 요청을 해당 포트로 프록시하는 설정을 만든다.

Ubuntu Nginx 패키지는 sites-availablesites-enabled 디렉토리 구조를 사용한다. 설정 파일을 sites-available에 만들고, sites-enabled에 심볼릭 링크를 걸어서 활성화한다.

/etc/nginx/sites-available/yourdomain.com
server { listen 80; listen [::]:80; server_name yourdomain.com www.yourdomain.com; # 클라이언트 요청 바디 최대 크기 (파일 업로드 있을 경우 조정) client_max_body_size 20M; location / { proxy_pass http://localhost:3000; # 원본 요청 정보를 백엔드에 전달 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; # WebSocket 지원 (Next.js HMR, Socket.io 등) proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # 타임아웃 설정 proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } # 정적 파일 직접 서빙 (선택 사항) location /static/ { alias /var/www/yourdomain/static/; expires 30d; add_header Cache-Control "public, immutable"; } }
설정 활성화 및 Nginx 재시작
# sites-enabled에 심볼릭 링크 생성 sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/ # 기본 설정 비활성화 (필요 시) sudo rm /etc/nginx/sites-enabled/default # 문법 검사 sudo nginx -t # 설정 반영 (무중단) sudo systemctl reload nginx

이 시점에서 curl http://yourdomain.com을 실행하면 백엔드 앱이 응답해야 한다. 백엔드가 실행 중이지 않으면 502 Bad Gateway가 뜬다 — 앱 서버 상태부터 확인할 것.

Nginx 설정 파일 구조 — sites-available, sites-enabled 디렉토리와 심볼릭 링크 관계
sites-available에 설정 파일을 만들고 sites-enabled에 symlink를 걸어 활성화하는 구조

Certbot으로 Let's Encrypt SSL 인증서 발급

Let's Encrypt는 무료 SSL 인증서를 자동으로 발급·갱신해주는 CA(인증 기관)다. Certbot은 Let's Encrypt의 공식 클라이언트 도구로, Nginx 플러그인을 사용하면 인증서 발급과 Nginx 설정 수정을 한 번에 처리해준다.

인증서 발급 전에 반드시 도메인의 DNS A 레코드가 서버 IP를 가리키고 있어야 한다. DNS 전파 전에 실행하면 인증 실패한다.

Certbot 설치 및 SSL 인증서 발급
# Certbot과 Nginx 플러그인 설치 sudo apt install -y certbot python3-certbot-nginx # SSL 인증서 발급 (Nginx 플러그인 사용) # --nginx: Nginx 설정 자동 수정 # -d: 도메인 지정 (여러 개 지정 가능) sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com # 이메일 입력 후 약관 동의 → 인증서 발급 완료 # 인증서 경로: # /etc/letsencrypt/live/yourdomain.com/fullchain.pem # /etc/letsencrypt/live/yourdomain.com/privkey.pem

Certbot이 Nginx 플러그인으로 실행되면 기존 설정 파일에 SSL 관련 설정을 자동으로 추가하고, HTTP 요청을 HTTPS로 리다이렉트하는 블록도 함께 삽입해준다. 발급 완료 후 브라우저에서 https://yourdomain.com에 접속해 자물쇠 아이콘이 뜨면 성공이다.

인증서 발급 속도 제한 주의
Let's Encrypt는 동일 도메인에 대해 주당 5회 발급 제한이 있다. 테스트 시에는 --staging 플래그를 추가해 스테이징 환경에서 먼저 검증하라. 스테이징 인증서는 신뢰할 수 없지만 발급 횟수 제한이 없다.

HTTPS 완성 설정 — 보안 헤더 및 TLS 옵션

Certbot이 자동으로 수정한 설정에 보안 헤더와 TLS 파라미터를 추가해 완성도를 높인다. SSL Labs 테스트에서 A+ 등급을 받으려면 아래 설정이 필요하다.

HTTPS 완성 설정 (/etc/nginx/sites-available/yourdomain.com)
# HTTP → HTTPS 리다이렉트 server { listen 80; listen [::]:80; server_name yourdomain.com www.yourdomain.com; return 301 https://$host$request_uri; } # HTTPS 메인 설정 server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name yourdomain.com www.yourdomain.com; # SSL 인증서 경로 (Certbot이 자동 설정) ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; # 권장 TLS 설정 (Certbot이 생성하는 파일) include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # 보안 헤더 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; client_max_body_size 20M; location / { proxy_pass http://localhost:3000; 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 Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } }

설정 변경 후 항상 sudo nginx -t && sudo systemctl reload nginx를 실행한다. nginx -t가 실패하면 reload를 실행하지 않도록 &&로 연결해두는 것이 실수를 방지하는 좋은 습관이다.

SSL 인증서 자동 갱신 설정

Let's Encrypt 인증서의 유효 기간은 90일이다. Certbot 설치 시 자동으로 systemd 타이머 또는 cron이 등록되어 갱신 작업을 처리한다. 실제로 자동 갱신이 설정되어 있는지 확인하고, 테스트까지 완료해야 한다.

자동 갱신 확인 및 테스트
# systemd 타이머 확인 (Ubuntu 22.04 기본) sudo systemctl status certbot.timer sudo systemctl list-timers | grep certbot # 자동 갱신 드라이런 테스트 (실제 갱신 없이 시뮬레이션) sudo certbot renew --dry-run # 수동 갱신 (필요 시) sudo certbot renew # 갱신 후 Nginx 재시작 (hooks 활용) # /etc/letsencrypt/renewal-hooks/deploy/ 에 스크립트 추가 cat << 'EOF' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh #!/bin/bash systemctl reload nginx EOF sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

certbot renew --dry-run"Congratulations, all simulated renewals succeeded"를 출력하면 자동 갱신이 정상 동작하는 것이다. 인증서 갱신 후 Nginx를 reload하지 않으면 새 인증서가 적용되지 않으므로, deploy hook에 스크립트를 추가해두는 것이 중요하다.

SSL 인증서 만료일 확인 — certbot certificates 명령 출력 결과
<code>sudo certbot certificates</code>로 발급된 인증서 목록과 만료일을 확인할 수 있다

여러 앱을 하나의 서버에서 운영하기

하나의 서버에서 여러 도메인 또는 서브도메인을 각각 다른 백엔드 포트로 라우팅할 수 있다. 각 도메인마다 별도의 server 블록을 만들고, Certbot으로 각각 인증서를 발급하면 된다.

여러 백엔드를 서브도메인으로 분리하는 설정
# /etc/nginx/sites-available/api.yourdomain.com server { listen 443 ssl http2; server_name api.yourdomain.com; ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; add_header Strict-Transport-Security "max-age=31536000" always; location / { proxy_pass http://localhost:8000; # Python FastAPI, Go 서버 등 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; } } server { listen 80; server_name api.yourdomain.com; return 301 https://$host$request_uri; } # 인증서 발급 (여러 서브도메인 동시 가능) # sudo certbot --nginx -d api.yourdomain.com

앱이 늘어날 때마다 sites-available에 파일을 추가하고 symlink를 걸어 활성화하면 된다. 각 앱이 다른 포트를 사용하기만 하면 충돌 없이 동시 운영이 가능하다. 포트 번호는 3000, 3001, 4000, 8000, 8080 등 관행적으로 자주 쓰이는 번호를 사용한다.

자주 막히는 케이스와 해결법

Nginx 리버스 프록시 설정에서 반복적으로 발생하는 오류들과 해결 방법을 정리했다.

케이스 1: 502 Bad Gateway
원인: 백엔드 앱이 실행 중이 아니거나, proxy_pass에 잘못된 포트 번호를 입력했다.
해결: curl http://localhost:3000으로 백엔드 직접 응답 확인. 앱이 실행 중인지, 포트가 일치하는지 체크.
케이스 2: Certbot 인증 실패 — "Connection refused"
원인: DNS A 레코드가 아직 서버 IP를 가리키지 않거나, 80번 포트가 방화벽에 막혀 있다.
해결: dig yourdomain.com으로 IP 확인. sudo ufw allow 80으로 방화벽 허용.
케이스 3: WebSocket 연결 끊김
원인: proxy_http_version 1.1과 Upgrade 헤더 설정이 빠져 있다.
해결: location 블록에 proxy_http_version 1.1;, proxy_set_header Upgrade $http_upgrade;, proxy_set_header Connection "upgrade"; 세 줄을 추가해라.
케이스 4: 파일 업로드 실패 — 413 Request Entity Too Large
원인: Nginx의 기본 client_max_body_size가 1MB로 제한되어 있다.
해결: server 블록 또는 location 블록에 client_max_body_size 50M; 추가.
케이스 5: HSTS 설정 후 사이트 접근 불가
원인: HTTPS 설정이 완전히 준비되기 전에 HSTS 헤더를 배포하면, 브라우저가 HTTP 접속을 차단한다.
해결: SSL 설정이 완전히 안정된 후에 Strict-Transport-Security 헤더를 추가할 것. 긴급 시 브라우저 캐시에서 HSTS를 수동으로 삭제(chrome://net-internals/#hsts).
Nginx리버스 프록시HTTPSLet's EncryptCertbotSSLTLSUbuntuDevOps서버 설정

관련 포스트

Docker Compose vs Kubernetes — 실무에서 언제 무엇을 쓸까2026-03-20Docker Compose vs Kubernetes — 언제 무엇을 선택할까2026-03-22프로덕션 배포 체크리스트 — 서비스 출시 전 반드시 점검해야 할 42개 항목2026-03-25Docker 멀티스테이지 빌드 실전 가이드 — 이미지 크기 90% 줄이기2026-03-31