Nginx 리버스 프록시 실전 가이드: SSL 적용

Nginx 리버스 프록시란?

Nginx 리버스 프록시는 클라이언트 요청을 받아 백엔드 애플리케이션 서버로 전달하고, 응답을 다시 클라이언트에 반환하는 중간 서버입니다. SSL 종료, 로드 밸런싱, 정적 파일 캐싱, 보안 헤더 주입을 한 곳에서 처리할 수 있어 운영 환경의 표준 구성으로 자리잡았습니다.

이 글에서는 Nginx 리버스 프록시의 핵심 설정, SSL 인증서 적용(Let’s Encrypt), 로드 밸런싱 전략, 보안 헤더 설정, 그리고 운영에서 자주 발생하는 문제와 해결법을 정리합니다.

왜 리버스 프록시가 필요한가

역할 직접 노출 시 문제 리버스 프록시로 해결
SSL 종료 각 앱 서버마다 인증서 관리 Nginx에서 한 번만 설정, 백엔드는 HTTP 통신
로드 밸런싱 단일 서버 장애 시 서비스 중단 upstream 블록으로 다수 백엔드 분산
정적 파일 서빙 앱 서버가 정적 파일까지 처리해 성능 저하 Nginx가 정적 파일을 직접 서빙
보안 백엔드 포트·기술 스택 노출 백엔드를 내부 네트워크에 숨기고 보안 헤더 추가
캐싱 동일 요청이 반복되어 백엔드 부하 증가 proxy_cache로 응답 캐싱

기본 리버스 프록시 설정

가장 기본적인 리버스 프록시 설정은 다음과 같습니다. 클라이언트의 요청을 로컬 3000번 포트에서 동작하는 Node.js 앱으로 전달합니다.

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1: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_set_header는 백엔드가 클라이언트의 실제 IP와 프로토콜을 인식할 수 있도록 원본 요청 정보를 전달합니다. 이 헤더가 없으면 백엔드 로그에 모든 요청이 127.0.0.1에서 온 것으로 기록됩니다.

Let’s Encrypt SSL 인증서 적용

Certbot을 사용하면 무료 SSL 인증서를 자동으로 발급받고 갱신할 수 있습니다.

# Certbot 설치 (Ubuntu/Debian)
sudo apt install certbot python3-certbot-nginx

# 인증서 발급 + Nginx 설정 자동 수정
sudo certbot --nginx -d example.com -d www.example.com

# 자동 갱신 테스트
sudo certbot renew --dry-run

Certbot이 자동으로 생성하는 SSL 설정은 다음과 같은 구조입니다.

server {
    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;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    location / {
        proxy_pass http://127.0.0.1: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;
    }
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

upstream을 활용한 로드 밸런싱

여러 백엔드 서버가 있을 때 upstream 블록으로 트래픽을 분산할 수 있습니다.

upstream app_servers {
    least_conn;  # 연결 수가 적은 서버에 우선 배분
    server 127.0.0.1:3001 weight=3;
    server 127.0.0.1:3002 weight=2;
    server 127.0.0.1:3003 backup;  # 다른 서버 모두 다운 시에만 사용
}

server {
    listen 443 ssl http2;
    server_name example.com;

    location / {
        proxy_pass http://app_servers;
        proxy_next_upstream error timeout http_502 http_503;
        proxy_next_upstream_tries 2;
    }
}

로드 밸런싱 알고리즘 비교

알고리즘 설정 적합한 상황
Round Robin 기본값 (별도 설정 불필요) 백엔드 서버 스펙이 동일할 때
Least Connections least_conn 요청 처리 시간이 불균일할 때
IP Hash ip_hash 세션 고정(sticky session)이 필요할 때
Weighted weight=N 서버 스펙이 다를 때

보안 헤더 설정

리버스 프록시에서 보안 헤더를 일괄 추가하면 백엔드 애플리케이션 코드를 수정하지 않고도 보안 수준을 높일 수 있습니다.

# 보안 헤더 (server 또는 location 블록에 추가)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;

# 백엔드 정보 숨기기
proxy_hide_header X-Powered-By;
proxy_hide_header Server;

WebSocket 프록시 설정

실시간 통신이 필요한 애플리케이션(채팅, 대시보드 등)에서는 WebSocket 업그레이드를 처리해야 합니다.

location /ws/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 86400s;  # WebSocket 연결 유지 시간
}

프록시 캐싱으로 백엔드 부하 줄이기

자주 변하지 않는 API 응답이나 페이지를 Nginx에서 캐싱하면 백엔드 요청 수를 줄일 수 있습니다.

# http 블록에 캐시 경로 정의
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m
                 max_size=1g inactive=60m use_temp_path=off;

# location 블록에서 캐시 적용
location /api/ {
    proxy_pass http://app_servers;
    proxy_cache api_cache;
    proxy_cache_valid 200 10m;
    proxy_cache_valid 404 1m;
    proxy_cache_key "$scheme$request_method$host$request_uri";
    add_header X-Cache-Status $upstream_cache_status;
}

운영에서 자주 발생하는 문제와 해결법

문제 증상 해결법
502 Bad Gateway 백엔드 서버 미응답 백엔드 프로세스 상태 확인, proxy_connect_timeout 조정
504 Gateway Timeout 백엔드 응답 시간 초과 proxy_read_timeout 값 증가 (기본 60초)
클라이언트 IP가 127.0.0.1로 기록 X-Real-IP 헤더 누락 proxy_set_header X-Real-IP $remote_addr 추가
큰 파일 업로드 실패 413 Request Entity Too Large client_max_body_size 100m 설정
HTTPS 리다이렉트 무한 루프 ERR_TOO_MANY_REDIRECTS X-Forwarded-Proto 헤더 확인, 앱의 프록시 신뢰 설정

실전 체크리스트

  1. proxy_set_header로 Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto 4개 헤더를 반드시 설정
  2. SSL 인증서 자동 갱신 cron이 동작하는지 certbot renew --dry-run으로 검증
  3. nginx -t로 설정 문법 검사 후 nginx -s reload로 무중단 반영
  4. 보안 헤더(HSTS, X-Frame-Options, CSP)가 응답에 포함되는지 curl -I로 확인
  5. upstream 서버 장애 시 proxy_next_upstream으로 자동 페일오버 설정
  6. access_log, error_log 경로와 로테이션(logrotate) 설정 확인

관련 글

Nginx 리버스 프록시 설정에 대해 궁금한 점이 있거나, 현재 인프라에 맞는 구성이 필요하시면 문의 페이지를 통해 연락해 주세요.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux