리버스 프록시
Reverse Proxy
클라이언트 요청을 백엔드 서버에 중계하는 서버입니다. Nginx, HAProxy 등으로 구현하며, 로드밸런싱, SSL 종료, 캐싱, 보안 기능을 제공합니다.
Reverse Proxy
클라이언트 요청을 백엔드 서버에 중계하는 서버입니다. Nginx, HAProxy 등으로 구현하며, 로드밸런싱, SSL 종료, 캐싱, 보안 기능을 제공합니다.
리버스 프록시(Reverse Proxy)는 클라이언트와 백엔드 서버 사이에 위치하여 클라이언트 요청을 대신 받아 적절한 서버로 전달하는 중간 서버입니다. 클라이언트는 리버스 프록시와만 통신하며, 실제 백엔드 서버의 존재를 알지 못합니다. 이를 통해 서버 인프라의 보안, 확장성, 성능을 크게 향상시킬 수 있습니다.
리버스 프록시의 핵심 역할은 로드밸런싱, SSL/TLS 종료, 캐싱, 압축, 보안(WAF), API 게이트웨이입니다. 대표적인 리버스 프록시 소프트웨어로는 Nginx(전 세계 웹서버 점유율 1위), HAProxy(고성능 로드밸런서), Apache httpd, Traefik(클라우드 네이티브), Envoy(서비스 메시) 등이 있습니다.
Forward Proxy(정방향 프록시)와 헷갈리기 쉽습니다. Forward Proxy는 클라이언트 측에서 외부 인터넷 접근을 대행하고(예: 기업 방화벽, VPN), Reverse Proxy는 서버 측에서 클라이언트 요청을 내부 서버로 중계합니다. 쉽게 말해, Forward Proxy는 '클라이언트를 숨기고', Reverse Proxy는 '서버를 숨깁니다'.
현대 마이크로서비스 아키텍처에서 리버스 프록시는 필수입니다. Netflix는 Zuul로 하루 수십억 요청을 처리하고, Cloudflare는 전 세계 트래픽의 25%를 리버스 프록시로 처리합니다. Kubernetes의 Ingress Controller도 본질적으로 리버스 프록시입니다.
| 구분 | Forward Proxy | Reverse Proxy |
|---|---|---|
| 위치 | 클라이언트 앞 (아웃바운드) | 서버 앞 (인바운드) |
| 숨기는 대상 | 클라이언트 IP | 서버 IP/구조 |
| 주요 용도 | 익명성, 접근제어, 우회 | 로드밸런싱, SSL, 캐싱, 보안 |
| 예시 | Squid, 기업 프록시, VPN | Nginx, HAProxy, Cloudflare |
| 설정 위치 | 클라이언트/브라우저 | 서버 인프라 |
┌─────────────┐ ┌─────────────┐
│ Forward │ 사용자 → 인터넷 접근 │ Reverse │
│ Proxy │ │ Proxy │
└─────────────┘ └─────────────┘
사용자 → [Forward Proxy] → 인터넷 인터넷 → [Reverse Proxy] → 서버들
(클라이언트 숨김) (서버 숨김)
# /etc/nginx/nginx.conf - 리버스 프록시 기본 설정
# 업스트림 서버 그룹 (로드밸런싱)
upstream backend_servers {
# 로드밸런싱 알고리즘: round-robin(기본), least_conn, ip_hash
least_conn;
server 10.0.0.1:8080 weight=3; # 가중치 높음
server 10.0.0.2:8080 weight=2;
server 10.0.0.3:8080 backup; # 백업 서버
# 헬스체크 (유료 버전)
keepalive 32; # 연결 재사용
}
server {
listen 80;
listen 443 ssl http2;
server_name api.example.com;
# SSL/TLS 종료 (백엔드는 HTTP로 통신)
ssl_certificate /etc/ssl/certs/example.crt;
ssl_certificate_key /etc/ssl/private/example.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
# 캐싱 설정
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g;
location / {
proxy_pass http://backend_servers;
# 필수 헤더 전달
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_cache api_cache;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
# 타임아웃 설정
proxy_connect_timeout 10s;
proxy_read_timeout 30s;
# 보안 헤더
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
}
# WebSocket 프록시
location /ws/ {
proxy_pass http://backend_servers;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
# /etc/haproxy/haproxy.cfg - 고성능 로드밸런서
global
maxconn 50000
log /dev/log local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
option httplog
option dontlognull
option forwardfor
# 프론트엔드 (리버스 프록시 진입점)
frontend http_front
bind *:80
bind *:443 ssl crt /etc/ssl/certs/example.pem
# HTTP → HTTPS 리다이렉트
http-request redirect scheme https unless { ssl_fc }
# ACL 기반 라우팅
acl is_api path_beg /api/
acl is_static path_beg /static/
use_backend api_servers if is_api
use_backend static_servers if is_static
default_backend web_servers
# 백엔드 서버 그룹
backend api_servers
balance roundrobin
option httpchk GET /health
http-check expect status 200
server api1 10.0.0.1:8080 check weight 100
server api2 10.0.0.2:8080 check weight 100
server api3 10.0.0.3:8080 check weight 50 backup
backend web_servers
balance leastconn
cookie SERVERID insert indirect nocache
server web1 10.0.1.1:3000 check cookie web1
server web2 10.0.1.2:3000 check cookie web2
backend static_servers
balance uri
server cdn1 10.0.2.1:80 check
server cdn2 10.0.2.2:80 check
# 통계 대시보드
listen stats
bind *:8404
stats enable
stats uri /stats
stats auth admin:password123
// Node.js 리버스 프록시 (http-proxy-middleware)
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// 로깅 미들웨어
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
});
// API 서버로 프록시 (로드밸런싱)
const apiServers = ['http://10.0.0.1:8080', 'http://10.0.0.2:8080'];
let currentServer = 0;
app.use('/api', createProxyMiddleware({
router: (req) => {
// Round-robin 로드밸런싱
const target = apiServers[currentServer];
currentServer = (currentServer + 1) % apiServers.length;
return target;
},
changeOrigin: true,
pathRewrite: { '^/api': '' },
// 요청/응답 수정
onProxyReq: (proxyReq, req, res) => {
proxyReq.setHeader('X-Real-IP', req.ip);
proxyReq.setHeader('X-Forwarded-For', req.ip);
},
onProxyRes: (proxyRes, req, res) => {
// 보안 헤더 추가
proxyRes.headers['X-Frame-Options'] = 'SAMEORIGIN';
proxyRes.headers['X-Content-Type-Options'] = 'nosniff';
},
onError: (err, req, res) => {
console.error('Proxy error:', err);
res.status(502).json({ error: 'Bad Gateway' });
}
}));
// WebSocket 프록시
app.use('/ws', createProxyMiddleware({
target: 'ws://10.0.0.1:8080',
ws: true,
changeOrigin: true
}));
// 정적 파일 서버로 프록시 (캐싱)
app.use('/static', createProxyMiddleware({
target: 'http://cdn.example.com',
changeOrigin: true,
selfHandleResponse: false
}));
app.listen(80, () => {
console.log('Reverse proxy running on port 80');
});
| 기능 | 설명 | 효과 |
|---|---|---|
| 로드밸런싱 | 트래픽을 여러 서버에 분산 | 확장성 ↑, 가용성 ↑ |
| SSL 종료 | HTTPS 암호화/복호화 처리 | 백엔드 부하 ↓, 인증서 관리 간소화 |
| 캐싱 | 응답을 저장해 재사용 | 응답시간 ↓, 서버 부하 ↓ |
| 압축 | gzip/brotli로 응답 압축 | 대역폭 ↓, 전송속도 ↑ |
| 보안(WAF) | 악성 요청 필터링 | DDoS 방어, SQL Injection 차단 |
| API 게이트웨이 | 인증, Rate Limiting, 라우팅 | 마이크로서비스 진입점 통합 |
"API 서버 앞에 Nginx 리버스 프록시를 두면 SSL 종료를 프록시에서 처리하니까 백엔드는 HTTP로 통신해도 됩니다. 인증서 관리도 한 곳에서 하면 되고, 백엔드 CPU 부하도 30% 정도 줄일 수 있어요."
"Forward Proxy는 클라이언트 측에서 인터넷 접근을 대행하고, Reverse Proxy는 서버 측에서 요청을 분산합니다. 실제로 저희 팀에서는 Nginx를 리버스 프록시로 사용해서 3대의 Node.js 서버로 트래픽을 분산하고, upstream 헬스체크로 장애 서버를 자동 제외했습니다."
"API 서버 2번이 OOM으로 죽었는데, 리버스 프록시의 헬스체크가 감지해서 자동으로 트래픽이 1번, 3번으로 분산됐습니다. 사용자 영향은 없었고, 서버 재시작 후 다시 로드밸런싱 풀에 복귀했습니다."
리버스 프록시를 거치면 백엔드에서 클라이언트 IP가 프록시 IP로 보입니다. proxy_set_header X-Real-IP, X-Forwarded-For를 반드시 설정하세요.
백엔드 서버 장애 시 죽은 서버로 계속 요청이 전달됩니다. HAProxy의 'option httpchk'나 Nginx Plus의 health_check를 활성화하세요.
기본 타임아웃이 너무 길면 슬로우 클라이언트가 연결을 점유합니다. connect_timeout 5s, read_timeout 30s 정도로 설정하세요.
keepalive 연결로 백엔드 연결 재사용, gzip 압축 활성화, 적절한 캐시 정책 설정. 모니터링으로 upstream 응답시간과 에러율을 항상 체크하세요.