Nginx Reverse Proxy 설정 가이드

Nginx Reverse Proxy란 무엇인가

Reverse Proxy는 클라이언트와 백엔드 서버 사이에 위치하여 요청을 중계하는 서버입니다. Nginx는 경량 아키텍처와 높은 동시 처리 성능 덕분에 가장 널리 사용되는 Reverse Proxy입니다. 로드 밸런싱, SSL 종료, 정적 파일 캐싱, 보안 헤더 주입 등 다양한 역할을 프록시 레이어에서 처리하면 백엔드 애플리케이션은 비즈니스 로직에만 집중할 수 있습니다.

이 글에서는 Nginx Reverse Proxy의 동작 원리, 실전 설정 패턴 6가지, SSL/TLS 구성, 로드 밸런싱 전략, 보안 강화 설정, 그리고 흔한 실수와 해결법까지 다룹니다.

Reverse Proxy vs Forward Proxy

항목 Forward Proxy Reverse Proxy
위치 클라이언트 앞 서버 앞
보호 대상 클라이언트 IP 숨김 서버 IP 숨김
용도 접근 제어, 캐시 로드 밸런싱, SSL 종료, 캐싱
대표 도구 Squid, Privoxy Nginx, HAProxy, Traefik

기본 설정: proxy_pass 한 줄로 시작하기

Nginx Reverse Proxy의 핵심은 proxy_pass 지시문입니다. 클라이언트 요청을 백엔드 서버로 전달합니다.

server {
    listen 80;
    server_name api.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로 원본 클라이언트 정보를 백엔드에 전달하는 것이 중요합니다. 이 헤더가 없으면 백엔드는 모든 요청이 127.0.0.1에서 온 것으로 인식합니다.

실전 패턴 1: SSL/TLS 종료 + HTTP/2

SSL 인증서 관리를 Nginx에서 처리하면 백엔드는 HTTP만 처리하면 됩니다. Let’s Encrypt와 Certbot을 사용한 자동 갱신 설정입니다.

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

    ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.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;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    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 api.example.com;
    return 301 https://$host$request_uri;
}

실전 패턴 2: upstream 로드 밸런싱

여러 백엔드 서버에 트래픽을 분산하려면 upstream 블록을 사용합니다.

upstream backend {
    least_conn;
    server 10.0.1.10:3000 weight=3;
    server 10.0.1.11:3000 weight=2;
    server 10.0.1.12:3000 backup;
    keepalive 32;
}

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

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
알고리즘 지시문 적합한 상황
라운드 로빈 (기본값) 서버 성능이 균일할 때
최소 연결 least_conn 요청 처리 시간이 불균일할 때
IP 해시 ip_hash 세션 고정(sticky)이 필요할 때
가중치 weight=N 서버 성능이 다를 때

keepalive 32proxy_http_version 1.1은 백엔드와의 TCP 연결을 재사용합니다. 연결 수립 오버헤드를 줄여 응답 시간이 개선됩니다.

실전 패턴 3: WebSocket 프록시

WebSocket은 HTTP Upgrade 메커니즘을 사용하므로 별도 헤더 설정이 필요합니다.

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;
    proxy_send_timeout 86400s;
}

proxy_read_timeout을 충분히 길게 설정하지 않으면 유휴 WebSocket 연결이 끊어집니다. 기본값 60초는 대부분의 WebSocket 애플리케이션에서 너무 짧습니다.

실전 패턴 4: 정적 파일 캐싱

proxy_cache_path /var/cache/nginx levels=1:2
    keys_zone=app_cache:10m max_size=1g inactive=60m;

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

    location /static/ {
        proxy_pass http://127.0.0.1:3000;
        proxy_cache app_cache;
        proxy_cache_valid 200 1d;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout updating;
        add_header X-Cache-Status $upstream_cache_status;
    }

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
    }
}

proxy_cache_use_stale는 백엔드가 응답하지 않을 때 캐시된 응답을 반환합니다. 백엔드 장애 시에도 사용자에게 정적 파일을 제공할 수 있습니다.

실전 패턴 5: Rate Limiting

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

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

    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        limit_req_status 429;

        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

burst=20은 순간적으로 20개 요청까지 허용하고, nodelay는 burst 내 요청을 지연 없이 처리합니다. DDoS 방어의 첫 번째 방어선으로 유효합니다.

실전 패턴 6: 보안 헤더 주입

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

    # 보안 헤더
    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 Content-Security-Policy "default-src 'self'" always;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

    # 서버 버전 숨기기
    server_tokens off;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_hide_header X-Powered-By;
    }
}

성능 튜닝 핵심 파라미터

파라미터 기본값 권장값 설명
worker_processes 1 auto (CPU 코어 수) 워커 프로세스 수
worker_connections 512 4096~8192 워커당 동시 연결 수
proxy_connect_timeout 60s 5~10s 백엔드 연결 타임아웃
proxy_read_timeout 60s 30~60s (API), 86400s (WS) 백엔드 응답 대기
proxy_buffering on on (대부분), off (SSE) 응답 버퍼링
gzip off on 응답 압축

gzip 압축 설정

gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml;

흔한 실수와 해결법

실수 1: proxy_pass 뒤 슬래시 유무 혼동

proxy_pass http://backend;proxy_pass http://backend/;는 URL 경로 전달 방식이 다릅니다. 슬래시가 있으면 location 매칭 부분이 제거되고, 없으면 전체 URI가 전달됩니다. 의도와 다른 라우팅이 발생하면 이 부분을 확인하세요.

실수 2: upstream keepalive 미설정

keepalive 없이 매 요청마다 TCP 연결을 새로 수립하면 성능이 저하됩니다. keepalive + proxy_http_version 1.1 + Connection "" 세 가지를 함께 설정해야 합니다.

실수 3: 대용량 파일 업로드 실패

Nginx의 기본 client_max_body_size는 1MB입니다. 파일 업로드가 필요하면 적절한 값으로 늘려야 합니다.

client_max_body_size 50m;

실수 4: SSL 인증서 갱신 미자동화

Let’s Encrypt 인증서는 90일마다 만료됩니다. Certbot 자동 갱신 크론잡이 설정되어 있는지 반드시 확인하세요.

0 0 1 * * certbot renew --quiet --post-hook "systemctl reload nginx"

운영 체크리스트

  1. nginx -t로 설정 파일 문법 검증 후 reload
  2. SSL Labs 테스트 (ssllabs.com)로 TLS 설정 등급 확인 (A+ 목표)
  3. access_log, error_log 경로와 로테이션 설정 확인
  4. server_tokens off로 Nginx 버전 숨김
  5. upstream health check 또는 max_fails/fail_timeout 설정
  6. 모니터링: stub_status 또는 Prometheus exporter 연동
  7. 설정 파일을 Git으로 버전 관리하여 변경 이력 추적

마무리

Nginx Reverse Proxy는 단순한 요청 전달을 넘어 SSL 종료, 로드 밸런싱, 캐싱, 보안, Rate Limiting까지 처리하는 인프라의 핵심 계층입니다. 위 패턴을 프로젝트에 맞게 조합하면, 백엔드 애플리케이션의 부담을 줄이면서 안정성과 보안을 동시에 확보할 수 있습니다.

컨테이너 환경에서 Nginx를 운영한다면 Docker Multi-Stage Build 최적화로 Nginx 이미지 크기를 줄이세요. CI/CD 파이프라인에서 설정 파일 검증을 자동화하려면 GitHub Actions CI/CD 파이프라인 설계 가이드를 참고하세요.

현재 운영 중인 Nginx 설정에 보안 헤더와 keepalive를 추가해 보세요. 적용 전후 응답 시간 변화를 댓글로 공유해 주시면 추가 튜닝 팁을 드리겠습니다.

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