리버스 프록시: 하나의 주소로 여러 애플리케이션
하나의 공인 VDS 주소를 도메인과 경로로 여러 애플리케이션에 나눠 쓰고, 헤더를 올바르게 전달하며, WebSocket을 깨뜨리지 않는 방법을 알아봐요.
업데이트 2026년 8월 23일
한 대의 머신에는 공인 IPv4 주소가 하나뿐이지만, 보통 여러 애플리케이션이 실행됩니다. 리버스 프록시는 80번과 443번 포트로 들어오는 모든 요청을 받아 도메인 이름이나 경로에 따라 내부 서비스로 라우팅합니다. 또한 인증서 하나와 공유 로그도 함께 관리합니다.
구성
각 애플리케이션은 자체 로컬 포트에서, 그것도 127.0.0.1에서만 수신합니다:
- 프런트엔드 사이트 —
127.0.0.1:3000; - API —
127.0.0.1:8000; - 모니터링 대시보드 —
127.0.0.1:9090.
외부에 노출되는 것은 Nginx뿐입니다. 애플리케이션이 직접 노출되지 않았는지 확인하려면:
ss -tulpn | grep LISTEN
0.0.0.0:3000 같은 줄은 해당 서비스가 프록시를 우회하고 암호화도 없이 인터넷에서 접근 가능하다는 뜻입니다.
도메인 기준
server {
listen 80;
server_name app.example.ru;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.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;
}
}
server {
listen 80;
server_name api.example.ru;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
X-Forwarded-For가 없으면 애플리케이션은 모든 요청이 127.0.0.1에서 오는 것으로 인식하고, IP 기반 제한이 모두 작동하지 않습니다.
경로 기준
location /api/ {
proxy_pass http://127.0.0.1:8000/;
}
여기서는 proxy_pass의 끝 슬래시가 중요합니다. 슬래시가 있으면 /api 접두사가 제거되어 애플리케이션이 /users를 받고, 없으면 /api/users를 받습니다. 프록시에서 발생하는 “정체불명의 404” 중 절반은 바로 이 때문입니다.
WebSocket과 긴 응답
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_read_timeout 3600s;
}
Upgrade/Connection 쌍이 없으면 핸드셰이크 직후 연결이 끊기고, 타임아웃을 늘리지 않으면 1분간 응답이 없을 때 끊깁니다.
흔한 실수
| 증상 | 원인 |
|---|---|
502 Bad Gateway | 애플리케이션이 실행되지 않았거나 다른 포트에서 수신 중 |
504 Gateway Time-out | 애플리케이션의 응답이 proxy_read_timeout보다 오래 걸림 |
| 무한 리디렉션 | 애플리케이션이 HTTPS를 인식하지 못함 — X-Forwarded-Proto를 전달하세요 |
| 파일 업로드 실패 | client_max_body_size가 너무 작음; 기본값은 1 MB |
| 애플리케이션 로그에 동일한 IP | X-Forwarded-For가 전달되지 않음 |
다음: 여러 사이트, 인증서, 자동 HTTPS를 지원하는 Caddy.
프록시의 인증서 하나가 그 뒤의 모든 애플리케이션을 커버합니다.
127.0.0.1을 통한 내부 연결은 암호화가 필요 없습니다 — 머신을 벗어나지 않기 때문입니다.