서론: 왜 Nginx인가

Nginx(엔진엑스)는 전 세계 웹 사이트의 약 34%가 사용하는 웹서버로, Apache와 함께 양대 산맥을 이루고 있습니다. 2004년 러시아 개발자 Igor Sysoev가 C10K 문제(동시 접속 1만 커넥션 처리)를 해결하기 위해 설계한 Nginx는, 이벤트 기반(event-driven) 비동기 아키텍처를 채택하여 적은 메모리로 대량의 동시 접속을 처리할 수 있습니다.

실무에서 Nginx는 단순한 웹서버를 넘어, 리버스 프록시, 로드밸런서, API Gateway, 캐시 서버 등 다양한 역할을 수행합니다. 특히 컨테이너 환경과 마이크로서비스 아키텍처가 보편화되면서 Nginx의 중요성은 더욱 커지고 있습니다.

이 가이드에서는 Nginx의 설치부터 실전 운영에 필요한 모든 설정을 체계적으로 다룹니다. 초보자도 따라할 수 있는 기본 설정부터, 실무에서 반드시 알아야 할 고급 설정까지 실제 설정 파일 예제와 함께 상세히 설명합니다.

1. Nginx 설치

1.1 Ubuntu/Debian 계열 설치

# 패키지 목록 업데이트 후 설치
sudo apt update
sudo apt install nginx -y

# 버전 확인
nginx -v

# 서비스 시작 및 자동 시작 등록
sudo systemctl start nginx
sudo systemctl enable nginx

# 상태 확인
sudo systemctl status nginx

1.2 RHEL/CentOS/Rocky Linux 계열 설치

# EPEL 저장소 추가 (CentOS 7)
sudo yum install epel-release -y
sudo yum install nginx -y

# Rocky Linux 9 / RHEL 9
sudo dnf install nginx -y

# 서비스 시작 및 자동 시작 등록
sudo systemctl start nginx
sudo systemctl enable nginx

# 방화벽 허용
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

1.3 공식 Nginx 저장소에서 최신 버전 설치

배포판 기본 저장소의 Nginx는 버전이 오래된 경우가 많습니다. 최신 기능이 필요하다면 공식 저장소를 추가합니다:

# Ubuntu - Nginx 공식 저장소 추가
sudo apt install curl gnupg2 ca-certificates lsb-release -y

# 서명 키 추가
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo gpg --dearmor -o /usr/share/keyrings/nginx-archive-keyring.gpg

# 저장소 추가 (stable)
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 -y
TIP: 공식 저장소의 Nginx는 mainline(최신 기능, 홀수 버전)과 stable(안정판, 짝수 버전) 두 가지 트랙이 있습니다. 운영 환경에서는 stable을 권장합니다.

1.4 설치 확인

# 브라우저에서 확인: http://서버IP
# 또는 커맨드라인으로 확인
curl -I http://localhost

# 설치 경로 확인
nginx -V    # 컴파일 옵션과 모듈 확인
nginx -t    # 설정 문법 검증

2. Nginx 디렉토리 구조와 설정 파일

2.1 핵심 디렉토리 구조

경로 설명
/etc/nginx/ 메인 설정 디렉토리
/etc/nginx/nginx.conf 메인 설정 파일 (모든 설정의 시작점)
/etc/nginx/conf.d/ 추가 설정 파일 (*.conf 자동 로드)
/etc/nginx/sites-available/ 사용 가능한 사이트 설정 (Ubuntu)
/etc/nginx/sites-enabled/ 활성화된 사이트 심볼릭 링크 (Ubuntu)
/etc/nginx/mime.types MIME 타입 매핑
/var/log/nginx/access.log 접속 로그
/var/log/nginx/error.log 에러 로그
/var/www/html/ 기본 웹 루트 디렉토리
/usr/share/nginx/html/ 기본 웹 루트 (RHEL 계열)

2.2 nginx.conf 기본 구조

Nginx 설정은 블록(block) 구조로 이루어져 있으며, 계층적으로 중첩됩니다:

# === 전역 컨텍스트 (Main Context) ===
user nginx;                          # 워커 프로세스 실행 사용자
worker_processes auto;               # 워커 프로세스 수 (auto = CPU 코어 수)
error_log /var/log/nginx/error.log warn;  # 에러 로그 경로 및 레벨
pid /run/nginx.pid;                  # PID 파일 경로

# === 이벤트 컨텍스트 ===
events {
    worker_connections 1024;         # 워커당 최대 동시 연결 수
    use epoll;                       # 이벤트 처리 방식 (Linux)
    multi_accept on;                 # 한 번에 여러 연결 수락
}

# === 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"';

    access_log /var/log/nginx/access.log main;

    sendfile on;                     # 커널 레벨 파일 전송
    tcp_nopush on;                   # sendfile과 함께 사용
    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;
    include /etc/nginx/sites-enabled/*;  # Ubuntu 방식
}
주의: 설정을 변경한 후에는 반드시 nginx -t로 문법을 검증하고, sudo systemctl reload nginx로 적용하세요. restart 대신 reload를 사용하면 서비스 중단 없이 설정을 반영할 수 있습니다.

3. Server Block (가상 호스트) 설정

Server Block은 Apache의 VirtualHost에 해당하며, 하나의 Nginx 서버에서 여러 도메인(사이트)을 운영할 수 있게 합니다.

3.1 기본 정적 웹사이트

# /etc/nginx/conf.d/example.com.conf
server {
    listen 80;                       # IPv4 포트
    listen [::]:80;                  # IPv6 포트
    server_name example.com www.example.com;

    root /var/www/example.com/html;  # 문서 루트
    index index.html index.htm;      # 기본 인덱스 파일

    # 기본 location
    location / {
        try_files $uri $uri/ =404;   # 파일 → 디렉토리 → 404
    }

    # 에러 페이지 커스터마이징
    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }

    # 로그 설정 (사이트별 분리)
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;
}
# 웹 루트 디렉토리 생성
sudo mkdir -p /var/www/example.com/html
sudo chown -R $USER:$USER /var/www/example.com/html

# 테스트 페이지 생성
echo "<h1>Welcome to example.com</h1>" > /var/www/example.com/html/index.html

# 설정 검증 및 적용
sudo nginx -t
sudo systemctl reload nginx

3.2 Ubuntu의 sites-available / sites-enabled 패턴

# 설정 파일 생성
sudo nano /etc/nginx/sites-available/example.com

# 심볼릭 링크로 활성화
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

# 기본 사이트 비활성화 (필요 시)
sudo rm /etc/nginx/sites-enabled/default

# 적용
sudo nginx -t
sudo systemctl reload nginx

3.3 여러 도메인 운영 (멀티 사이트)

# 사이트 A: /etc/nginx/conf.d/site-a.conf
server {
    listen 80;
    server_name site-a.com www.site-a.com;
    root /var/www/site-a/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

# 사이트 B: /etc/nginx/conf.d/site-b.conf
server {
    listen 80;
    server_name site-b.com www.site-b.com;
    root /var/www/site-b/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

# 기본 서버 (매칭 안 되는 요청 처리)
server {
    listen 80 default_server;
    server_name _;
    return 444;    # 연결 종료 (응답 없이)
}

4. Location 블록 - URL 매칭의 핵심

location 블록은 Nginx 설정에서 가장 자주 사용되고 가장 중요한 디렉티브입니다. URL 경로에 따라 서로 다른 처리를 할 수 있습니다.

4.1 매칭 방식과 우선순위

문법 매칭 방식 우선순위
= /path 정확히 일치 (Exact Match) 1순위 (최우선)
^~ /path 접두사 일치 (정규식 검색 안 함) 2순위
~ /regex 정규표현식 (대소문자 구분) 3순위
~* /regex 정규표현식 (대소문자 무시) 3순위
/path 접두사 일치 (가장 긴 매칭) 4순위 (기본)

4.2 실전 Location 예제

server {
    listen 80;
    server_name example.com;
    root /var/www/example.com;

    # 정확히 루트 경로만 매칭
    location = / {
        # 메인 페이지 전용 처리
        try_files /index.html =404;
    }

    # /api/ 로 시작하는 경로 → 백엔드 프록시
    location /api/ {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 정적 파일 (이미지, CSS, JS)
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2?)$ {
        expires 30d;                 # 30일 캐시
        add_header Cache-Control "public, immutable";
        access_log off;              # 정적 파일 로그 비활성화
    }

    # PHP 파일 처리
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # 숨김 파일 접근 차단 (.htaccess, .git 등)
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    # favicon.ico 404 로그 방지
    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    # robots.txt
    location = /robots.txt {
        log_not_found off;
        access_log off;
    }
}
TIP: location 블록의 우선순위를 기억하세요: =(정확) → ^~(접두사 우선) → ~/~*(정규식) → 일반 접두사. 정확한 이해가 없으면 예상치 못한 라우팅 문제가 발생할 수 있습니다.

5. 리버스 프록시 설정

리버스 프록시는 Nginx의 가장 강력한 기능 중 하나입니다. 클라이언트의 요청을 백엔드 서버(Node.js, Python, Java 등)로 전달하고 응답을 반환합니다.

5.1 기본 리버스 프록시

# Node.js 앱 프록시 (포트 3000)
server {
    listen 80;
    server_name app.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;

        # 타임아웃 설정
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # 버퍼 설정
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
    }
}

5.2 WebSocket 프록시

# WebSocket 지원이 필요한 경우 (채팅, 실시간 앱)
server {
    listen 80;
    server_name ws.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;

        # WebSocket 업그레이드 헤더
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # WebSocket 연결 유지 시간
        proxy_read_timeout 86400s;   # 24시간
        proxy_send_timeout 86400s;
    }
}

5.3 경로별 다중 백엔드 프록시

# 마이크로서비스 라우팅
server {
    listen 80;
    server_name example.com;

    # 프론트엔드 (React/Vue SPA)
    location / {
        root /var/www/frontend/dist;
        try_files $uri $uri/ /index.html;  # SPA 라우팅
    }

    # API 서버 (Node.js)
    location /api/ {
        proxy_pass http://127.0.0.1:3000/;    # 마지막 / 중요!
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 인증 서버 (Python)
    location /auth/ {
        proxy_pass http://127.0.0.1:5000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 파일 업로드 서버 (별도 크기 제한)
    location /upload/ {
        client_max_body_size 100M;   # 업로드 최대 100MB
        proxy_pass http://127.0.0.1:4000/;
        proxy_set_header Host $host;
    }
}
주의: proxy_pass URL의 마지막 슬래시(/)에 주의하세요. proxy_pass http://backend/;는 location 경로를 제거하고 전달하며, proxy_pass http://backend;(슬래시 없음)는 location 경로를 포함하여 전달합니다.

6. 로드밸런싱

여러 백엔드 서버에 트래픽을 분산하여 고가용성과 성능을 확보합니다.

6.1 로드밸런싱 방식

# === Round Robin (기본) ===
# 순서대로 번갈아가며 요청 분배
upstream backend_rr {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

# === Weighted Round Robin ===
# 서버 성능에 따라 가중치 부여
upstream backend_weighted {
    server 192.168.1.10:8080 weight=5;    # 5배 더 많은 요청
    server 192.168.1.11:8080 weight=3;
    server 192.168.1.12:8080 weight=1;
}

# === Least Connections ===
# 가장 연결이 적은 서버에 분배
upstream backend_lc {
    least_conn;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

# === IP Hash ===
# 같은 클라이언트 IP는 항상 같은 서버로 (세션 유지)
upstream backend_hash {
    ip_hash;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

# 로드밸런서 적용
server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend_rr;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

6.2 헬스체크와 장애 대응

upstream backend {
    server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:8080 backup;     # 백업 서버 (다른 서버 모두 실패 시 사용)
    server 192.168.1.13:8080 down;       # 수동으로 비활성화

    # max_fails=3: 3번 실패하면 해당 서버를 비활성화
    # fail_timeout=30s: 30초 후 다시 시도
    # backup: 다른 모든 서버가 다운될 때만 사용
    # down: 영구적으로 비활성화 (유지보수용)
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;
        proxy_next_upstream error timeout http_500 http_502 http_503;
        proxy_next_upstream_tries 3;        # 최대 3번 다른 서버 시도
        proxy_next_upstream_timeout 10s;    # 타임아웃
    }
}

7. HTTPS / SSL 설정

7.1 Let's Encrypt 무료 인증서 발급

# Certbot 설치
sudo apt install certbot python3-certbot-nginx -y    # Ubuntu
sudo dnf install certbot python3-certbot-nginx -y    # Rocky/RHEL

# 인증서 발급 (Nginx 자동 설정)
sudo certbot --nginx -d example.com -d www.example.com

# 인증서 자동 갱신 테스트
sudo certbot renew --dry-run

# 자동 갱신 크론 확인
sudo systemctl status certbot.timer

7.2 SSL 수동 설정 (최적화 포함)

# /etc/nginx/conf.d/example.com.conf
server {
    listen 80;
    server_name example.com www.example.com;
    # HTTP → HTTPS 리다이렉트
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    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 프로토콜 (TLS 1.2 이상만 허용)
    ssl_protocols TLSv1.2 TLSv1.3;
    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 1d;
    ssl_session_tickets off;

    # OCSP Stapling (인증서 검증 속도 향상)
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;

    # 보안 헤더
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-XSS-Protection "1; mode=block" always;

    root /var/www/example.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}
TIP: Mozilla SSL Configuration Generator를 사용하면 서버 환경에 맞는 최적의 SSL 설정을 자동으로 생성할 수 있습니다.

8. 보안 설정

8.1 기본 보안 강화

# /etc/nginx/conf.d/security.conf (공통 보안 설정)

# Nginx 버전 정보 숨기기
server_tokens off;

# 요청 크기 제한 (기본 1MB)
client_max_body_size 10M;

# 요청 헤더/바디 타임아웃
client_header_timeout 10s;
client_body_timeout 10s;
send_timeout 10s;

# 버퍼 오버플로우 공격 방지
client_body_buffer_size 1K;
client_header_buffer_size 1k;
large_client_header_buffers 2 1k;

8.2 IP 기반 접근 제어

# 관리자 페이지 IP 제한
location /admin/ {
    allow 10.0.0.0/8;           # 내부 네트워크 허용
    allow 192.168.1.100;         # 특정 IP 허용
    deny all;                    # 나머지 차단

    proxy_pass http://127.0.0.1:8080;
}

# 특정 국가/IP 대역 차단
# geo 모듈 활용
geo $blocked_ip {
    default 0;
    1.2.3.0/24 1;               # 차단할 IP 대역
    5.6.7.0/24 1;
}

server {
    if ($blocked_ip) {
        return 403;
    }
}

8.3 Rate Limiting (요청 제한)

# http 블록에서 zone 정의
http {
    # IP당 초당 10개 요청으로 제한
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

    # IP당 동시 연결 수 제한
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
}

# server 블록에서 적용
server {
    # API에 Rate Limit 적용
    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        # burst=20: 순간 20개까지 허용
        # nodelay: 초과분을 즉시 처리 (대기하지 않음)

        limit_req_status 429;    # 제한 초과 시 429 응답

        proxy_pass http://backend;
    }

    # 로그인 페이지 (더 엄격한 제한)
    location /login {
        limit_req zone=api_limit burst=5;
        limit_conn conn_limit 5;  # 동시 5개 연결로 제한

        proxy_pass http://backend;
    }
}

8.4 봇/스크래퍼 차단

# 악성 User-Agent 차단
if ($http_user_agent ~* (scrapy|curl|wget|python-requests|httpclient|Go-http-client)) {
    return 403;
}

# 빈 User-Agent 차단
if ($http_user_agent = "") {
    return 403;
}

# 특정 리퍼러 차단 (핫링크 방지)
location ~* \.(jpg|jpeg|png|gif|webp)$ {
    valid_referers none blocked server_names *.example.com;
    if ($invalid_referer) {
        return 403;
    }
}

9. 성능 최적화

9.1 Gzip 압축

http {
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;           # 압축 레벨 (1-9, 6 권장)
    gzip_min_length 256;         # 256바이트 이상만 압축
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml
        application/rss+xml
        application/atom+xml
        image/svg+xml
        font/woff
        font/woff2;
}

9.2 정적 파일 캐싱

# 정적 리소스 브라우저 캐싱
location ~* \.(jpg|jpeg|png|gif|ico|webp|avif)$ {
    expires 365d;
    add_header Cache-Control "public, immutable";
    access_log off;
}

location ~* \.(css|js)$ {
    expires 30d;
    add_header Cache-Control "public";
    access_log off;
}

location ~* \.(woff|woff2|ttf|otf|eot)$ {
    expires 365d;
    add_header Cache-Control "public, immutable";
    access_log off;
    add_header Access-Control-Allow-Origin "*";  # CORS (폰트)
}

location ~* \.(html|htm)$ {
    expires 1h;
    add_header Cache-Control "public, must-revalidate";
}

9.3 프록시 캐싱

http {
    # 프록시 캐시 영역 정의
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m
                     max_size=1g inactive=60m use_temp_path=off;

    server {
        location / {
            proxy_pass http://backend;

            # 캐시 활성화
            proxy_cache my_cache;
            proxy_cache_valid 200 302 10m;   # 200, 302 → 10분 캐시
            proxy_cache_valid 404 1m;        # 404 → 1분 캐시

            # 캐시 상태 헤더 (디버깅용)
            add_header X-Cache-Status $upstream_cache_status;

            # 캐시 바이패스 조건
            proxy_cache_bypass $http_cache_control;
            proxy_no_cache $http_pragma;
        }
    }
}

9.4 Worker 프로세스 튜닝

# CPU 코어 수에 맞게 설정
worker_processes auto;          # 자동 감지 (권장)

events {
    worker_connections 4096;    # 서버 규모에 따라 조정
    use epoll;                  # Linux 최적 이벤트 모델
    multi_accept on;
}

http {
    # 파일 디스크립터 캐싱
    open_file_cache max=10000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
}
# OS 레벨 튜닝 (커널 파라미터)
# /etc/sysctl.conf에 추가
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1

# 워커 프로세스의 파일 디스크립터 제한
# /etc/security/limits.conf
# nginx soft nofile 65535
# nginx hard nofile 65535

# 또는 nginx.conf의 전역 컨텍스트에서:
worker_rlimit_nofile 65535;

10. 로그 설정과 모니터링

10.1 커스텀 로그 형식

http {
    # 상세 로그 형식 (응답시간 포함)
    log_format detailed '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent" '
                        '$request_time $upstream_response_time';

    # JSON 형식 로그 (ELK/Loki 연동에 유용)
    log_format json_log 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",'
        '"http_referer":"$http_referer",'
        '"http_user_agent":"$http_user_agent"'
    '}';

    # 조건부 로그 (2xx 응답은 로그하지 않기)
    map $status $loggable {
        ~^[23] 0;
        default 1;
    }

    server {
        access_log /var/log/nginx/access.log detailed;
        # access_log /var/log/nginx/access.json json_log if=$loggable;
    }
}

10.2 로그 로테이션

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
    endscript
}

10.3 Nginx 상태 모니터링 (stub_status)

# 내부에서만 접근 가능한 상태 페이지
server {
    listen 8080;
    server_name localhost;

    location /nginx_status {
        stub_status on;
        allow 127.0.0.1;
        allow 10.0.0.0/8;
        deny all;
    }
}

# 출력 예시:
# Active connections: 291
# server accepts handled requests
#  16630948 16630948 31070465
# Reading: 6 Writing: 179 Waiting: 106
# 상태 확인
curl http://localhost:8080/nginx_status

# Prometheus 연동 시 nginx-prometheus-exporter 사용
# docker run -p 9113:9113 nginx/nginx-prometheus-exporter -nginx.scrape-uri=http://host:8080/nginx_status

11. 실전 운영 팁

11.1 자주 쓰는 명령어 모음

# 설정 문법 검증 (수정 후 반드시 실행!)
sudo nginx -t

# 서비스 중단 없이 설정 적용
sudo systemctl reload nginx

# 서비스 재시작 (중단됨)
sudo systemctl restart nginx

# 로그 실시간 모니터링
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log

# 특정 상태코드만 필터
tail -f /var/log/nginx/access.log | grep --line-buffered '" 500 '

# 연결 상태 확인
ss -tlnp | grep nginx

# 현재 활성 연결 수
ss -s | head -5

# Nginx 마스터/워커 프로세스 확인
ps aux | grep nginx

# 설정 파일 경로 및 모듈 확인
nginx -V 2>&1 | tr ' ' '\n' | grep -E "^--"

11.2 무중단 설정 변경 절차

# 1. 설정 파일 수정
sudo nano /etc/nginx/conf.d/example.com.conf

# 2. 문법 검증 (필수!)
sudo nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# 3. reload로 적용 (다운타임 없음)
sudo systemctl reload nginx

# 만약 문법 오류가 있다면:
# nginx: [emerg] unknown directive "servr" in /etc/nginx/conf.d/example.com.conf:2
# nginx: configuration file /etc/nginx/nginx.conf test failed
# → reload하지 말고 오류를 수정!

11.3 흔한 실수와 해결법

증상 원인 해결
502 Bad Gateway 백엔드 서버 다운 또는 소켓 오류 백엔드 프로세스 확인, proxy_pass 주소 확인
504 Gateway Timeout 백엔드 응답 지연 proxy_read_timeout 값 증가
413 Request Entity Too Large 업로드 크기 초과 client_max_body_size 값 증가
403 Forbidden 파일 권한 또는 SELinux 파일 소유자/권한 확인, SELinux 컨텍스트 확인
301 리다이렉트 루프 HTTP↔HTTPS 설정 충돌 server 블록 분리, $scheme 조건 확인
실시간 로그가 안 나옴 access_log off; 또는 버퍼링 location 블록의 로그 설정 확인

12. 설정 파일 체크리스트

운영 서버에 Nginx를 배포하기 전에 다음 항목을 반드시 점검하세요:

  • 기본 보안: server_tokens off; 설정 여부
  • HTTPS: 모든 HTTP 트래픽이 HTTPS로 리다이렉트되는지
  • SSL 프로토콜: TLS 1.2 이상만 허용하는지
  • 보안 헤더: HSTS, X-Frame-Options, X-Content-Type-Options 설정
  • 파일 접근: .git, .env 등 숨김 파일 차단 여부
  • 업로드 제한: client_max_body_size 적절한 값 설정
  • Rate Limiting: API, 로그인 등 민감한 경로에 제한 설정
  • Gzip 압축: 텍스트 기반 응답에 압축 활성화
  • 캐싱: 정적 파일에 적절한 expires 설정
  • 로그: 사이트별 로그 분리 및 로테이션 설정
  • Default Server: 매칭되지 않는 요청 처리 (return 444;)
  • 타임아웃: proxy_read_timeout 등 적절한 타임아웃 설정
  • 백업: 설정 파일의 백업 및 버전 관리 (Git 추천)

결론: Nginx는 설정이 곧 실력이다

Nginx는 설치는 쉽지만 제대로 운영하려면 설정에 대한 깊은 이해가 필요합니다. 이 가이드에서 다룬 내용은 실무에서 가장 자주 마주치는 시나리오를 기반으로 구성했습니다.

핵심 포인트를 정리하면 다음과 같습니다:

  • 기본기부터 탄탄히 - nginx.conf의 블록 구조와 상속 관계를 이해하세요.
  • location 우선순위를 외워라 - =^~~/~* → 일반 접두사. 이것만 알아도 대부분의 라우팅 문제를 해결할 수 있습니다.
  • 변경 전 반드시 nginx -t - 설정 오류는 서비스 장애로 직결됩니다. 문법 검증은 선택이 아닌 필수입니다.
  • reload ≠ restart - reload는 무중단 반영, restart는 서비스 중단이 발생합니다.
  • 보안은 처음부터 - HTTPS, 보안 헤더, Rate Limiting은 운영 시작 전에 설정하세요.
  • 로그는 자산이다 - 커스텀 로그 형식을 설정하고, 정기적으로 분석하면 장애 예방과 성능 개선에 큰 도움이 됩니다.

이 가이드의 설정 예제를 자신의 환경에 맞게 수정하여 적용해 보세요. 실전 경험이 쌓일수록 Nginx 설정은 점점 자연스러워지고, 복잡한 아키텍처도 자신 있게 구성할 수 있게 될 것입니다.