nginx에서 한 도메인에 정적/API/WebSocket/서브앱 붙이기

실전 운영에서 많이 쓰는 nginx 라우팅 패턴과 흔한 함정(proxy_pass/alias/try_files)

분야: DevOps/인프라 시리즈: Nginx nginxreverse-proxywebsocketsparouting

한 도메인에서 정적 프론트(SSG/SPA) + API + WebSocket + 별도 앱(서브 디렉터리 마운트)을 같이 운영하는 패턴은 매우 흔합니다. 문제는 작은 실수로 404/무한리다이렉트/쿠키 누락/WS 끊김이 자주 발생한다는 점입니다.

1) 체크리스트

  • /.well-known/acme-challenge/는 SSL 갱신을 위해 예외 처리
  • 정적 파일은 try_files로 안전하게 서빙 (디렉터리 트래버설 방지)
  • proxy_pass슬래시 유무를 의식 (/api/ → 업스트림 경로 결합 규칙)
  • WebSocket은 Upgrade/Connection 헤더 설정
  • 백엔드에 원본 정보를 전달: X-Forwarded-For, X-Forwarded-Proto, Host
  • 민감 파일 차단: .env, .git, 백업 파일 등

바로 쓰는 스니펫: nginx: 민감 파일(.env/.git/백업) 차단

2) 익명화된 예시 설정

server {
  server_name example.com;
  root /var/www/site/dist;
  index index.html;

  # LetsEncrypt
  location /.well-known/acme-challenge/ {
    root /var/www/letsencrypt;
  }

  # 민감 파일 차단 (필수)
  include /etc/nginx/snippets/block-sensitive-files.conf;

  # 정적 앱 (SPA fallback)
  location / {
    try_files $uri $uri/ /index.html;
  }

  # API 프록시
  location /api/ {
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://127.0.0.1:3000/;
  }

  # WebSocket
  location /ws/ {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_pass http://127.0.0.1:3001/;
  }

  # 서브앱 마운트(빌드 산출물 폴더를 붙이는 방식)
  location ^~ /docs/ {
    alias /var/www/docs/dist/;
    try_files $uri $uri/ =404;
  }
}

3) 흔한 함정

  • location /api/에서 proxy_pass http://127.0.0.1:3000;처럼 “뒤 슬래시”가 빠지면 업스트림으로 전달되는 경로가 기대와 달라질 수 있습니다.
  • aliasroot와 동작 방식이 다릅니다. alias를 쓸 때는 try_files 경로도 함께 점검합니다.
  • 프록시 뒤 HTTPS인데 앱이 HTTP로 인식하면, 쿠키의 Secure 설정/리다이렉트가 꼬일 수 있습니다. (프레임워크의 trust proxy 설정 필요)

4) 관련 문서/도구

관련 가이드