서론: 리버스 프록시, 왜 필요한가

현대 웹 서비스는 단일 서버로 운영되는 경우가 거의 없습니다. 프론트엔드는 React나 Vue로 빌드되어 정적 파일로 서빙되고, 백엔드는 Node.js, Python, Java, Go 등으로 작성된 애플리케이션 서버가 담당합니다. 여기에 데이터베이스, 캐시, 메시지 큐까지 결합되면 서비스는 수십 개의 컴포넌트로 구성된 복잡한 시스템이 됩니다.

이때 Nginx 리버스 프록시는 이 모든 컴포넌트 앞에서 트래픽을 지휘하는 역할을 합니다. 사용자는 https://example.com 하나의 진입점으로 접속하지만, Nginx는 요청 경로와 호스트명에 따라 적절한 백엔드로 트래픽을 라우팅합니다. 이를 통해 SSL 종료, 로드밸런싱, 캐싱, 보안, 압축, 모니터링 등을 중앙에서 일관되게 관리할 수 있습니다.

이 가이드에서는 Nginx 리버스 프록시의 개념부터 실전 구성, 최적화, 문제 해결까지 실무에서 필요한 모든 내용을 다룹니다. Node.js, Python, Java 등 다양한 백엔드 연동 예제와 WebSocket, gRPC 등 특수한 프로토콜 처리, 그리고 운영 중 자주 마주치는 문제의 해결 방법까지 실제 설정 파일과 함께 상세히 설명합니다.

1. 리버스 프록시 vs 포워드 프록시

1.1 개념 차이

포워드 프록시(Forward Proxy)는 클라이언트 측에 위치하여 클라이언트를 대신해 외부 서버에 요청을 전달합니다. 회사의 방화벽, VPN, 학교의 인터넷 필터링 등이 대표적인 예입니다. 클라이언트는 프록시를 인식하지만, 서버는 프록시의 존재를 모릅니다.

리버스 프록시(Reverse Proxy)는 서버 측에 위치하여 서버를 대신해 클라이언트 요청을 받습니다. 클라이언트는 프록시를 실제 서버로 인식하며, 프록시 뒤에 숨겨진 실제 서버(백엔드)의 존재를 알지 못합니다.

구분 포워드 프록시 리버스 프록시
위치 클라이언트 측 서버 측
대리 대상 클라이언트를 대리 서버를 대리
주요 용도 접근 제어, 캐싱, 익명화 로드밸런싱, SSL 종료, 캐싱
대표 예시 Squid, Privoxy Nginx, HAProxy, Traefik

1.2 리버스 프록시의 주요 이점

  • 단일 진입점: 여러 백엔드 서버를 하나의 도메인/IP로 통합
  • SSL 종료: HTTPS 처리를 프록시에서 중앙 관리, 백엔드는 HTTP 사용 가능
  • 로드밸런싱: 여러 백엔드에 트래픽 분산
  • 캐싱: 백엔드 부하 감소, 응답 속도 향상
  • 압축: Gzip/Brotli 압축을 프록시에서 처리
  • 보안: 백엔드 서버 직접 노출 방지, WAF 적용 가능
  • Rate Limiting: 중앙에서 요청 제한 관리
  • 로그 중앙화: 모든 요청을 한 곳에서 로깅

2. proxy_pass 기본 사용법

2.1 가장 기본적인 프록시 설정

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

이것만으로도 기본적인 리버스 프록시가 동작합니다. 하지만 실무에서는 최소한 다음 헤더들을 반드시 설정해야 합니다:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;

        # 원본 호스트 정보 전달
        proxy_set_header Host $host;

        # 클라이언트 실제 IP (백엔드가 로깅할 때 사용)
        proxy_set_header X-Real-IP $remote_addr;

        # 프록시 체인 정보
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # 원본 프로토콜 (http/https)
        proxy_set_header X-Forwarded-Proto $scheme;

        # 원본 포트
        proxy_set_header X-Forwarded-Port $server_port;

        # 원본 호스트
        proxy_set_header X-Forwarded-Host $host;
    }
}
중요: proxy_set_header Host $host;를 설정하지 않으면 Nginx는 기본값으로 proxy_pass에 명시된 호스트(여기서는 127.0.0.1:3000)를 Host 헤더로 전달합니다. 이는 가상 호스트 라우팅을 사용하는 백엔드에서 문제를 일으킵니다.

2.2 proxy_pass URL의 슬래시 차이

proxy_pass의 URL 끝에 슬래시(/)가 있느냐 없느냐에 따라 동작이 완전히 달라집니다. 이것은 Nginx 초보자가 가장 자주 혼동하는 부분입니다:

# 사례 1: proxy_pass에 슬래시 없음
# /api/users → http://backend/api/users (경로 그대로 전달)
location /api/ {
    proxy_pass http://backend;
}

# 사례 2: proxy_pass에 슬래시 있음
# /api/users → http://backend/users (location 경로 제거)
location /api/ {
    proxy_pass http://backend/;
}

# 사례 3: 경로 교체
# /api/users → http://backend/v1/users (/api/가 /v1/로 치환)
location /api/ {
    proxy_pass http://backend/v1/;
}
기억하기:
슬래시 없음 = 경로를 그대로 전달
슬래시 있음 = location 경로를 잘라내고 남은 부분만 전달
이 차이를 명확히 이해하지 못하면 404 에러나 잘못된 라우팅이 발생합니다.

3. 백엔드별 프록시 설정

3.1 Node.js (Express, Next.js)

# Node.js 앱 (기본 포트 3000)
upstream nodejs_backend {
    server 127.0.0.1:3000;
    keepalive 32;
}

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

    location / {
        proxy_pass http://nodejs_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";    # keepalive 사용

        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;

        # Node.js의 req.ip를 위한 trust proxy 설정 필요
        # app.set('trust proxy', true);

        proxy_read_timeout 300;
        proxy_connect_timeout 75;
    }
}

Next.js 특화 설정

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

    # Next.js 정적 파일 (빌드 시 생성)
    location /_next/static/ {
        proxy_pass http://127.0.0.1:3000;
        proxy_cache_valid 200 60m;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # 이미지 최적화 API
    location /_next/image {
        proxy_pass http://127.0.0.1:3000;
        proxy_cache_valid 200 60m;
    }

    # 나머지 요청
    location / {
        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_cache_bypass $http_upgrade;
    }
}

3.2 Python (Django, Flask, FastAPI)

# Gunicorn으로 실행 중인 Django/Flask/FastAPI
upstream python_backend {
    server 127.0.0.1:8000 fail_timeout=30s;
}

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

    # 정적 파일은 Nginx가 직접 서빙 (Django collectstatic 등)
    location /static/ {
        alias /var/www/example/static/;
        expires 30d;
        access_log off;
    }

    location /media/ {
        alias /var/www/example/media/;
        expires 7d;
    }

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

        # Python 앱은 보통 응답 시간이 길 수 있음
        proxy_read_timeout 120;
        proxy_connect_timeout 75;

        # Django: SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
        # Flask: app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
    }
}

Unix 소켓 사용 (성능 향상)

# Gunicorn을 Unix 소켓으로 실행한 경우
# gunicorn app:app --bind unix:/tmp/gunicorn.sock
upstream python_backend {
    server unix:/tmp/gunicorn.sock;
}

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

    location / {
        proxy_pass http://python_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

3.3 Java (Spring Boot, Tomcat)

# Spring Boot 앱 (기본 포트 8080)
upstream spring_backend {
    server 127.0.0.1:8080;
    keepalive 32;
}

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

    client_max_body_size 50M;    # Java 앱은 큰 페이로드가 흔함

    location / {
        proxy_pass http://spring_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";

        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;

        # Spring Boot application.properties:
        # server.forward-headers-strategy=native
        # server.tomcat.remote-ip-header=X-Forwarded-For
        # server.tomcat.protocol-header=X-Forwarded-Proto

        proxy_read_timeout 300;
        proxy_buffer_size 16k;
        proxy_buffers 8 16k;
    }
}

3.4 PHP-FPM (별도 섹션)

PHP는 proxy_pass 대신 fastcgi_pass를 사용합니다. 엄밀히 말하면 리버스 프록시가 아니라 FastCGI 프록시입니다:

server {
    listen 80;
    server_name php.example.com;
    root /var/www/php-app/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

4. WebSocket 프록시

WebSocket은 HTTP 1.1의 Upgrade 메커니즘을 사용합니다. 일반적인 HTTP 프록시 설정으로는 WebSocket이 동작하지 않으며, 다음과 같은 특수 설정이 필요합니다:

# WebSocket 프록시 기본 설정
server {
    listen 80;
    server_name ws.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;

        # WebSocket 필수 설정
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # WebSocket은 장시간 연결 유지
        proxy_read_timeout 86400s;    # 24시간
        proxy_send_timeout 86400s;

        # 버퍼링 비활성화 (실시간 양방향 통신)
        proxy_buffering off;
    }
}

4.1 HTTP와 WebSocket 동시 처리

# Socket.IO처럼 HTTP와 WebSocket을 같은 포트에서 처리하는 경우
# $connection_upgrade 맵 정의 (http 블록)
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

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

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 86400s;
    }

    # WebSocket만 별도 경로로 분리하는 경우
    location /socket.io/ {
        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;
    }
}

5. 마이크로서비스 라우팅

여러 백엔드 서비스를 단일 도메인으로 통합하는 것은 리버스 프록시의 가장 강력한 활용 사례입니다.

5.1 경로 기반 라우팅

# upstream 정의
upstream frontend { server 127.0.0.1:3000; }
upstream api_service { server 127.0.0.1:4000; }
upstream auth_service { server 127.0.0.1:5000; }
upstream upload_service { server 127.0.0.1:6000; }

server {
    listen 80;
    server_name example.com;

    # 프론트엔드 (SPA)
    location / {
        proxy_pass http://frontend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # API 서비스
    location /api/ {
        proxy_pass http://api_service/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # 인증 서비스
    location /auth/ {
        proxy_pass http://auth_service/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # 인증 서비스는 Rate Limit 엄격하게
        limit_req zone=auth_limit burst=5 nodelay;
    }

    # 업로드 서비스 (큰 파일 처리)
    location /upload/ {
        proxy_pass http://upload_service/;
        proxy_set_header Host $host;

        # 업로드는 큰 body 허용
        client_max_body_size 500M;

        # 타임아웃 길게
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;

        # 버퍼링 비활성화 (스트리밍 업로드)
        proxy_request_buffering off;
    }
}

5.2 호스트명 기반 라우팅 (서브도메인)

# 각 서브도메인을 다른 서비스로 라우팅
server {
    listen 80;
    server_name app.example.com;
    location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; }
}

server {
    listen 80;
    server_name api.example.com;
    location / { proxy_pass http://127.0.0.1:4000; proxy_set_header Host $host; }
}

server {
    listen 80;
    server_name admin.example.com;
    location / {
        allow 10.0.0.0/8;
        deny all;
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
    }
}

6. 로드밸런싱 통합

여러 백엔드 인스턴스를 운영할 때, 리버스 프록시에서 자연스럽게 로드밸런싱을 수행할 수 있습니다.

# 여러 백엔드 인스턴스
upstream api_cluster {
    # 로드밸런싱 방식 (미지정 시 round-robin)
    least_conn;

    server 10.0.0.10:8080 weight=3 max_fails=3 fail_timeout=30s;
    server 10.0.0.11:8080 weight=2 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:8080 weight=1 max_fails=3 fail_timeout=30s;

    server 10.0.0.13:8080 backup;   # 백업 서버
    server 10.0.0.14:8080 down;     # 유지보수 중

    keepalive 32;    # 각 워커당 유지할 유휴 연결 수
}

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

    location / {
        proxy_pass http://api_cluster;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        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_next_upstream error timeout http_500 http_502 http_503 http_504;
        proxy_next_upstream_tries 3;
        proxy_next_upstream_timeout 10s;
    }
}

7. 프록시 캐싱

리버스 프록시의 캐싱 기능을 활용하면 백엔드 부하를 크게 줄일 수 있습니다.

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

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

        location / {
            proxy_pass http://api_backend;
            proxy_set_header Host $host;

            # 캐시 활성화
            proxy_cache api_cache;
            proxy_cache_key "$scheme$request_method$host$request_uri";

            # 캐시 유효 시간
            proxy_cache_valid 200 302 10m;   # 성공 응답 10분
            proxy_cache_valid 404 1m;        # 404는 1분
            proxy_cache_valid any 30s;       # 나머지는 30초

            # 캐시 락 (동시 요청에서 한 번만 백엔드 호출)
            proxy_cache_lock on;
            proxy_cache_lock_timeout 5s;

            # 캐시 사용 조건
            proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
            proxy_cache_background_update on;

            # 캐시 상태 헤더 (디버깅용)
            add_header X-Cache-Status $upstream_cache_status;

            # 캐시 바이패스 조건
            proxy_cache_bypass $cookie_nocache $arg_nocache;
            proxy_no_cache $cookie_nocache $arg_nocache;
        }
    }
}
캐시 상태 확인: X-Cache-Status 헤더 값으로 캐시 동작을 확인할 수 있습니다.
HIT - 캐시에서 응답
MISS - 캐시에 없어서 백엔드 호출
EXPIRED - 캐시 만료
BYPASS - 캐시 바이패스 조건
UPDATING - 캐시 갱신 중 (stale 응답)

8. 버퍼링과 타임아웃 튜닝

8.1 버퍼링 이해

Nginx는 기본적으로 백엔드 응답을 버퍼링합니다. 백엔드의 응답을 메모리(또는 디스크)에 모은 후 클라이언트에 전달하는 방식입니다. 이는 느린 클라이언트로 인해 백엔드 연결이 오래 점유되는 것을 방지하는 중요한 역할을 합니다.

location / {
    proxy_pass http://backend;

    # 응답 버퍼링 설정
    proxy_buffering on;              # 기본값
    proxy_buffer_size 4k;            # 응답 헤더용 버퍼
    proxy_buffers 8 4k;              # 응답 바디용 버퍼 (개수 x 크기)
    proxy_busy_buffers_size 8k;      # 클라이언트로 전송 중인 버퍼
    proxy_max_temp_file_size 1024m;  # 임시 파일 최대 크기
    proxy_temp_file_write_size 8k;

    # 요청 버퍼링
    proxy_request_buffering on;      # 기본값
    client_body_buffer_size 16k;
    client_max_body_size 10m;
}

8.2 스트리밍 응답 (버퍼링 비활성화)

# Server-Sent Events (SSE), 스트리밍 API, 대용량 파일 다운로드
location /stream/ {
    proxy_pass http://backend;

    # 버퍼링 비활성화
    proxy_buffering off;
    proxy_request_buffering off;

    # 청크 전송 지원
    proxy_http_version 1.1;
    proxy_set_header Connection "";

    # 즉시 전달
    proxy_cache off;

    # 긴 타임아웃
    proxy_read_timeout 24h;
}

8.3 타임아웃 설정

location / {
    proxy_pass http://backend;

    # 백엔드 연결 타임아웃 (TCP 핸드셰이크)
    proxy_connect_timeout 60s;

    # 백엔드에 요청 전송 타임아웃
    proxy_send_timeout 60s;

    # 백엔드 응답 대기 타임아웃
    proxy_read_timeout 60s;
}

9. 보안 헤더와 클라이언트 보호

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

    # 보안 헤더 (모든 location에 적용)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Content-Security-Policy "default-src 'self'" always;
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

    location / {
        proxy_pass http://backend;
        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_hide_header X-Powered-By;
        proxy_hide_header X-AspNet-Version;
        proxy_hide_header Server;
    }
}

9.1 Rate Limiting과 커넥션 제한

# http 블록에서 zone 정의
http {
    # IP당 초당 요청 수 제한
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
    limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;

    # IP당 동시 연결 수 제한
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    server {
        location / {
            limit_req zone=general burst=20 nodelay;
            limit_conn conn_limit 10;
            proxy_pass http://backend;
        }

        location /login {
            limit_req zone=login burst=3 nodelay;
            proxy_pass http://auth_backend;
        }

        location /api/ {
            limit_req zone=api burst=50 nodelay;
            proxy_pass http://api_backend;
        }
    }
}

10. 실전 문제 해결

10.1 504 Gateway Timeout

원인: 백엔드 응답이 proxy_read_timeout(기본 60초)을 초과
해결:

location / {
    proxy_pass http://backend;
    proxy_connect_timeout 75s;
    proxy_send_timeout 300s;
    proxy_read_timeout 300s;   # 필요에 따라 증가
}

10.2 502 Bad Gateway

원인: 백엔드 서버가 다운되었거나 접근 불가
해결:

# 백엔드 프로세스 확인
ps aux | grep node    # 또는 python, java 등

# 포트 확인
ss -tlnp | grep 3000

# Nginx 에러 로그 확인
tail -f /var/log/nginx/error.log | grep upstream

# 백엔드 서버 직접 호출 테스트
curl -v http://127.0.0.1:3000/

10.3 413 Request Entity Too Large

원인: 업로드 파일 크기가 client_max_body_size(기본 1MB)를 초과
해결:

# 전역 설정 (http 블록)
client_max_body_size 100M;

# 또는 특정 location에만
location /upload/ {
    client_max_body_size 500M;
    proxy_pass http://upload_backend;
}

10.4 백엔드 앱이 HTTP로 리다이렉트

증상: HTTPS로 접속했는데 백엔드 리다이렉트 후 HTTP로 떨어짐
원인: 백엔드가 원본 스키마를 모름
해결:

location / {
    proxy_pass http://backend;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;

    # 또는 Nginx 레벨에서 리다이렉트 재작성
    proxy_redirect http:// https://;
}
프레임워크별 설정:
Express: app.set('trust proxy', true)
Django: SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Flask: ProxyFix(app.wsgi_app, x_proto=1)
Spring Boot: server.forward-headers-strategy=native

10.5 CORS 프리플라이트 이슈

location /api/ {
    # CORS 프리플라이트 요청 처리
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '$http_origin' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Accept' always;
        add_header 'Access-Control-Max-Age' 1728000 always;
        add_header 'Content-Type' 'text/plain; charset=utf-8' always;
        add_header 'Content-Length' 0 always;
        return 204;
    }

    # 실제 요청
    add_header 'Access-Control-Allow-Origin' '$http_origin' always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;

    proxy_pass http://api_backend;
    proxy_set_header Host $host;
}

11. 리버스 프록시 운영 체크리스트

  • 프록시 헤더 전달: Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto 설정
  • 백엔드 프레임워크 설정: trust proxy 관련 설정으로 원본 IP/프로토콜 인식
  • 타임아웃 적절히 설정: 서비스 특성에 맞게 읽기/연결/전송 타임아웃
  • 업로드 크기 제한: client_max_body_size 적절한 값
  • WebSocket 지원: 필요시 Upgrade 헤더 설정
  • 로드밸런싱 장애 대응: max_fails, fail_timeout, backup 서버
  • 보안 헤더: HSTS, X-Frame-Options 등 적용
  • Rate Limiting: 민감한 경로(로그인, API)에 제한 적용
  • 로그 분리: 프록시 로그와 에러 로그를 서비스별로 분리
  • 모니터링: upstream_response_time 로그 수집
  • 백엔드 헬스체크: 주기적인 헬스체크 스크립트
  • 설정 버전 관리: Git으로 설정 파일 관리

결론: 리버스 프록시는 현대 웹 인프라의 기본기

Nginx 리버스 프록시는 단순한 "요청 전달" 기능을 넘어, 현대 웹 인프라의 핵심 컴포넌트입니다. SSL 종료, 로드밸런싱, 캐싱, 보안, 모니터링 등 운영에 필요한 거의 모든 기능을 한 곳에 집중시킬 수 있으며, 백엔드 애플리케이션은 비즈니스 로직에만 집중할 수 있게 해줍니다.

이 가이드에서 다룬 핵심 포인트:

  • proxy_set_header는 필수 - Host, X-Real-IP, X-Forwarded-* 헤더를 반드시 설정하세요.
  • proxy_pass의 슬래시 - 경로 동작이 완전히 달라지므로 정확히 이해해야 합니다.
  • 백엔드별 특성 고려 - Node.js는 keepalive, Python은 Unix 소켓, Java는 큰 버퍼 등 각각의 특성에 맞는 설정이 필요합니다.
  • WebSocket은 Upgrade 헤더 - 일반 HTTP 프록시 설정으로는 동작하지 않습니다.
  • 타임아웃을 신중하게 - 너무 짧으면 정상 요청 실패, 너무 길면 리소스 낭비.
  • 캐싱으로 백엔드 부담 경감 - GET 요청의 대부분은 캐싱 대상이 될 수 있습니다.
  • 보안을 함께 고려 - 프록시는 보안 경계이므로 Rate Limiting과 보안 헤더를 반드시 설정하세요.

이 가이드의 설정 예제를 자신의 서비스 환경에 맞게 조정하여 적용해 보세요. 리버스 프록시 설정은 실전 경험이 쌓일수록 더욱 정교해지며, 이를 통해 안정적이고 확장 가능한 웹 인프라를 구축할 수 있습니다.