Nginx

HTTP1.0/HTTP1.1을 기반으로 하는 다이나믹 리버스 프록시

_Jin_ 2026. 7. 8.

 

이번에는 Nginx의 핵심 기능 중 하나인 리버스 프록시(Reverse Proxy) 에 대해 파악하고 정리해보겠다.

 

리버스 프록시란?

클라이언트의 요청을 받아 내부의 백엔드 서버로 전달하고, 백엔드의 응답을 다시 클라이언트에게 돌려주는 중간 서버다.

즉, 클라이언트와 백엔드 사이에서 요청을 중개하는 역할을 수행하는 것이다.

 

이와 같은 구조에서 클라이언트는 뒤에 어떤 서버가 존재하는 지 모르며, 굳이 알 필요도 없게 된다. 

 

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

프록시에는 두 종류가 있으며, 누구를 대신하는가에 따라 구분된다.

 

 

포워드 프록시 — 클라이언트 대신 나가는 대리인

회사에서 보안 정책상 직원이 인터넷에 직접 접근하지 못하고, 회사 프록시 서버를 통해서만 나갈 수 있는 상황을 생각해보자.

직원(클라이언트) → 회사 프록시 → 인터넷(서버들)


서버 입장에서는 요청이 직원에게서 온 건지 프록시에게서 온 건지 알 수 없다. 클라이언트를 숨기는 것이 목적이며, 사내 인터넷 필터링, 익명성, 캐시 등에 활용된다.

 

리버스 프록시 — 서버 대신 받아주는 대리인

이번엔 반대로 식당 앞에 도어맨이 있는 상황을 생각해보자.

손님(클라이언트) → 도어맨(Nginx) → 주방 A (Node.js :3000) → 주방 B (Python :8000) → 주방 C (Java :8080)


손님은 도어맨한테만 말하고, 실제로 어느 주방이 요리하는지 알 수 없다. 서버를 숨기는 것이 목적이며, 클라이언트는 Nginx 주소만 알고 백엔드 서버의 존재를 모른다.

 

 

이와 같은 두 가지 개념에서 Nginx는 기본적으로 리버스 프록시를 가장 많이 사용한다. 

왜 그럴까?

 

리버스 프록시를 사용하는 이유

Nginx를 리버스 프록시로 사용하는 이유는 다양한 이유들이 존재한다.

 

백엔드 서버 보호

운영 중인 서비스의 서버 앞에 nginx가 존재하니 백엔드 서버를 직접 외부에 노출하지 않을 수 있게 된다.

즉, 백엔드 서버의 실제 ip 와 port를 외부에 노출하지 않게 되는 것이다.

 

SSL 처리 일원화 

HTTPS 인증서를 Nginx 한 곳에서만 위임하여 처리하면 된다.

따라서 백엔드는 별도의 SSL 설정 없이도 통신이 가능하며, 사실상 백엔드의 부담을 줄일 수 있는 구조가 된다. 

 

로드밸런싱

여러 백엔드 서버에 요청을 분산해서 트래픽을 나눌 수 있다.

클라이언트 → Nginx → 백엔드 1 (50%)
                                → 백엔드 2 (50%)
 

 

하나의 도메인으로 여러 서비스 운영

경로나 도메인에 따라 다른 백엔드로 라우팅할 수 있다.

example.com/api/ → Node.js 서버
example.com/admin/ → Python 서버
example.com/ → 정적 파일 서빙

 

 

리버스 프록시 설정(실습)

 

리버스 프록시로 Nginx를 사용하기 위한 설정들에 대해서 알아보자.

 

nginx.conf 파일을 만들어 실습해보겠다.

 

 

Proxy_pass 기본설정

proxy_pass는 요청을 어느 백엔드 서버로 전달할지 지정하는 디렉티브이다.

위의 설정을 보면,  /api/로 오는 요청은 http://localhost:3000 경로의 서버로 요청을 보내도록 구성되었다.

 

슬래시(/) 유무에 따른 차이

proxy_pass 끝에 슬래시가 있느냐 없느냐에 따라 전달되는 URI가 달라진다.

# 슬래시 없음
proxy_pass http://localhost:3000;
# /api/users → http://localhost:3000/api/users
# location 경로(/api/)를 포함한 URI 그대로 전달

# 슬래시 있음
proxy_pass http://localhost:3000/;
# /api/users → http://localhost:3000/users
# location 경로(/api/)를 제거하고 나머지만 전달

URI 구조를 그대로 백엔드에 전달하려면 슬래시 없이, location 경로를 제거하고 넘기려면 슬래시를 붙인다.

 

upstream — 로드밸런싱

upstream 블록은 여러 백엔드 서버를 그룹으로 묶어 관리하는 설정이다. proxy_pass에서 upstream 이름을 참조하면 Nginx가 자동으로 로드밸런싱한다.

 

기본 로드밸런싱 방식은 라운드 로빈으로, 요청을 3000, 3001 포트에 번갈아가며 분산한다.

 

클라이언트 IP 전달 문제와 proxy_set_header

 

리버스 프록시를 사용하면 한 가지 문제가 생긴다.

백엔드 서버 입장에서 요청이 클라이언트(1.2.3.4)에서 온 것이 아니라, Nginx(10.0.0.1)에서 온 것으로 보인다.

 

백엔드에서 클라이언트의 실제 IP를 알아야 하는 경우(로그, 인증, 지역 차단 등)에는 Nginx가 원본 정보를 헤더에 담아서 전달해야 한다. 그리고 이를 위해서는 Nginx의 요청 header에 값을 설정하여 적용할 수 있다.

 

예를 들어, 아래와 같은 설정들이 있다.(사실상 헤더 설정값들은 너무 많기에 몇개만 적었다.)

 

각 헤더의 역할은 다음과 같다.

 

헤더 Nginx 변수 역할
Host $host 클라이언트가 요청한 원본 도메인 전달
X-Real-IP $remote_addr 클라이언트의 실제 IP 전달
X-Forwarded-For $proxy_add_x_forwarded_for 거쳐온 모든 IP 목록 전달
X-Forwarded-Proto $scheme 원본 요청 프로토콜(http/https) 전달

 

X-Forwarded-For가 필요한 이유

클라이언트와 Nginx 사이에 중계소(CDN, 로드밸런서 등)가 여러 개 있는 경우, 각 중계소를 거칠 때마다 IP가 헤더에 누적된다.

 

이 경우 X-Forwarded-For 헤더에는 거쳐온 IP가 순서대로 쌓인다.

X-Forwarded-For: 1.2.3.4, 5.6.7.8, 10.0.0.1

백엔드는 이 헤더의 첫 번째 값이 원래 클라이언트의 IP임을 알 수 있다.

 

 

 

 

프록시 버퍼링 (proxy_buffering)

 

리버스 프록시 환경에서 백엔드는 응답을 빠르게 만들어 보내지만, Nginx가 중간에 일부러 천천히 전달하는 설정 또한 가능하다.

그런데, 일반적으로 이와 같은 설정은 레이턴시를 증가시키는 것으로 염려되기에 적용 이유에 공감하기 어려울 수도 있다.

 

그 이유는 사용자 경험을 조금 헤치더라고 백엔드와의 연결을 빠르게 끊어야할 필요가 있는 경우에 해당한다. 

일반적으로 서버의 리소스는 제한적이기에 특정 작업이 백엔드와 프론트 모두에서 처리 가능한 작업이라면, 프론트엔드에 위임하는 것이 적절하다.

 

따라서 Nginx에서 백엔드를 통한 응답 결과를 잠시 버퍼에 저장하고, 이를 클라이언트에 전달한다.

대신 백엔드는 다른 작업 처리가 가능하게 되는 것이다. 

 

 

 

이에 해당하는 설정에는 아래와 같은 것들이 있다.

location /api/ {
     proxy_pass http://backend;

    proxy_buffering on; # 버퍼링 활성화
    proxy_buffer_size 4k; # 응답 헤더 버퍼 크기
    proxy_buffers 8 4k; # 응답 본문 버퍼 (8개 × 4k = 32k)
    proxy_busy_buffers_size 8k; # 클라이언트에게 전송 중인 버퍼 최대 크기
}

 

각 설정의 의미를 정리하면 다음과 같다.

설정 의미
proxy_buffering on 버퍼링 활성화
proxy_buffer_size 4k 응답 헤더를 저장하는 버퍼 크기
proxy_buffers 8 4k 응답 본문을 저장하는 버퍼 개수와 크기
proxy_busy_buffers_size 8k 클라이언트에게 전송 중인 버퍼의 최대 크기

 

그렇다고, 항상 버퍼를 활성화해야 하는 것은 아니다.

버퍼링을 한다는 것은 곧 어쩔 수 없이 실시간성을 포기한다는 것과 같다. 

따라서 실시간 서비스의 경우에는 버퍼링을 사용하는 것이 적절하지 않다.

 

 

HTTP 버전과 keepalive 설정

keepalive 설정은 한 번 맺은 네트워크 연결(TCP/HTTP 세션)을 즉시 끊지 않고 유지해, 연속되는 요청 시 연결 생성(TCP 3-Way Handshake) 비용을 줄여주는 설정이다.

 

한편, 레거시 환경에서 자주 사용되는 HTTP/1.0은 요청마다 연결을 새로 맺고 끊기 때문에, 요청이 많아지면 연결 오버헤드가 누적된다.

따라서 keepalive 설정을 사용하기 위해서는 HTTP/1.1 버젼 이상의 프로토콜 사용이 필요하다.

 

이를 위한 nginx 설정은 아래와 같이 작성될 수 있다. 

upstream backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;

    keepalive 32; # 워커 당 유지할 최대 연결 수가 32개
}

location /api/ {
    proxy_pass http://backend;

    proxy_http_version 1.1; # HTTP/1.1 사용
    proxy_set_header Connection ""; # Connection 헤더 제거 (keepalive 유지)
}

 

keepalive :32;

유지할 연결 수로 워커당 최대 32개의 연결을 유지하는 설정값이다.

 

proxy_http_version 1.1

HTTP/1.1 버젼을 사용하도록 만드는 설정값이다.

 

proxy_set_header Connection ""

클라이언트가 보낸 커넥션 헤더값을 지워서 설정한 1.1 버젼이 적용될 수 있도록 한다.

 

여기서 nginx를 도입하여 keepalive 연결을 설정하는 경우, 

' 클라이언트 - nginx / nginx - 백엔드 ' 간의 관계를 분리해서 고려해주어야 한다.

예를 들어, 클라이언트와 연결을 끊고 싶다고하여, Nginx - 백엔드 사이의 연결까지 끊어줄 필요가 있을까?

nginx와 백엔드 사이의 연결은 재사용하면 성능을 높이는 데 좋지 때문이다.

 

댓글