Nginx 완벽 가이드 | 설치·설정·리버스 프록시·로드 밸런싱 총정리
이 글의 핵심
Nginx 웹 서버의 모든 것. 설치부터 리버스 프록시, 로드 밸런싱, SSL/TLS, 캐싱, 보안 설정까지. Apache와의 비교, 성능 튜닝, 실전 설정 예제로 완벽 마스터.
들어가며: Nginx가 필요한 이유
Nginx(엔진엑스)는 전 세계 상위 1,000만 웹사이트 중 약 33%가 사용하는 고성능 웹 서버입니다. 정적 파일 서빙, 리버스 프록시, 로드 밸런싱, SSL 종료 등 현대 웹 인프라의 핵심 역할을 담당합니다.
Nginx의 주요 용도:
- 웹 서버 (정적 파일 서빙)
- 리버스 프록시
- 로드 밸런서
- API 게이트웨이
- SSL/TLS 종료
- 캐싱 서버
목차
1. Nginx 기본 개념
Nginx란?
Nginx(Engine X)는 Igor Sysoev가 2004년 개발한 고성능 웹 서버이자 리버스 프록시 서버입니다.
Nginx vs Apache
flowchart TB
subgraph Nginx[Nginx 아키텍처]
N_Master[Master Process]
N_W1[Worker 1<br/>이벤트 기반]
N_W2[Worker 2<br/>이벤트 기반]
N_Master --> N_W1
N_Master --> N_W2
N_W1 --> N_C1[10,000 연결]
N_W2 --> N_C2[10,000 연결]
end
subgraph Apache[Apache 아키텍처]
A_Master[Master Process]
A_P1[Process 1]
A_P2[Process 2]
A_P3[Process 3]
A_Master --> A_P1
A_Master --> A_P2
A_Master --> A_P3
A_P1 --> A_C1[1 연결]
A_P2 --> A_C2[1 연결]
A_P3 --> A_C3[1 연결]
end
비교표
| 특성 | Nginx | Apache |
|---|---|---|
| 아키텍처 | 이벤트 기반 비동기 | 프로세스/스레드 기반 |
| 메모리 | 낮음 (2-4MB/worker) | 높음 (10-20MB/process) |
| 동시 연결 | 수만 개 | 수백 개 |
| 정적 파일 | 매우 빠름 | 빠름 |
| 동적 콘텐츠 | 프록시 필요 | 직접 처리 (.htaccess) |
| 설정 파일 | 간결 | 복잡 (.htaccess) |
| 모듈 | 컴파일 시 포함 | 동적 로딩 |
| 사용 사례 | 고트래픽, 정적 파일 | 복잡한 동적 처리 |
2. 설치 및 기본 설정
설치
Ubuntu/Debian
# 공식 저장소 추가
sudo apt update
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
# 설치
sudo apt update
sudo apt install nginx
# 버전 확인
nginx -v
# nginx version: nginx/1.24.0
CentOS/RHEL
# 저장소 추가
sudo yum install yum-utils
sudo yum-config-manager --add-repo https://nginx.org/packages/centos/nginx.repo
# 설치
sudo yum install nginx
# 시작
sudo systemctl start nginx
sudo systemctl enable nginx
macOS
# Homebrew로 설치
brew install nginx
# 시작
brew services start nginx
# 설정 파일 위치
# /usr/local/etc/nginx/nginx.conf
Docker
# Docker로 실행
docker run -d \
--name nginx \
-p 80:80 \
-v $(pwd)/html:/usr/share/nginx/html:ro \
nginx:latest
# 커스텀 설정
docker run -d \
--name nginx \
-p 80:80 \
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
-v $(pwd)/html:/usr/share/nginx/html:ro \
nginx:latest
기본 명령어
# 시작
sudo systemctl start nginx
sudo nginx
# 중지
sudo systemctl stop nginx
sudo nginx -s stop
# 재시작
sudo systemctl restart nginx
sudo nginx -s reload # 설정 리로드 (무중단)
# 상태 확인
sudo systemctl status nginx
# 설정 테스트
sudo nginx -t
# 버전 및 모듈 확인
nginx -V
3. Nginx 아키텍처
프로세스 모델
flowchart TB
Master[Master Process<br/>root 권한]
W1[Worker Process 1<br/>nginx 사용자]
W2[Worker Process 2<br/>nginx 사용자]
W3[Worker Process 3<br/>nginx 사용자]
W4[Worker Process 4<br/>nginx 사용자]
Cache[Cache Loader<br/>Cache Manager]
Master --> W1
Master --> W2
Master --> W3
Master --> W4
Master --> Cache
subgraph Connections[클라이언트 연결]
C1[10,000 연결]
C2[10,000 연결]
C3[10,000 연결]
C4[10,000 연결]
end
W1 --> C1
W2 --> C2
W3 --> C3
W4 --> C4
이벤트 기반 처리
sequenceDiagram
participant C1 as Client 1
participant C2 as Client 2
participant C3 as Client 3
participant W as Worker Process
participant Backend as Backend Server
Note over W: epoll/kqueue로<br/>이벤트 감시
C1->>W: HTTP Request
W->>Backend: Proxy Request
C2->>W: HTTP Request (동시)
W->>Backend: Proxy Request (동시)
C3->>W: HTTP Request (동시)
Backend->>W: Response for C1
W->>C1: HTTP Response
Backend->>W: Response for C2
W->>C2: HTTP Response
W->>Backend: Proxy Request for C3
Backend->>W: Response for C3
W->>C3: HTTP Response
Note over W: 하나의 프로세스가<br/>모든 연결 처리
설정 파일 구조
# /etc/nginx/nginx.conf
# 메인 컨텍스트
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
# 이벤트 컨텍스트
events {
worker_connections 1024;
use epoll; # Linux
}
# HTTP 컨텍스트
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 로그 포맷
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# 성능 설정
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
# Gzip 압축
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# 서버 블록 포함
include /etc/nginx/conf.d/*.conf;
}
4. 정적 파일 서빙
기본 웹 서버 설정
# /etc/nginx/conf.d/static.conf
server {
listen 80;
server_name example.com www.example.com;
# 루트 디렉토리
root /var/www/html;
index index.html index.htm;
# 접근 로그
access_log /var/log/nginx/example.access.log;
error_log /var/log/nginx/example.error.log;
# 기본 location
location / {
try_files $uri $uri/ =404;
}
# 정적 파일 캐싱
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 특정 파일 차단
location ~ /\.(?!well-known) {
deny all;
}
}
SPA (Single Page Application) 설정
# React, Vue, Angular 등
server {
listen 80;
server_name app.example.com;
root /var/www/app/dist;
index index.html;
# SPA 라우팅 (모든 요청을 index.html로)
location / {
try_files $uri $uri/ /index.html;
}
# API 프록시
location /api/ {
proxy_pass http://localhost:3000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# 정적 자산 캐싱
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
5. 리버스 프록시
리버스 프록시란?
flowchart LR
Client[클라이언트] --> Nginx[Nginx<br/>리버스 프록시<br/>:80]
Nginx --> Backend1[Backend 1<br/>:3000]
Nginx --> Backend2[Backend 2<br/>:3001]
Nginx --> Backend3[Backend 3<br/>:3002]
Nginx --> Static[정적 파일<br/>/var/www]
style Nginx fill:#4CAF50
기본 리버스 프록시 설정
server {
listen 80;
server_name api.example.com;
# 백엔드로 프록시
location / {
proxy_pass http://localhost: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;
# 타임아웃
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 버퍼링
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
}
}
WebSocket 프록시
# WebSocket 지원
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name ws.example.com;
location / {
proxy_pass http://localhost:3000;
# WebSocket 헤더
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
# 타임아웃 (WebSocket은 길게)
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
gRPC 프록시
server {
listen 80 http2;
server_name grpc.example.com;
location / {
grpc_pass grpc://localhost:50051;
# gRPC 헤더
grpc_set_header Host $host;
grpc_set_header X-Real-IP $remote_addr;
# 에러 처리
error_page 502 = /error502grpc;
}
location = /error502grpc {
internal;
default_type application/grpc;
add_header grpc-status 14;
add_header grpc-message "unavailable";
return 204;
}
}
6. 로드 밸런싱
업스트림 설정
# 백엔드 서버 그룹 정의
upstream backend {
# 로드 밸런싱 알고리즘: round-robin (기본값)
server backend1.example.com:8080 weight=3;
server backend2.example.com:8080 weight=2;
server backend3.example.com:8080 weight=1;
# 백업 서버
server backup.example.com:8080 backup;
# 헬스 체크 실패 시 제외
server backend4.example.com:8080 max_fails=3 fail_timeout=30s;
# Keep-Alive 연결 유지
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
# Keep-Alive 헤더
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
로드 밸런싱 알고리즘
# 1. Round Robin (기본값)
upstream backend_rr {
server backend1:8080;
server backend2:8080;
server backend3:8080;
}
# 2. Least Connections (연결 수 기반)
upstream backend_lc {
least_conn;
server backend1:8080;
server backend2:8080;
server backend3:8080;
}
# 3. IP Hash (세션 고정)
upstream backend_ip {
ip_hash;
server backend1:8080;
server backend2:8080;
server backend3:8080;
}
# 4. Hash (커스텀 키)
upstream backend_hash {
hash $request_uri consistent;
server backend1:8080;
server backend2:8080;
server backend3:8080;
}
# 5. Random (무작위)
upstream backend_random {
random two least_conn; # 2개 중 연결 적은 것 선택
server backend1:8080;
server backend2:8080;
server backend3:8080;
}
헬스 체크
# 기본 헬스 체크 (수동)
upstream backend {
server backend1:8080 max_fails=3 fail_timeout=30s;
server backend2:8080 max_fails=3 fail_timeout=30s;
# max_fails: 실패 횟수
# fail_timeout: 실패 후 재시도까지 대기 시간
}
# Nginx Plus (상용)에서는 능동적 헬스 체크 지원
# 오픈소스는 외부 모듈 필요
7. SSL/TLS 설정
Let’s Encrypt SSL 설정
# Certbot 설치
sudo apt install certbot python3-certbot-nginx
# SSL 인증서 발급
sudo certbot --nginx -d example.com -d www.example.com
# 자동 갱신 설정
sudo certbot renew --dry-run
SSL 설정 파일
server {
listen 80;
server_name example.com www.example.com;
# HTTP → HTTPS 리다이렉트
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL 인증서
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL 프로토콜
ssl_protocols TLSv1.2 TLSv1.3;
# SSL 암호화 스위트 (강력한 것만)
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
# SSL 세션 캐시
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# 보안 헤더
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
SSL 성능 최적화
# SSL 세션 재사용
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
# OCSP Stapling (SSL 핸드셰이크 속도 향상)
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# HTTP/2 활성화
listen 443 ssl http2;
# SSL 버퍼 크기 조정
ssl_buffer_size 4k; # 작은 파일에 최적화
8. 캐싱
프록시 캐시 설정
# HTTP 컨텍스트에서 캐시 경로 정의
http {
# 캐시 저장 경로
proxy_cache_path /var/cache/nginx/proxy
levels=1:2
keys_zone=my_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
# 캐시 활성화
proxy_cache my_cache;
# 캐시 키
proxy_cache_key "$scheme$request_method$host$request_uri";
# 캐시 유효 시간
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
# 캐시 우회 조건
proxy_cache_bypass $http_cache_control;
# 캐시 상태 헤더 추가
add_header X-Cache-Status $upstream_cache_status;
# 캐시 락 (동일 요청 중복 방지)
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
}
}
}
캐시 제어
location /api/ {
proxy_pass http://backend;
proxy_cache my_cache;
# 특정 조건에서 캐시 우회
proxy_cache_bypass $cookie_nocache $arg_nocache;
# 특정 조건에서 캐시 안 함
proxy_no_cache $cookie_nocache $arg_nocache;
# 캐시 메서드
proxy_cache_methods GET HEAD;
# 최소 사용 횟수 (2번 이상 요청된 것만 캐시)
proxy_cache_min_uses 2;
}
# 캐시 퍼지 (Nginx Plus 또는 모듈 필요)
location ~ /purge(/.*) {
allow 127.0.0.1;
deny all;
proxy_cache_purge my_cache "$scheme$request_method$host$1";
}
FastCGI 캐시 (PHP)
# FastCGI 캐시 경로
fastcgi_cache_path /var/cache/nginx/fastcgi
levels=1:2
keys_zone=php_cache:10m
max_size=1g
inactive=60m;
server {
listen 80;
server_name example.com;
root /var/www/html;
index index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# FastCGI 캐시
fastcgi_cache php_cache;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_valid 200 60m;
# 캐시 우회 (로그인 사용자, POST 요청)
fastcgi_cache_bypass $cookie_user $request_method;
fastcgi_no_cache $cookie_user $request_method;
add_header X-Cache-Status $upstream_cache_status;
}
}
9. 보안 설정
기본 보안 강화
server {
listen 443 ssl http2;
server_name example.com;
# 서버 버전 숨기기
server_tokens off;
# 보안 헤더
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
# HSTS (Strict-Transport-Security)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# 클릭재킹 방지
add_header X-Frame-Options "DENY" always;
# 파일 업로드 크기 제한
client_max_body_size 10M;
# 요청 속도 제한
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
limit_req zone=one burst=20 nodelay;
location / {
root /var/www/html;
}
}
Rate Limiting (요청 속도 제한)
# HTTP 컨텍스트
http {
# IP별 요청 속도 제한
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
# API 엔드포인트별 제한
limit_req_zone $request_uri zone=api:10m rate=5r/s;
# 동시 연결 수 제한
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
listen 80;
# 일반 페이지: 초당 10개 요청, 버스트 20개
location / {
limit_req zone=general burst=20 nodelay;
limit_conn addr 10;
}
# API: 초당 5개 요청, 버스트 10개
location /api/ {
limit_req zone=api burst=10 nodelay;
limit_conn addr 5;
# 제한 초과 시 응답
limit_req_status 429;
limit_conn_status 429;
}
# 로그인: 초당 1개 요청
location /login {
limit_req zone=general burst=5;
}
}
}
IP 화이트리스트/블랙리스트
# 특정 IP 차단
location /admin/ {
# 허용 IP
allow 192.168.1.0/24;
allow 10.0.0.0/8;
# 나머지 차단
deny all;
proxy_pass http://backend;
}
# GeoIP로 국가별 차단 (모듈 필요)
http {
geoip_country /usr/share/GeoIP/GeoIP.dat;
map $geoip_country_code $allowed_country {
default no;
KR yes; # 한국만 허용
US yes; # 미국 허용
}
server {
listen 80;
if ($allowed_country = no) {
return 403 "Access denied from your country";
}
location / {
root /var/www/html;
}
}
}
DDoS 방어
# HTTP 컨텍스트
http {
# 연결 속도 제한
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=5r/s;
# 느린 요청 차단
client_body_timeout 10s;
client_header_timeout 10s;
send_timeout 10s;
# 큰 요청 차단
client_max_body_size 1M;
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;
server {
listen 80;
# 연결 제한 적용
limit_conn conn_limit 10;
limit_req zone=req_limit burst=10 nodelay;
# User-Agent 검증
if ($http_user_agent ~* (bot|crawler|spider|scraper)) {
return 403;
}
# Referer 검증
valid_referers none blocked example.com *.example.com;
if ($invalid_referer) {
return 403;
}
location / {
root /var/www/html;
}
}
}
10. 성능 튜닝
Worker 프로세스 최적화
# CPU 코어 수만큼 워커 생성
worker_processes auto;
# CPU 친화성 (각 워커를 특정 CPU에 바인딩)
worker_cpu_affinity auto;
# 워커당 최대 연결 수
events {
worker_connections 4096; # 기본값: 1024
# 이벤트 모델
use epoll; # Linux
# use kqueue; # FreeBSD/macOS
# 여러 연결을 한 번에 accept
multi_accept on;
}
# 최대 동시 연결 수 = worker_processes × worker_connections
# 예: 4 × 4096 = 16,384 연결
파일 I/O 최적화
http {
# Sendfile (커널 공간에서 직접 전송)
sendfile on;
# TCP_NOPUSH (sendfile과 함께 사용)
tcp_nopush on;
# TCP_NODELAY (작은 패킷 즉시 전송)
tcp_nodelay on;
# 파일 디스크립터 캐시
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# 버퍼 크기
client_body_buffer_size 128k;
client_max_body_size 10m;
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;
output_buffers 1 32k;
postpone_output 1460;
}
Gzip 압축
http {
# Gzip 압축 활성화
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6; # 1-9 (높을수록 압축률 높지만 CPU 사용 증가)
gzip_min_length 256;
# 압축할 MIME 타입
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/rss+xml
application/atom+xml
image/svg+xml
text/x-component
text/x-cross-domain-policy;
# 압축 안 할 브라우저 (구형 IE)
gzip_disable "msie6";
}
Brotli 압축 (Gzip보다 효율적)
# Brotli 모듈 설치 필요
http {
brotli on;
brotli_comp_level 6;
brotli_types
text/plain
text/css
application/json
application/javascript
text/xml
application/xml
application/xml+rss
text/javascript;
}
11. 실전 시나리오
시나리오 1: Node.js 앱 배포
# Node.js 앱 (Express) 프록시
upstream nodejs_backend {
least_conn;
server localhost:3000;
server localhost:3001;
server localhost:3002;
keepalive 64;
}
server {
listen 443 ssl http2;
server_name app.example.com;
# SSL 설정
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
# 정적 파일 (빌드된 프론트엔드)
location / {
root /var/www/app/dist;
try_files $uri $uri/ /index.html;
# 캐싱
expires 1h;
add_header Cache-Control "public, immutable";
}
# API 프록시
location /api/ {
proxy_pass http://nodejs_backend/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 캐시 비활성화 (동적 API)
proxy_cache_bypass 1;
proxy_no_cache 1;
}
# WebSocket
location /socket.io/ {
proxy_pass http://nodejs_backend/socket.io/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
}
}
시나리오 2: 마이크로서비스 API 게이트웨이
# 여러 마이크로서비스 프록시
upstream auth_service {
server auth:8080;
}
upstream user_service {
server user:8081;
}
upstream order_service {
server order:8082;
}
upstream payment_service {
server payment:8083;
}
server {
listen 80;
server_name api.example.com;
# 공통 설정
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Rate Limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
limit_req zone=api_limit burst=200 nodelay;
# 인증 서비스
location /api/auth/ {
proxy_pass http://auth_service/;
}
# 사용자 서비스
location /api/users/ {
# 인증 확인
auth_request /auth;
proxy_pass http://user_service/;
}
# 주문 서비스
location /api/orders/ {
auth_request /auth;
proxy_pass http://order_service/;
# 타임아웃 (결제 처리는 길게)
proxy_read_timeout 120s;
}
# 결제 서비스
location /api/payments/ {
auth_request /auth;
proxy_pass http://payment_service/;
# HTTPS만 허용
if ($scheme != "https") {
return 301 https://$server_name$request_uri;
}
}
# 내부 인증 엔드포인트
location = /auth {
internal;
proxy_pass http://auth_service/verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
# 헬스 체크
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}
시나리오 3: 정적 사이트 + CDN
server {
listen 443 ssl http2;
server_name cdn.example.com;
root /var/www/cdn;
# 브라우저 캐싱
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Vary "Accept-Encoding";
# CORS 허용
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, OPTIONS";
}
location ~* \.(css|js)$ {
expires 1M;
add_header Cache-Control "public";
add_header Vary "Accept-Encoding";
}
location ~* \.(woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Access-Control-Allow-Origin "*";
}
# 이미지 최적화 (ngx_http_image_filter_module)
location /images/ {
image_filter_buffer 10M;
# 썸네일 생성
location ~ /images/thumb/(.+)$ {
image_filter resize 300 300;
image_filter_jpeg_quality 85;
}
}
}
고급 설정
로깅 최적화
http {
# 로그 포맷 정의
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# JSON 로그 포맷
log_format json escape=json '{'
'"time": "$time_iso8601",'
'"remote_addr": "$remote_addr",'
'"request": "$request",'
'"status": $status,'
'"body_bytes_sent": $body_bytes_sent,'
'"request_time": $request_time,'
'"upstream_response_time": "$upstream_response_time",'
'"user_agent": "$http_user_agent"'
'}';
# 조건부 로깅 (헬스 체크 제외)
map $request_uri $loggable {
/health 0;
/metrics 0;
default 1;
}
server {
listen 80;
# 조건부 로깅
access_log /var/log/nginx/access.log main if=$loggable;
# JSON 로그
access_log /var/log/nginx/access.json.log json;
# 버퍼링 (성능 향상)
access_log /var/log/nginx/access.log main buffer=32k flush=5s;
}
}
리다이렉트 규칙
server {
listen 80;
server_name example.com www.example.com;
# HTTP → HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# www → non-www
if ($host = www.example.com) {
return 301 https://example.com$request_uri;
}
# 특정 경로 리다이렉트
location /old-page {
return 301 /new-page;
}
# 정규식 리다이렉트
location ~ ^/blog/(\d+)/(.+)$ {
return 301 /posts/$1/$2;
}
# 쿼리 스트링 유지
location /search {
return 301 /new-search$is_args$args;
}
}
커스텀 에러 페이지
server {
listen 80;
server_name example.com;
# 에러 페이지 정의
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /404.html {
root /var/www/errors;
internal;
}
location = /50x.html {
root /var/www/errors;
internal;
}
# 백엔드 에러 시 커스텀 응답
location /api/ {
proxy_pass http://backend;
proxy_intercept_errors on;
error_page 502 503 504 = @backend_error;
}
location @backend_error {
return 503 '{"error": "Service temporarily unavailable"}';
add_header Content-Type application/json;
}
}
Docker Compose로 Nginx 스택
version: '3.8'
services:
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./conf.d:/etc/nginx/conf.d:ro
- ./html:/usr/share/nginx/html:ro
- ./ssl:/etc/nginx/ssl:ro
- ./logs:/var/log/nginx
depends_on:
- app1
- app2
networks:
- webnet
restart: unless-stopped
app1:
image: node:18
working_dir: /app
volumes:
- ./app:/app
command: npm start
environment:
- PORT=3000
networks:
- webnet
app2:
image: node:18
working_dir: /app
volumes:
- ./app:/app
command: npm start
environment:
- PORT=3000
networks:
- webnet
networks:
webnet:
driver: bridge
volumes:
nginx-cache:
모니터링
Nginx 상태 모듈
# stub_status 모듈 활성화
server {
listen 80;
server_name localhost;
location /nginx_status {
stub_status;
# 로컬에서만 접근 허용
allow 127.0.0.1;
deny all;
}
}
# 상태 확인
curl http://localhost/nginx_status
# 출력:
# Active connections: 291
# server accepts handled requests
# 16630948 16630948 31070465
# Reading: 6 Writing: 179 Keeping: 106
Prometheus 메트릭
# nginx-prometheus-exporter 사용
# https://github.com/nginxinc/nginx-prometheus-exporter
# Docker로 실행
docker run -d \
--name nginx-exporter \
-p 9113:9113 \
nginx/nginx-prometheus-exporter:latest \
-nginx.scrape-uri=http://nginx/nginx_status
로그 분석
# 접근 로그 분석
# 1. 상위 IP 주소
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# 2. 상위 요청 URL
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# 3. HTTP 상태 코드 분포
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 4. 평균 응답 시간
awk '{sum+=$NF; count++} END {print sum/count}' /var/log/nginx/access.log
# 5. 5xx 에러 찾기
grep " 5[0-9][0-9] " /var/log/nginx/access.log
# GoAccess로 실시간 분석
goaccess /var/log/nginx/access.log -o /var/www/html/report.html --log-format=COMBINED
문제 해결
일반적인 문제
문제 1: 502 Bad Gateway
# 원인:
# - 백엔드 서버 다운
# - 타임아웃
# - 버퍼 크기 부족
# 해결:
location / {
proxy_pass http://backend;
# 타임아웃 증가
proxy_connect_timeout 75s;
proxy_read_timeout 300s;
# 버퍼 크기 증가
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
# 재시도
proxy_next_upstream error timeout http_502 http_503 http_504;
}
문제 2: 413 Request Entity Too Large
# 원인: 파일 업로드 크기 제한
# 해결:
http {
client_max_body_size 100M; # 전역 설정
}
server {
location /upload {
client_max_body_size 500M; # 특정 경로만
}
}
문제 3: 느린 응답
# 디버깅
# 1. 백엔드 응답 시간 확인
tail -f /var/log/nginx/access.log | grep -oP 'upstream_response_time: \K[\d.]+'
# 2. Nginx 설정 테스트
sudo nginx -t
# 3. 워커 프로세스 상태
ps aux | grep nginx
# 4. 연결 수 확인
netstat -an | grep :80 | wc -l
# 5. 에러 로그 확인
tail -f /var/log/nginx/error.log
베스트 프랙티스
설정 파일 구조
/etc/nginx/
├── nginx.conf # 메인 설정
├── conf.d/
│ ├── default.conf # 기본 서버
│ ├── api.conf # API 서버
│ └── static.conf # 정적 파일 서버
├── sites-available/ # 사용 가능한 사이트
│ ├── example.com
│ └── app.example.com
├── sites-enabled/ # 활성화된 사이트 (심볼릭 링크)
│ ├── example.com -> ../sites-available/example.com
│ └── app.example.com -> ../sites-available/app.example.com
├── snippets/ # 재사용 가능한 설정
│ ├── ssl-params.conf
│ ├── proxy-params.conf
│ └── security-headers.conf
└── modules-enabled/ # 활성화된 모듈
재사용 가능한 스니펫
# /etc/nginx/snippets/proxy-params.conf
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_http_version 1.1;
proxy_set_header Connection "";
# /etc/nginx/snippets/ssl-params.conf
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
# /etc/nginx/snippets/security-headers.conf
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# 사용
server {
listen 443 ssl http2;
server_name example.com;
include snippets/ssl-params.conf;
include snippets/security-headers.conf;
location / {
proxy_pass http://backend;
include snippets/proxy-params.conf;
}
}
환경별 설정
# 개발 환경
# /etc/nginx/conf.d/dev.conf
server {
listen 80;
server_name dev.example.com;
# 에러 표시 (개발 환경)
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# 디버그 로그
error_log /var/log/nginx/dev.error.log debug;
location / {
proxy_pass http://localhost:3000;
# CORS 허용 (개발 환경)
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type";
}
}
# 프로덕션 환경
# /etc/nginx/conf.d/prod.conf
server {
listen 443 ssl http2;
server_name example.com;
# 보안 강화
include snippets/ssl-params.conf;
include snippets/security-headers.conf;
# 에러 페이지 숨기기
error_page 500 502 503 504 /error.html;
location = /error.html {
root /var/www/errors;
internal;
}
# 에러 로그 (warn 레벨)
error_log /var/log/nginx/prod.error.log warn;
location / {
proxy_pass http://backend;
include snippets/proxy-params.conf;
}
}
Nginx Plus (상용 버전)
Nginx Plus 기능
# 능동적 헬스 체크
upstream backend {
zone backend 64k;
server backend1:8080;
server backend2:8080;
# 헬스 체크
health_check interval=5s fails=3 passes=2 uri=/health;
}
# 동적 업스트림 재설정 (API)
server {
listen 8080;
location /api {
api write=on;
allow 127.0.0.1;
deny all;
}
# 대시보드
location = /dashboard.html {
root /usr/share/nginx/html;
}
}
# 고급 캐싱
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=cache:10m
max_size=10g
inactive=60m
use_temp_path=off
purger=on; # Nginx Plus
성능 벤치마크
Apache Bench로 테스트
# 동시 연결 100개, 총 10,000 요청
ab -n 10000 -c 100 http://localhost/
# 결과 예시:
# Requests per second: 5000 [#/sec]
# Time per request: 20.0 [ms]
# Transfer rate: 1000 [Kbytes/sec]
# Keep-Alive 테스트
ab -n 10000 -c 100 -k http://localhost/
# POST 요청 테스트
ab -n 1000 -c 10 -p data.json -T application/json http://localhost/api/
wrk로 부하 테스트
# 12 스레드, 400 연결, 30초 동안
wrk -t12 -c400 -d30s http://localhost/
# Lua 스크립트로 복잡한 시나리오
wrk -t12 -c400 -d30s -s script.lua http://localhost/api/
# script.lua
wrk.method = "POST"
wrk.body = '{"key":"value"}'
wrk.headers["Content-Type"] = "application/json"
wrk.headers["Authorization"] = "Bearer token123"
정리
Nginx 사용 시나리오
flowchart TD
Start[웹 서비스 구축] --> Q1{용도는?}
Q1 -->|정적 파일| Static[✅ Nginx<br/>정적 파일 서버]
Q1 -->|API 서버| Q2{백엔드는?}
Q1 -->|마이크로서비스| Gateway[✅ Nginx<br/>API 게이트웨이]
Q2 -->|Node.js/Python| Proxy[✅ Nginx<br/>리버스 프록시]
Q2 -->|PHP| PHP[Nginx + PHP-FPM]
Static --> CDN{CDN 필요?}
CDN -->|Yes| CloudFlare[Nginx + CloudFlare]
CDN -->|No| Direct[Nginx 직접]
Proxy --> LB{로드 밸런싱?}
LB -->|Yes| Multiple[Nginx<br/>+ 여러 백엔드]
LB -->|No| Single[Nginx<br/>+ 단일 백엔드]
핵심 설정 요약
# 최소 프로덕션 설정
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
events {
worker_connections 4096;
use epoll;
}
http {
# 기본 설정
include mime.types;
default_type application/octet-stream;
# 성능
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
# 압축
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# 보안
server_tokens off;
client_max_body_size 10M;
# 로깅
access_log /var/log/nginx/access.log;
# 서버 블록
include /etc/nginx/conf.d/*.conf;
}
체크리스트
설치 후
-
nginx -t로 설정 검증 - 방화벽 포트 오픈 (80, 443)
- SELinux 설정 (CentOS/RHEL)
- 로그 디렉토리 권한 확인
보안
- SSL/TLS 설정
-
server_tokens off - Rate Limiting 설정
- 보안 헤더 추가
- IP 화이트리스트 (관리자 페이지)
성능
-
worker_processes auto -
worker_connections증가 - Gzip 압축 활성화
- 캐싱 설정
- Keep-Alive 활성화
모니터링
- 접근/에러 로그 설정
-
stub_status활성화 - 로그 로테이션 설정
- 모니터링 도구 연동
참고 자료
- Nginx 공식 문서
- Nginx 설정 생성기
- Mozilla SSL Configuration Generator
- Nginx Cookbook (O’Reilly)
- Nginx 성능 튜닝 가이드
한 줄 요약: Nginx는 이벤트 기반 비동기 아키텍처로 적은 리소스로 수만 개의 동시 연결을 처리할 수 있어, 정적 파일 서빙, 리버스 프록시, 로드 밸런싱에 최적화된 고성능 웹 서버입니다.