한 VPS에서 여러 도메인/앱을 nginx로 운영하는 구조
정적 사이트 + API 프록시 + PHP(FastCGI)까지 함께 올릴 때의 vhost/경로/캐시/보안 패턴
한 VPS에서 여러 서비스를 운영하는 가장 흔한 형태는 아래 조합입니다.
- 정적 사이트(랜딩/문서) 1~N개
- Node API(인증, 서비스 API) 1~N개
- PHP 앱(예: MediaWiki 같은 레거시) 0~1개
이 글은 “다 같이 올려도, 설정이 꼬이지 않게” 하는 구조를 정리합니다.
예시는 모두 익명화되어 있으며,
example.com을 사용합니다.
1) 디렉토리 레이아웃(권장)
서버의 “앱 루트”를 한 곳으로 모으면 운영이 편해집니다.
/srv/apps/
dev.example.com/ # 정적(SSG) dist
www.example.com/ # 정적 랜딩
auth-api/ # Node API (127.0.0.1:4000)
wiki/ # PHP 앱 (php-fpm)
social-api/ # Node API (127.0.0.1:4600)
Certbot webroot는 별도로 둡니다.
/var/www/letsencrypt
2) 사이트별 server 블록을 분리
파일을 “도메인 단위”로 나누면 사고가 줄어듭니다.
/etc/nginx/sites-available/
dev.example.com.conf
example.com.conf
wiki.example.com.conf
3) 정적 사이트(vhost)의 기본 패턴
정적은 단순할수록 안정적입니다.
server {
server_name dev.example.com;
root /srv/apps/dev.example.com/dist;
index index.html;
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
}
location / {
try_files $uri $uri/ =404;
}
}
4) API 프록시(vhost) 패턴
Node API는 로컬에서만 띄우고(127.0.0.1), nginx가 프록시합니다.
location /api/ {
proxy_pass http://127.0.0.1:4000;
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;
}
관련 레시피: nginx: Node API 리버스 프록시 기본
5) PHP(FastCGI) 앱(예: MediaWiki) 패턴
PHP 앱은 “정적 자산도 결국 index.php로 돌아갈 수 있다”는 전제를 두고 구성합니다.
root /srv/apps/wiki;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
6) 캐시: “해시된 파일만” 길게
- 해시가 붙는 번들(
/assets/app.abc123.js)은 1년 캐시 +immutable - 해시가 없는 파일(예:
/index.html)은 짧게 또는 no-cache
관련 레시피: nginx: 해시된 정적 자산에 immutable 캐시
7) 운영에서 많이 터지는 함정(체크리스트)
proxy_pass끝 슬래시(/)로 경로가 의도치 않게 바뀜/socialvs/social/리다이렉트 누락(상대경로/쿠키 path 꼬임)alias를 쓰면서try_files경로를 잘못 조합함- WebSocket 프록시에
Upgrade/Connection헤더를 빼먹음 - 보안 헤더/CSP를 한 사이트에만 적용하고 나머지를 놓침
같이 보면 좋은 문서: