[TIPS] Nginx 리버스 프록시 완벽 가이드: WebSocket 1006 비정상 종료·대용량 파일 413 Request Entity Too Large 및 버퍼 타임아웃 해결

Nginx 리버스 프록시 WebSocket 및 대용량 파일 업로드 해결 가이드

[TIPS] Nginx 리버스 프록시 완벽 가이드: WebSocket 1006 비정상 종료·대용량 파일 413 Request Entity Too Large 및 버퍼 타임아웃 해결

핵심 요약: Nginx를 API 게이트웨이나 리버스 프록시로 도입할 때 가장 흔히 겪는 두 가지 치명적 장애는 WebSocket 연결이 60초 만에 끊어지는 1006 Abnormal Closure 에러와 대용량 파일(1MB 초과) 업로드 시 발생하는 413 Request Entity Too Large 에러입니다. 이는 Nginx의 레거시 기본 프록시 모드가 HTTP/1.0 프로토콜을 사용하며 Hop-by-Hop 헤더(Upgrade, Connection)를 자동으로 소멸시키고, 기본 본문 크기 제한이 단 1MB로 설정되어 있기 때문입니다. 본 가이드에서는 Nginx의 프로토콜 중계 및 버퍼링 메커니즘을 상세히 분석하고, 최신 map 블록 기반 동적 Connection 분기, 대용량 파일 스트리밍 최적화, 프로덕션 완성형 nginx.conf 및 Docker Compose 환경, 그리고 실무 4대 치명적 장애 트러블슈팅과 5단계 배포 체크리스트를 완벽하게 정리합니다.

1. 왜 기본 Nginx 설정은 웹소켓과 대용량 파일 업로드에서 터지는가?

Nginx는 높은 동시성 처리를 자랑하는 고성능 비동기 이벤트 기반 리버스 프록시이지만, 기본 제공 설정(Default Directive)은 정적 웹 자원 캐싱과 가벼운 HTTP/1.0 기반 요청 중계에 최적화되어 있습니다. 아무런 튜닝 없이 Spring Boot, Node.js, FastAPI 등의 모던 백엔드 앞에 Nginx를 그대로 배치하면 즉각적인 프로덕션 장애로 이어집니다.

1) WebSocket 연결 실패 및 1006 Abnormal Closure의 근본 원인

WebSocket 핸드셰이크는 클라이언트가 HTTP/1.1 GET 요청에 Upgrade: websocket과 Connection: Upgrade 헤더를 실어 보내면서 시작됩니다. 백엔드가 101 Switching Protocols로 응답하면 이후 TCP 커넥션은 전이중(Full-Duplex) 바이너리/텍스트 스트림 채널로 승격됩니다.

그러나 Nginx 기본 설정은 다음 두 가지 치명적 문제를 발생시킵니다:

text
[클라이언트 브라우저] ────(HTTP/1.1 Upgrade: websocket)────► [기본 Nginx 프록시] ────(HTTP/1.0 Connection: close)────► [백엔드 WAS]
                                                                        │
                                                     (Upgrade 헤더 소멸 + Hop-by-Hop 차단)
                                                                        ▼
[클라이언트 브라우저] ◄───(400 Bad Request 또는 60초 후 1006)───── [핸드셰이크 실패 / 유휴 타임아웃]
  1. HTTP/1.0 다운그레이드 및 Hop-by-Hop 헤더 소멸: Nginx의 proxy_pass 기본 지시어는 업스트림(백엔드) 통신 프로토콜로 HTTP/1.0을 채택합니다. RFC 2616 및 RFC 7230에 정의된 Hop-by-Hop 헤더(Connection, Upgrade, Keep-Alive, Transfer-Encoding 등)는 단일 전송 링크에서만 유효하므로, 프록시가 명시적으로 포워딩하지 않으면 자동으로 소멸(Drop)됩니다. 따라서 백엔드는 일반 HTTP/1.0 GET 요청으로 수신하게 되어 400 Bad Request를 응답하거나 연결을 즉시 종료합니다.
  2. 60초 유휴 타임아웃(proxy_read_timeout 60s): 핸드셰이크가 성공하더라도 Nginx의 기본 proxy_read_timeout과 proxy_send_timeout은 60초입니다. WebSocket 커넥션에서 클라이언트나 서버가 60초 동안 아무런 프레임(Ping/Pong 또는 데이터)을 전송하지 않으면 Nginx가 TCP 소켓을 일방적으로 RST/FIN 종료합니다. 이로 인해 브라우저 JavaScript WebSocket API에는 원인 불명의 WebSocket connection to 'wss://...' failed: Error during WebSocket handshake: net::ERR_CONNECTION_RESET 또는 브라우저 닫힘 코드 1006 (Abnormal Closure)이 기록됩니다.

2) 대용량 파일 업로드 시 413 Request Entity Too Large 및 디스크 I/O 병목

Nginx의 client_max_body_size 기본값은 1m (1 Megabyte)입니다. 사용자가 1.5MB의 이미지나 수백 MB의 동영상/압축 파일을 multipart/form-data로 전송하면 Nginx는 본문을 파싱하는 도중 즉시 처리를 중단하고 백엔드 애플리케이션으로 트래픽을 전달하지도 않은 채 413 Request Entity Too Large HTTP 상태 코드를 반환합니다.

또한 기본적으로 Nginx는 클라이언트 요청 본문을 메모리 버퍼(client_body_buffer_size)에 담고, 이를 초과하면 임시 디스크 경로(client_body_temp_path)에 전체 페이로드를 기록한 뒤 백엔드로 전달하는 '요청 버퍼링(Request Buffering)'을 수행합니다. 이로 인해 다음과 같은 2차 장애가 발생합니다:

  • 디스크 I/O 스파이크: 기가바이트(GB) 단위 파일 업로드 시 Nginx 컨테이너의 디스크 쓰기 I/O가 포화 상태에 이릅니다.
  • 업로드 지연 시간 2배 증가: 클라이언트 ➔ Nginx 디스크 저장 완료 후 ➔ 백엔드로 재전송하므로 네트워크 대역폭과 레이턴시가 낭비됩니다.
  • 권한 오류 (13: Permission denied): client_body_temp_path에 대한 Nginx 워커 프로세스(nginx 유저)의 쓰기 권한이 누락되면 파일 업로드가 영구 실패합니다.

2. 최신 공식 표준 해법: 동적 Connection 분기 및 스트리밍 파이프라인

모던 Nginx 아키텍처에서는 단순 무식하게 proxy_set_header Connection "upgrade";를 하드코딩하지 않습니다. 일반 REST API 호출 시 불필요하게 Connection: upgrade를 전송하면 백엔드 웹 서버(Tomcat, Netty, Envoy)에서 HTTP 파싱 예외가 발생할 수 있기 때문입니다.

1) map 지시어를 활용한 WebSocket Upgrade 동적 분기

Nginx 공식 모범 사례(Best Practice)는 http 컨텍스트에서 $http_upgrade 변수를 평가하여, 실제 WebSocket 업그레이드 요청일 때만 Connection "upgrade"를 설정하고, 일반 HTTP 요청일 때는 Connection "close"(또는 keep-alive)로 처리하는 동적 매핑입니다.

nginx
# /etc/nginx/nginx.conf (http 컨텍스트 내부)
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

이 매핑을 적용하면:

  • 클라이언트가 Upgrade: websocket을 보내면 $connection_upgrade 변수는 "upgrade"가 됩니다.
  • 클라이언트가 Upgrade 헤더를 보내지 않는 일반 REST/GraphQL 요청이면 빈 문자열('')과 매칭되어 $connection_upgrade 변수는 "close"가 됩니다.

2) 버퍼링 바이패스(Buffering Bypass) 및 대용량 청크 스트리밍

기가바이트 단위 파일 업로드나 실시간 SSE(Server-Sent Events), WebSocket 트래픽에서는 Nginx가 중간에서 페이로드를 가로채 디스크에 쓰지 않도록 버퍼링을 선택적으로 비활성화해야 합니다:

  • proxy_request_buffering off;: 클라이언트가 전송하는 HTTP Body 청크를 Nginx 디스크에 임시 저장하지 않고 백엔드로 즉시 파이프라이닝(Direct Streaming)합니다. 이를 통해 메모리/디스크 I/O를 절감하고 TTFB(Time To First Byte)를 획기적으로 단축합니다.
  • proxy_buffering off;: 백엔드의 응답을 Nginx가 버퍼링하지 않고 클라이언트로 즉시 스트리밍합니다. SSE나 실시간 채팅 푸시 응답에 필수적입니다.

3. 트래픽 유형별 Nginx 파라미터 호환성 매트릭스

실무 서비스는 일반 REST API, WebSocket 실시간 통신, 대용량 파일 업로드/다운로드 스트리밍, gRPC 마이크로서비스 등 다양한 트래픽이 혼재합니다. 각 워크로드별 핵심 지시어의 권장 설정을 비교합니다.

지시어 (Directive) 기본값 (Default) 일반 REST API WebSocket 실시간 통신 대용량 파일 스트리밍 (Upload) gRPC 마이크로서비스
proxy_http_version 1.0 1.1 (Keep-Alive 권장) 1.1 (필수) 1.1 grpc_pass (HTTP/2 필수)
Upgrade 헤더 전달 안 됨 (Drop) $http_upgrade $http_upgrade 생략 HTTP/2 프레임 내부 캡슐화
Connection 헤더 close $connection_upgrade $connection_upgrade keep-alive N/A
client_max_body_size 1m 10m ~ 20m N/A (프레임 단위 통신) 500m ~ 2g (비즈니스 요구) 4m ~ 16m
proxy_read_timeout 60s 30s ~ 60s 3600s ~ 86400s (하트비트 연계) 300s ~ 600s 60s ~ 300s
proxy_send_timeout 60s 30s ~ 60s 3600s ~ 86400s 300s ~ 600s 60s ~ 300s
proxy_request_buffering on on off off (스트리밍 업로드) N/A
proxy_buffering on on off (실시간 프레임 즉시 전송) off (대용량 스트리밍) N/A
client_body_buffer_size 8k / 16k 64k ~ 128k N/A 256k ~ 512k N/A

4. 실무 프로덕션 설정 코드

운영 환경에서 모듈화와 재사용성을 극대화하기 위해 nginx.conf, 공통 프록시 파라미터 파일(proxy_params), 그리고 Docker Compose 명세서를 완전히 분리하여 구성합니다.

1) 모듈화된 프록시 헤더 설정: /etc/nginx/proxy_params

모든 location 블록에 중복 선언되는 공통 헤더를 단일 파일로 관리합니다.

nginx
# /etc/nginx/proxy_params
# 1. 클라이언트 식별 헤더 정밀 포워딩
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 X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;

# 2. HTTP/1.1 강제 및 WebSocket Upgrade 동적 헤더 연결
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

# 3. 브로큰 파이프 에러 억제
proxy_ignore_client_abort off;

2) 완성형 메인 설정 파일: /etc/nginx/nginx.conf

REST API, 대용량 파일 업로드 엔드포인트, WebSocket 채널이 완벽하게 분리 격리된 프로덕션 Nginx 설정입니다.

nginx
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /var/run/nginx.pid;

events {
    worker_connections 8192;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # 고성능 정적 파일 전송 최적화
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # WebSocket Upgrade 동적 매핑 테이블
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    # 로그 포맷 (요청 처리 시간 및 업스트림 응답 시간 포함)
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for" '
                    'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"';

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;

    # 전역 클라이언트 요청 버퍼 및 타임아웃
    client_body_buffer_size 256k;
    client_header_buffer_size 4k;
    large_client_header_buffers 4 16k;
    client_body_temp_path /var/cache/nginx/client_temp 1 2;

    keepalive_timeout 65;
    keepalive_requests 1000;

    # 업스트림 백엔드 클러스터 정의 (Keepalive 커넥션 풀 유지)
    upstream backend_api {
        server backend:8080 max_fails=3 fail_timeout=10s;
        keepalive 32;
    }

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

        # 일반 REST API 엔드포인트 (기본 20MB 제한)
        client_max_body_size 20m;

        # -------------------------------------------------------------
        # 1. 일반 비즈니스 API 라우팅
        # -------------------------------------------------------------
        location /api/ {
            include /etc/nginx/proxy_params;
            proxy_pass http://backend_api;

            proxy_connect_timeout 5s;
            proxy_read_timeout 30s;
            proxy_send_timeout 30s;

            # 백엔드 Keepalive 세션 유지용 Connection 초기화
            proxy_set_header Connection "";
        }

        # -------------------------------------------------------------
        # 2. WebSocket 전용 실시간 통신 라우팅 (/ws, /socket.io 등)
        # -------------------------------------------------------------
        location /ws/ {
            include /etc/nginx/proxy_params;
            proxy_pass http://backend_api;

            # 실시간 전송을 위한 버퍼링 완전 차단
            proxy_buffering off;
            proxy_request_buffering off;

            # 유휴 세션 끊김 방지를 위한 타임아웃 확장 (1시간)
            # 애플리케이션 레벨의 Ping/Pong(하트비트) 주기보다 길게 설정 필수
            proxy_read_timeout 3600s;
            proxy_send_timeout 3600s;
            proxy_connect_timeout 10s;
        }

        # -------------------------------------------------------------
        # 3. 대용량 파일 업로드 라우팅 (/api/v1/files/upload)
        # -------------------------------------------------------------
        location /api/v1/files/upload {
            include /etc/nginx/proxy_params;
            proxy_pass http://backend_api;

            # 파일 업로드 허용 용량 최대 1GB 확장
            client_max_body_size 1024m;

            # [핵심] Nginx 디스크 쓰기 방지 및 백엔드 직접 스트리밍
            proxy_request_buffering off;
            proxy_buffering off;

            # 대용량 전송을 위한 I/O 타임아웃 10분 설정
            proxy_connect_timeout 15s;
            proxy_read_timeout 600s;
            proxy_send_timeout 600s;
            client_body_timeout 600s;
        }
    }
}

3) 프로덕션 Docker Compose 환경 명세: docker-compose.yml

컨테이너 환경에서 client_body_temp_path 권한 오류와 볼륨 유실을 방지하고 Nginx-WAS 네트워크를 격리합니다.

yaml
version: '3.8'

services:
  nginx-proxy:
    image: nginx:1.27-alpine
    container_name: production-nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/proxy_params:/etc/nginx/proxy_params:ro
      # 임시 버퍼 디렉토리 볼륨 마운트 및 퍼미션 보장
      - nginx-client-temp:/var/cache/nginx/client_temp
      - ./logs/nginx:/var/log/nginx
    environment:
      - TZ=Asia/Seoul
    networks:
      - internal-net
    depends_on:
      backend:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G

  backend:
    image: my-backend-api:1.0.0
    container_name: production-backend
    restart: unless-stopped
    expose:
      - "8080"
    environment:
      - TZ=Asia/Seoul
      # Spring Boot 대용량 업로드 멀티파트 동기화 필수
      - SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE=1024MB
      - SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE=1024MB
    networks:
      - internal-net
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 10s
      timeout: 3s
      retries: 3

volumes:
  nginx-client-temp:

networks:
  internal-net:
    driver: bridge

5. 자주 겪는 4대 치명적 에러 트러블슈팅

실무에서 Nginx 프록시를 설정한 뒤 가장 빈번하게 발생하는 4대 에러의 원형 로그, 내부 메커니즘, 그리고 즉각적인 해결 방안입니다.

Error 1. 413 Request Entity Too Large

  • 발생 증상 및 콘솔/브라우저 로그:

```text [Browser DevTools Network] POST https://api.example.com/api/v1/files/upload 413 (Request Entity Too Large)

[Nginx error.log] 2026/10/27 14:22:10 [error] 31#31: *120 client intended to send too large body: 52428800 bytes, client: 192.168.1.50, server: api.example.com, request: "POST /api/v1/files/upload HTTP/1.1", host: "api.example.com" ```

  • 원인 분석:
  1. Nginx의 기본 client_max_body_size가 1m로 설정되어 있어 전송된 50MB 페이로드를 거부했습니다. 2. Nginx를 통과하더라도 백엔드 프레임워크(Spring Boot 기본 1MB/10MB, Express multer, FastAPI 등) 자체 제한이 동기화되지 않으면 백엔드에서 2차 413 Payload Too Large 또는 SizeLimitExceededException이 발생합니다.
  • 해결 방법:

Nginx와 백엔드 프레임워크의 업로드 제한을 동일한 크기(예: 100MB)로 동기화합니다.

``nginx # /etc/nginx/nginx.conf location /api/v1/files/upload { client_max_body_size 100m; proxy_pass http://backend_api; } ``

```properties # Spring Boot application.properties (동시 설정 필수) spring.servlet.multipart.max-file-size=100MB spring.servlet.multipart.max-request-size=100MB

# Spring WebFlux / Netty 환경 spring.codec.max-in-memory-size=100MB ```


Error 2. WebSocket 1006 Abnormal Closure 및 400 Bad Request 핸드셰이크 실패

  • 발생 증상 및 클라이언트 로그:

``text [Browser JavaScript Console] WebSocket connection to 'wss://api.example.com/ws/chat' failed: Error during WebSocket handshake: Unexpected response code: 400 WebSocket connection to 'wss://api.example.com/ws/chat' was closed abnormally (code 1006) ``

  • 원인 분석:
  1. proxy_http_version 1.1;이 누락되어 Nginx가 백엔드로 HTTP/1.0 요청을 보냈습니다. 2. proxy_set_header Upgrade $http_upgrade; 및 proxy_set_header Connection $connection_upgrade;가 설정되지 않아 중간 프록시 홉(Hop)에서 WebSocket 승격 헤더가 소실되었습니다. 백엔드 WAS는 일반 HTTP 요청에 프로토콜 스위칭이 요청된 것으로 인식하고 400 Bad Request를 리턴합니다.
  • 해결 방법:

해당 WebSocket location 블록에 HTTP/1.1 강제 및 명시적 업그레이드 헤더를 반드시 선언합니다.

```nginx location /ws/ { proxy_pass http://backend_api;

# [필수 3대 지시어] proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";

# 웹소켓 페이로드 즉시 전달 proxy_buffering off; } ```


Error 3. 대용량 파일 업로드 시 client_body_temp_path (13: Permission denied)

  • 발생 증상 및 Nginx 로그:

``text [Nginx error.log] 2026/10/27 15:10:02 [crit] 28#28: *451 open() "/var/cache/nginx/client_temp/0000000001" failed (13: Permission denied), client: 10.0.0.12, server: api.example.com, request: "POST /api/upload HTTP/1.1", host: "api.example.com" ``

  • 원인 분석:

클라이언트가 전송한 요청 본문 크기가 client_body_buffer_size(기본 8KB~16KB)를 초과하여 Nginx가 디스크 임시 디렉토리(/var/cache/nginx/client_temp)에 파일을 쓰려고 시도했습니다. 하지만 워커 프로세스를 구동하는 nginx 유저에게 해당 경로의 쓰기 권한(w)이 없어서 OS 레벨에서 EACCES (13: Permission denied)가 발생한 것입니다.

  • 해결 방법:
  1. 임시 경로 소유권 수정 (호스트/도커 환경): ``bash # Nginx 실행 유저 확인 후 소유권 및 권한 정합성 부여 chown -R nginx:nginx /var/cache/nginx/client_temp chmod 700 /var/cache/nginx/client_temp ` 2. **근본적 회피책 (버퍼링 오프):** Nginx가 디스크에 임시 파일을 쓰지 않고 백엔드로 즉시 스트리밍하도록 설정하면 본 에러를 원천 차단할 수 있습니다. `nginx location /api/upload { proxy_pass http://backend_api; proxy_request_buffering off; # 디스크 파일 생성 자체를 생략 } ``

Error 4. 유휴 커넥션 60초 강제 종료 (proxy_read_timeout 만료)

  • 발생 증상:

WebSocket 연결은 정상적으로 맺어지지만, 사용자가 아무런 메시지를 입력하지 않고 정확히 60초가 경과하면 커넥션이 자동으로 닫히며 클라이언트 소켓 이벤트가 발생합니다. ``text [Browser Console] [WebSocket] Disconnected from wss://api.example.com/ws/live. Code: 1006, Reason: None ``

  • 원인 분석:

Nginx의 기본 TCP 읽기 타임아웃 지시어인 proxy_read_timeout의 기본값은 60초입니다. 백엔드와 프록시, 클라이언트 사이에 트래픽이 60초 동안 전혀 오가지 않으면 Nginx는 커넥션을 유휴 상태(Idle)로 판단하고 TCP 소켓을 일방적으로 종료합니다.

  • 해결 방법 (2-Way Solutuion):
  1. Nginx 프록시 타임아웃 연장: WebSocket 엔드포인트의 proxy_read_timeout과 proxy_send_timeout을 운영 정책(예: 1시간 또는 24시간)으로 확장합니다. ``nginx location /ws/ { proxy_pass http://backend_api; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } ` 2. **애플리케이션 하트비트(Ping/Pong) 구현 (권장):** 프록시 타임아웃만 무한정 늘리면 좀비 커넥션이 누적되어 Nginx 워커 커넥션 풀(worker_connections)이 고갈됩니다. 백엔드 또는 클라이언트에서 **20초~30초 주기**로 Ping 프레임을 전송하여 소켓이 살아있음을 Nginx에 알려야 합니다. `javascript // 클라이언트 예시: 30초 주기 Ping 전송 const socket = new WebSocket('wss://api.example.com/ws/chat'); const heartbeatInterval = setInterval(() => { if (socket.readyState === WebSocket.OPEN) { socket.send(JSON.stringify({ type: 'PING' })); } }, 30000); ``

6. 스트레스 테스트 검증 및 5단계 배포 체크리스트

설정을 프로덕션에 배포하기 전, 터미널 도구를 활용하여 WebSocket 연결 유지와 대용량 파일 업로드를 신속히 검증하는 방법과 운영 점검 체크리스트입니다.

1) 터미널 연동 검증 (wscat & curl)

① WebSocket 핸드셰이크 및 타임아웃 검증 (wscat)

CLI 도구인 wscat을 사용하여 60초 이상 커넥션이 유지되는지 테스트합니다.

bash
# wscat 설치 (Node.js 환경)
npm install -g wscat

# Nginx 프록시 WebSocket 연결 테스트
wscat -c ws://api.example.com/ws/chat

# 연결 성공 시 프롬프트 진입 확인:
# Connected (press CTRL+C to quit)
# > {"type":"HELLO"}
# < {"status":"ACK"}
# (60초 이상 유휴 상태 유지 후 끊김 여부 관찰)

② 대용량 더미 파일 업로드 검증 (curl)

100MB 크기의 더미 바이너리 파일을 생성한 후 curl을 통해 HTTP 200/201 응답이 정상 반환되는지 확인합니다.

bash
# 100MB 더미 파일 생성 (Linux / macOS)
dd if=/dev/urandom of=dummy_100mb.dat bs=1M count=100

# curl 업로드 및 HTTP 응답 코드 확인
curl -v -X POST \
     -H "Content-Type: multipart/form-data" \
     -F "file=@dummy_100mb.dat" \
     http://api.example.com/api/v1/files/upload

# 기대 결과:
# < HTTP/1.1 200 OK (또는 201 Created)
# < 413 Request Entity Too Large가 발생하지 않아야 함

2) 프로덕션 5단계 최종 배포 체크리스트

배포 파이프라인을 가동하거나 Nginx 컨테이너를 재시작하기 전 다음 5개 핵심 점검 사항을 반드시 확인하십시오.

  • 1. nginx -t 문법 무결성 검증: 설정 파일 수정 후 즉각적인 재로드 전에 반드시 nginx -t를 실행하여 syntax is ok 및 test is successful 결과를 확인했는가?
  • 2. WebSocket 3대 필수 헤더 적용: WebSocket location 블록에 proxy_http_version 1.1;, proxy_set_header Upgrade $http_upgrade;, proxy_set_header Connection $connection_upgrade;가 모두 포함되었는가?
  • 3. client_max_body_size 양방향 정합성: Nginx의 client_max_body_size뿐만 아니라 백엔드 WAS(Spring Boot multipart.max-file-size, Express, Nginx Ingress 등)의 최대 페이로드 제한이 일치하도록 동기화되었는가?
  • 4. 대용량 업로드 디스크 병목 방지: 기가바이트 단위 파일 업로드 엔드포인트에 proxy_request_buffering off;가 적용되어 불필요한 디스크 I/O와 Permission denied (13) 오류를 원천 차단했는가?
  • 5. 하트비트 vs proxy_read_timeout 마진 확보: WebSocket 유휴 종료를 방지하기 위해 클라이언트/서버의 Ping/Pong 주기(예: 30초)보다 Nginx의 proxy_read_timeout(최소 60초 이상, 권장 300초~3600초)이 넉넉하게 설정되었는가?

댓글 쓰기

다음 이전