nginx reverse proxy로 내부 서버 외부에 공개하는 원리와 설정법

조회수: 0

reverse proxy란 무엇이고 왜 쓰는가

웹을 외부에 공개할 때 서버 하나에 직접 포트를 열어두는 방식은 관리와 보안 면에서 한계가 있다. 특히 이런 상황에서 reverse proxy는 매우 유용하다.

  • 여러 내부 서버를 하나의 도메인·IP로 묶어 외부에 노출
  • 애플리케이션 서버는 사설망에 숨겨두고, nginx만 DMZ 또는 공인망에 배치
  • SSL 종료, 압축, 캐싱, 접속 로그 등 공통 기능을 프록시 단에서 처리
    여기서 말하는 reverse proxy는 “클라이언트 입장에서 보면 실제 서버처럼 보이는 프록시”다. 클라이언트는 nginx까지밖에 모르고, nginx가 내부 서버로 요청을 전달한 뒤 응답을 대신 돌려준다.

내부 서버를 외부에 공개하는 전체 구조

nginx reverse proxy로 내부 서버를 외부에 공개하는 구조를 단순화해서 그려보면 대략 이렇게 된다.

  • 클라이언트(인터넷 사용자)
    • 브라우저에서 https://example.com 요청
  • 공인망에 있는 nginx 서버
    • 공인 IP 또는 포트포워딩된 80/443 포트를 사용
    • 리버스 프록시 역할로 내부 서버에 HTTP 요청 전달
  • 내부망(사설망)에 있는 애플리케이션 서버
    • 예: 192.168.0.10:8080, 192.168.0.11:3000
    • 외부에서는 직접 접근 불가능
      이 구조의 핵심은 “외부에서 직접 내부 서버에 붙지 않고, 반드시 nginx를 경유하도록 만드는 것”이다. 외부의 모든 트래픽이 nginx를 지나가기 때문에, 접근 제어·로그·SSL·리다이렉트 등 정책을 한 곳에서 통제할 수 있다.

nginx reverse proxy 기본 동작 원리

nginx가 reverse proxy로 동작할 때의 기본 흐름을 정리해 보자.

  1. 클라이언트가 nginx의 도메인/포트로 HTTP(S) 요청을 보낸다.
  2. nginx는 설정 파일의 server / location 블록에서 이 요청을 어떤 내부 서버(upstream)로 보낼지 결정한다.
  3. proxy_pass 지시어를 이용해 내부 서버로 HTTP 요청을 전달한다.
  4. 내부 서버가 응답을 보내면 nginx가 이 응답을 받아 클라이언트에게 다시 전달한다.
    이 과정에서 nginx는 다음과 같은 작업들을 추가로 수행할 수 있다.
  • X-Forwarded-For, X-Real-IP 등 헤더를 추가해 원래 클라이언트 IP 전달
  • gzip 압축, 캐싱, 헤더 추가/삭제
  • HTTP → HTTPS 리다이렉트
  • URL 리라이트, 특정 경로만 다른 서버로 라우팅 등

기본 reverse proxy 설정 예제

가장 단순한 예제를 하나 보자. 공인 IP를 가진 nginx 서버가 있고, 내부에 192.168.0.10:8080에서 웹 애플리케이션이 동작한다고 가정한다.

/etc/nginx/sites-available/myapp.conf 예시:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://192.168.0.10:8080;
        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_pass에 내부 서버 주소(사설 IP)를 지정
  • Host 헤더를 원래 요청 도메인으로 유지하도록 proxy_set_header 설정
  • 원래 클라이언트 IP를 내부 서버에 전달하기 위한 X-Real-IP, X-Forwarded-For
    이 설정을 넣은 뒤 nginx 설정을 테스트하고 재시작한다.
sudo nginx -t
sudo systemctl reload nginx

이제 브라우저에서 http://example.com으로 접속하면, 실제로는 nginx가 내부 192.168.0.10:8080에 요청을 보내고 그 응답을 가져와 보여주게 된다.

여러 내부 서버를 하나의 nginx로 공개하기

실무에서는 보통 내부에 서비스가 여러 개 있다. 이때 nginx reverse proxy는 다음과 같이 라우팅할 수 있다.

도메인 단위로 분리

  • api.example.com → 내부 192.168.0.10:8080
  • admin.example.com → 내부 192.168.0.11:3000
server {
    listen 80;
    server_name api.example.com;

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

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

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

경로(Path) 단위로 분리

같은 도메인에서 경로에 따라 다른 내부 서버로 보내는 것도 흔하다.

  • / → 192.168.0.10:8080 (웹 프론트)
  • /api/ → 192.168.0.11:3000 (API 서버)
server {
    listen 80;
    server_name example.com;

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

    location / {
        proxy_pass http://192.168.0.10:8080/;
        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_pass 뒤에 /를 붙이느냐 마느냐에 따라 실제 전달되는 URI가 달라지니 주의가 필요하다. API 서버가 /api를 전제로 라우팅을 구성했는지, 루트(/)를 전제로 했는지에 맞춰 설정을 조정해야 한다.

HTTPS(SSL/TLS) 적용과 reverse proxy

실제 서비스에서는 HTTP만 공개하기보다는 HTTPS를 기본으로 사용하는 것이 일반적이다. nginx가 SSL 종료를 담당하게 하면 내부 서버는 HTTP로만 통신해도 된다.

기본 HTTPS 설정 흐름

  1. 도메인에 대한 인증서 발급 (Let’s Encrypt 등)
  2. nginx의 server 블록에서 443 포트 listen, ssl_certificate 설정
  3. 서버 내부와 nginx 간 통신은 HTTP(80 또는 다른 포트)로 유지

예시:

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

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

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://192.168.0.10:8080;
        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 https;
    }
}

이 구조의 장점은 다음과 같다.

  • 클라이언트와 nginx 사이 구간은 HTTPS로 암호화
  • 내부망에서는 HTTP를 사용해 성능과 구성 단순화
  • 인증서 갱신·관리도 nginx 한 곳에서만 처리하면 된다
    인증서 자동 발급·갱신에는 certbot을 많이 사용하며, 공식 문서는 https://nginx.orghttps://certbot.eff.org 에서 참고할 수 있다.

내부 서버를 보호하기 위한 방화벽·네트워크 설정

“reverse proxy로 외부에 공개한다”는 말은 곧 “내부 서버는 직접 노출하지 않는다”는 뜻이기도 하다. 이 원칙을 지키려면 방화벽과 네트워크를 다음과 같이 설계하는 것이 좋다.

  • 내부 서버는 80/443 포트를 외부에 열지 않는다.
    • 오직 nginx 서버(또는 해당 서브넷)에서만 접근 허용
  • nginx 서버는 외부에서 80/443만 열고 나머지 포트는 막는다.
  • SSH, DB, 캐시 서버 등은 사설망에서만 접근 가능하게 구성
    예를 들어 내부 서버에서 UFW를 쓴다면, nginx 서버 IP만 허용하는 식의 구성이 가능하다.
# 내부 서버에서 실행 (예: 192.168.0.20이 nginx 서버)
sudo ufw allow from 192.168.0.20 to any port 8080 proto tcp
sudo ufw deny 8080/tcp

이렇게 하면 nginx를 거치지 않는 8080 직접 접속은 막고, nginx에서 들어오는 요청만 받게 할 수 있다.

WebSocket, SSE 등 실시간 통신 처리 시 주의점

최근 서비스에서는 WebSocket, Server-Sent Events(SSE) 같은 실시간 통신도 많이 사용한다. nginx reverse proxy로 이 트래픽을 처리할 때는 다음 부분을 신경 써야 한다.

  • WebSocket의 경우 UpgradeConnection 헤더를 그대로 전달
  • 타임아웃 설정을 너무 짧게 잡지 않기
    예시:
location /ws/ {
    proxy_pass http://192.168.0.11:4000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
}

이렇게 하면 /ws/ 경로의 WebSocket 연결을 내부 서버로 안정적으로 프록시할 수 있다.

자주 발생하는 오류와 점검 포인트

nginx reverse proxy로 내부 서버를 외부에 공개하는 과정에서 흔히 겪는 문제들을 몇 가지 정리해 보자.

  • 502 Bad Gateway
    • 내부 서버가 죽었거나 포트/주소를 잘못 지정한 경우
    • 방화벽에서 nginx → 내부 서버 트래픽이 막힌 경우
  • 404 또는 정적 파일 누락
    • 내부 서버가 기대하는 경로와 실제 proxy_pass 경로가 맞지 않을 때
    • 예: 애플리케이션이 /app 기준인데 nginx에서 /로 넘기는 경우
  • 리디렉션 루프
    • 내부 서버가 HTTP→HTTPS 리디렉트를 하고 있는데, nginx에서 이미 HTTPS로 받고 있는 상황
    • X-Forwarded-Proto 헤더를 이용해 내부 서버가 프록시 환경을 인식하도록 설정 필요
      문제가 생기면 다음 순서로 확인하는 것이 보통 빠르다.
  1. 내부 서버에 직접 접속해서 정상 동작 여부 확인 (curl로 사설 IP:포트 테스트)
  2. nginx 에러 로그(/var/log/nginx/error.log) 확인
  3. nginx 설정 문법 오류 여부 (nginx -t) 재확인
  4. 방화벽, SELinux, 보안 그룹(클라우드 환경)의 포트 허용 여부 점검

마무리: reverse proxy를 기준으로 아키텍처를 설계하기

nginx reverse proxy로 내부 서버를 외부에 공개하는 패턴은 소규모 개인 서버부터 기업 서비스까지 폭넓게 사용된다.
정리해보면 핵심은 다음과 같다.

  • 외부에서 들어오는 모든 요청은 nginx 한 곳으로 모은다.
  • nginx가 도메인/경로에 따라 적절한 내부 서버로 요청을 라우팅한다.
  • 내부 서버는 사설망에 숨겨두고, nginx와의 통신만 허용한다.
  • HTTPS, 로깅, 접속 제어 등을 nginx에서 일괄 처리해 관리 포인트를 줄인다.
    이 원리를 이해하고 기본 설정을 손으로 몇 번 써보면, 이후 서비스가 늘어나도 크게 구조를 바꾸지 않고 확장해 나갈 수 있다. nginx reverse proxy를 “외부와 내부를 나누는 관문”으로 두고 설계하는 습관이 잡히면, 보안과 운영 측면에서 모두 안정적인 웹 인프라를 구축하기가 훨씬 수월해진다.