NGINX(엔진엑스)는 고성능 웹 서버이자 리버스 프록시, 로드 밸런서, HTTP 캐시 서버 역할을 할 수 있는 오픈소스 소프트웨어.
NGINX는 정적 파일 서빙, 리버스 프록시, 로드밸런싱, 보안 강화까지 담당할 수 있는 다기능 웹 서버.
| 역할 | 설명 |
|---|---|
| 1. 웹 서버 | HTML, CSS, JS, 이미지 같은 정적 파일을 직접 서빙함 |
| 2. 리버스 프록시 | 클라이언트 요청을 내부 서버(Spring, Node 등)로 중계함 |
| 3. 로드 밸런서 | 여러 백엔드 서버로 트래픽을 분산시킴 |
| 4. SSL 종료 (TLS Termination) | HTTPS 연결의 암호 해제 처리를 담당함 |
| 5. 캐시 서버 | 자주 요청되는 데이터를 캐싱하여 성능 향상 |
Nginx가 디스크의 파일(HTML/CSS/JS/이미지)을 직접 반환. 커널 sendfile, open_file_cache 등으로 매우 빠름.
server {
listen 443 ssl http2;
server_name example.com;
root /var/www/app/dist; # 빌드 산출물 위치
index index.html;
# 정적 자원 캐시 정책
location ~* \.(css|js|png|jpg|svg|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
try_files $uri =404;
}
# SPA 라우팅
location / {
try_files $uri /index.html;
}
# 큰 업로드 필요 시
client_max_body_size 50m;
# 파일 I/O 최적화
sendfile on;
tcp_nopush on;
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 120s;
open_file_cache_min_uses 2;
}
클라이언트는 백엔드 주소를 모르고 항상 Nginx로만 접속. Nginx가 요청을 받아 라우팅·헤더정리·보안·버퍼링을 수행한 뒤 업스트림(백엔드) 으로 전달한다.
Client ──HTTPS──▶ Nginx(경계)
├─▶ /api/* → upstream api_svc (Spring 등)
└─▶ / → 정적파일(or SPA index.html)
map $http_upgrade $connection_upgrade { default upgrade; '' close; }
upstream api_svc {
server api1:8080;
server api2:8080;
keepalive 64; # 재사용 커넥션
}
server {
listen 443 ssl http2;
server_name example.com;
# SSL 섹션은 아래 SSL 파트에서 자세히 설명
location /api/ {
proxy_pass http://api_svc;
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;
# WebSocket 지원(필요 시)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 60s;
proxy_connect_timeout 5s;
# SSE/WebSocket이라면 버퍼링 끄기
# proxy_buffering off;
}
}
Nginx가 여러 백엔드로 트래픽을 분산. 기본은 라운드로빈.
upstream api_svc {
least_conn; # 연결 적은 서버에 분산
server api1:8080 max_fails=3 fail_timeout=10s;
server api2:8080 max_fails=3 fail_timeout=10s;
keepalive 64;
}
/* 고정 분산(해시) 예시
upstream api_svc {
hash $cookie_session_id consistent;
server api1:8080;
server api2:8080;
}
*/
Nginx가 SSL 종단(TLS Termination): 클라이언트와 TLS, 내부는 HTTP로 백엔드 연결(물론 내부도 TLS 가능).
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri; # HTTP → HTTPS 강제
}
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;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
# 성능/보안
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# OCSP stapling (체인/CA 파일 필요)
ssl_stapling on;
ssl_stapling_verify on;
# 이하 location 블록들…
}
Nginx가 백엔드 응답을 디스크에 캐시. 다음 동일 요청은 Nginx가 백엔드를 거치지 않고 바로 응답. CDN의 엣지 캐시 같은 역할을 내부에서 수행.
# 1) 캐시 영역 정의 (디스크 경로, 용량, 만료 정책 인덱스)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:256m
max_size=20g inactive=30m use_temp_path=off;
map $request_method $cacheable {
default 0;
GET 1;
HEAD 1;
}
server {
listen 443 ssl http2;
server_name example.com;
location /public-api/ {
proxy_pass http://api_svc;
# 2) 캐시 on/off
proxy_cache api_cache;
proxy_cache_methods GET HEAD;
proxy_cache_key $scheme$host$request_uri;
# 3) 조건부 캐시: GET/HEAD만
proxy_no_cache $http_cache_control $http_authorization;
proxy_cache_bypass $http_cache_control;
# 4) 만료/스테일/락
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504 updating;
proxy_cache_lock on; # 동일 키 동시요청 하나만 백엔드로
proxy_cache_lock_timeout 5s;
proxy_cache_revalidate on; # If-Modified-Since/ETag 재검증
add_header X-Cache-Status $upstream_cache_status always;
}
}
아래는 TLS 종단 + 정적 서빙 + /api 리버스프록시 + 로드밸런싱 + 캐시를 한 번에 보여주는 축약 버전:
map $http_upgrade $connection_upgrade { default upgrade; '' close; }
map $request_method $cacheable { default 0; GET 1; HEAD 1; }
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:256m
max_size=10g inactive=30m use_temp_path=off;
upstream api_svc {
least_conn;
server api1:8080 max_fails=3 fail_timeout=10s;
server api2:8080 max_fails=3 fail_timeout=10s;
keepalive 64;
}
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;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/app/dist;
index index.html;
# 정적 자원 캐시
location ~* \.(css|js|png|jpg|svg|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
try_files $uri =404;
}
# SPA 라우팅
location / {
try_files $uri /index.html;
}
# 캐시가 유효한 공개 API
location /public-api/ {
proxy_pass http://api_svc;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache api_cache;
proxy_cache_methods GET HEAD;
proxy_cache_key $scheme$host$request_uri;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504 updating;
proxy_cache_lock on;
proxy_cache_revalidate on;
add_header X-Cache-Status $upstream_cache_status always;
}
# 인증 필요한 변경 API (캐시 금지)
location /api/ {
proxy_pass http://api_svc;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
proxy_connect_timeout 5s;
# proxy_buffering off; # SSE/WebSocket이면 켜기
}
client_max_body_size 50m;
sendfile on;
tcp_nopush on;
}