← 글 목록

notes

nginx

nginx 기본 구조·리버스 프록시 설정과 운영 시 주의점 메모.

서버에 앱을 올려 두면 보통 Node는 3000·13002처럼 내부 포트에서만 듣고, 바깥에는 80/443만 열어 둔다. 그때 앞에서 받아 넘겨 주는 게 nginx다. 여기엔 내가 실제로 쓰는 리버스 프록시 패턴만 모아 둔다.

  • 바깥엔 80/443만 열고, 앱은 내부 포트에서 nginx가 proxy_pass로 받아 넘긴다.
  • 설정은 http → server → location으로 좁아지고, location은 가장 긴 prefix가 이긴다.
  • 바꾼 설정은 항상 nginx -t && nginx -s reload로 반영한다.

nginx가 하는 일

nginx(“engine x”)는 HTTP 웹 서버이면서 리버스 프록시·캐시·로드밸런서·TCP/UDP 프록시도 한다. Igor Sysoev가 만들었고 BSD 계열 라이선스. 동시 접속을 이벤트 기반으로 나눠서 메모리 적게 쓰는 쪽으로 유명하다.

내가 실무에서 쓰는 역할은 거의 이거다.

  • 바깥 80/443 받기
  • 안쪽 앱(Next standalone, Nest, Docker 컨테이너 포트)으로 proxy_pass
  • 정적 파일·이미지 일부는 nginx가 직접 서빙
  • SSL 종료(인증서는 certbot 등)

앱이 몇 개든 바깥에서 보이는 건 nginx 하나다. 도메인이나 경로로 갈라서 각 포트로 보내면 된다.

프로세스 구조

마스터 프로세스 하나 + 워커 여러 개. 마스터가 설정을 읽고 워커를 관리하고, 실제 요청은 워커가 처리한다. worker_processes는 CPU 코어에 맞추거나 auto로 둔다.

설정은 보통 /etc/nginx/nginx.conf. Debian/Ubuntu면 sites-available / sites-enabled로 가상 호스트를 나눈다.

# 설정 문법 검사
sudo nginx -t

# 반영 (끊김 없이 워커 교체)
sudo nginx -s reload
# 또는
sudo systemctl reload nginx

reload는 새 워커를 띄운 뒤 옛 워커가 진행 중인 요청만 마무리하고 죽는다. 문법 틀리면 롤백한다. 그래서 배포 스크립트에 nginx -t && nginx -s reload를 넣는 습관을 들였다.

설정 뼈대

지시어는 ;로 끝나는 단순형과 { } 블록(컨텍스트)이 있다. http → server → location 순으로 좁아진다.

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

    location / {
      proxy_pass http://127.0.0.1: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;
      proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /images/ {
      root /var/www;
    }
  }
}

location은 가장 긴 prefix가 이기고, ~ 정규식은 그다음에 본다. =는 정확히 일치할 때만, ^~는 prefix가 맞으면 정규식 검사를 건너뛴다. 이미지 확장자만 로컬에서 주고 나머지는 앱으로 넘기는 패턴도 자주 쓴다.

location / {
  proxy_pass http://127.0.0.1:3000/;
}

location ~ \.(gif|jpg|png)$ {
  root /data/images;
}

Docker랑 같이 쓸 때

컨테이너 앱을 13002:3000처럼 올려 두고 nginx는 호스트(또는 nginx 컨테이너)에서 proxy_pass http://127.0.0.1:13002로 붙인다. Cloud V L7 LB 뒤에 둘 때도 LB → nginx(또는 바로 앱 포트) 포트만 맞추면 된다.

같은 머신에 도메인이 여러 개면 server_name으로 server 블록을 나눈다. 예전에 nginx - domain cross setting 쪽에 적어 둔 것과 같은 결.

HTTPS는 certbot으로 인증서를 받고 443 블록을 하나 더 두는 식으로 붙인다. 80은 443으로 돌려보내기만 한다.

HTTPS server 블록 예시
sudo certbot --nginx -d example.com
server {
  listen 80;
  server_name example.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl;
  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://127.0.0.1:13002;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    # 웹소켓 쓸 때
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}

자주 걸린 실수

  • proxy_set_header 빼먹음 — 앱이 Host/프로토콜을 잘못 읽어 리다이렉트 루프나 쿠키 도메인 꼬임.
  • nginx -t 없이 reload — 문법 깨진 채 재시작하다가 죽는 경우.
  • sites-enabled 심볼릭 링크 안 함 — sites-available만 고치고 enabled에 ln -s를 안 해서 반영이 안 됨.
  • 앱 포트만 방화벽에 열어둠 — 바깥에는 80/443만, 앱 포트는 127.0.0.1에서만 듣게 두는 게 안전하다.

로그는 보통 /var/log/nginx/access.log, error.log. 502면 일단 업스트림(앱)이 살아 있는지, 포트·방화벽부터 본다.