Nginx Configuration - RumenDamyanov/js-chess GitHub Wiki

Nginx Configuration Guide

Comprehensive guide for configuring Nginx as a reverse proxy, load balancer, and web server for the chess showcase application.

Overview

This guide covers:

  • Basic Nginx setup and configuration
  • Reverse proxy configuration for Node.js applications
  • SSL/TLS termination and certificate management
  • Load balancing across multiple instances
  • Static file serving and caching
  • WebSocket proxy configuration
  • Security hardening and rate limiting
  • Performance optimization
  • Monitoring and logging

Basic Nginx Installation

Installation on Different Platforms

# Ubuntu/Debian
sudo apt update
sudo apt install nginx

# CentOS/RHEL
sudo yum install epel-release
sudo yum install nginx

# macOS (for development)
brew install nginx

# Start and enable Nginx
sudo systemctl start nginx
sudo systemctl enable nginx

# Check status
sudo systemctl status nginx

Directory Structure

# Main configuration files
/etc/nginx/
├── nginx.conf                 # Main configuration file
├── sites-available/           # Available site configurations
├── sites-enabled/             # Enabled site configurations (symlinks)
├── conf.d/                    # Additional configuration files
├── ssl/                       # SSL certificates and keys
├── snippets/                  # Reusable configuration snippets
└── modules-enabled/           # Enabled modules

# Log files
/var/log/nginx/
├── access.log                 # Access logs
├── error.log                  # Error logs
└── chess-showcase.log         # Application-specific logs

# Web root
/var/www/html/                 # Default web root

Main Configuration

nginx.conf

# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /run/nginx.pid;

# Load dynamic modules
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}

http {
    # Basic Settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 1000;
    types_hash_max_size 2048;
    server_tokens off;

    # MIME types
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging Format
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for" '
                    'rt=$request_time uct="$upstream_connect_time" '
                    'uht="$upstream_header_time" urt="$upstream_response_time"';

    log_format chess_detailed '$remote_addr - $remote_user [$time_local] '
                              '"$request" $status $body_bytes_sent '
                              '"$http_referer" "$http_user_agent" '
                              '"$http_x_forwarded_for" "$http_x_real_ip" '
                              'rt=$request_time uct="$upstream_connect_time" '
                              'uht="$upstream_header_time" urt="$upstream_response_time" '
                              'game_id="$arg_gameId" player_id="$cookie_playerId"';

    # Logging
    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;

    # Gzip Compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;

    # Brotli Compression (if module available)
    # brotli on;
    # brotli_comp_level 6;
    # brotli_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;

    # Rate Limiting
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=websocket:10m rate=5r/s;
    limit_req_zone $binary_remote_addr zone=static:10m rate=50r/s;

    # Connection Limiting
    limit_conn_zone $binary_remote_addr zone=addr:10m;

    # Map for WebSocket upgrade
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    # Upstream definitions
    include /etc/nginx/conf.d/upstreams.conf;

    # Virtual Host Configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Upstream Configuration

# /etc/nginx/conf.d/upstreams.conf

# Main application servers
upstream chess_app {
    least_conn;
    server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3002 max_fails=3 fail_timeout=30s;

    # Health check (requires nginx-plus or custom module)
    # health_check interval=30s fails=3 passes=2;

    keepalive 32;
}

# WebSocket servers
upstream chess_websocket {
    ip_hash; # Ensure sticky sessions for WebSocket
    server 127.0.0.1:3010 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3011 max_fails=3 fail_timeout=30s;

    keepalive 16;
}

# API servers (if separate from main app)
upstream chess_api {
    least_conn;
    server 127.0.0.1:4000 weight=3 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:4001 weight=3 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:4002 weight=1 max_fails=3 fail_timeout=30s backup;

    keepalive 32;
}

Site Configuration

Production Configuration

# /etc/nginx/sites-available/chess-showcase
server {
    listen 80;
    server_name chessgame.com www.chessgame.com;

    # Redirect all HTTP to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name chessgame.com www.chessgame.com;

    # SSL Configuration
    ssl_certificate /etc/nginx/ssl/chessgame.com.crt;
    ssl_certificate_key /etc/nginx/ssl/chessgame.com.key;
    ssl_trusted_certificate /etc/nginx/ssl/ca-bundle.crt;

    # Include SSL security settings
    include /etc/nginx/snippets/ssl-params.conf;
    include /etc/nginx/snippets/security-headers.conf;

    # Logging
    access_log /var/log/nginx/chess-showcase.log chess_detailed;
    error_log /var/log/nginx/chess-showcase-error.log;

    # Basic settings
    root /var/www/chess-showcase;
    index index.html;

    # Connection limits
    limit_conn addr 10;

    # Security settings
    client_max_body_size 10M;
    client_body_timeout 60s;
    client_header_timeout 60s;

    # Static files with caching
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header X-Content-Type-Options nosniff;

        # CORS for fonts and assets
        add_header Access-Control-Allow-Origin *;

        # Try static files first, then fallback to app
        try_files $uri @app;

        # Rate limiting for static files
        limit_req zone=static burst=20 nodelay;

        # Enable gzip for text-based assets
        gzip_static on;
    }

    # API endpoints
    location /api/ {
        # Rate limiting for API
        limit_req zone=api burst=20 nodelay;

        # Proxy headers
        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_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        # Proxy timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Proxy buffering
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
        proxy_busy_buffers_size 8k;

        # Pass to application
        proxy_pass http://chess_app;

        # Health check exclusion
        if ($request_uri ~ "^/api/health") {
            access_log off;
        }
    }

    # WebSocket connections
    location /socket.io/ {
        # Rate limiting for WebSocket
        limit_req zone=websocket burst=10 nodelay;

        # WebSocket headers
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $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;

        # WebSocket timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 3600s; # Long timeout for persistent connections

        # Disable buffering for real-time communication
        proxy_buffering off;

        # Pass to WebSocket servers
        proxy_pass http://chess_websocket;
    }

    # Framework-specific routes
    location /react/ {
        alias /var/www/chess-showcase/react/;
        try_files $uri $uri/ /react/index.html;

        # SPA routing support
        location ~ ^/react/(?!static/).* {
            try_files $uri /react/index.html;
        }
    }

    location /vue/ {
        alias /var/www/chess-showcase/vue/;
        try_files $uri $uri/ /vue/index.html;

        location ~ ^/vue/(?!static/).* {
            try_files $uri /vue/index.html;
        }
    }

    location /angular/ {
        alias /var/www/chess-showcase/angular/;
        try_files $uri $uri/ /angular/index.html;

        location ~ ^/angular/(?!static/).* {
            try_files $uri /angular/index.html;
        }
    }

    location /vanilla/ {
        alias /var/www/chess-showcase/vanilla/;
        try_files $uri $uri/ /vanilla/index.html;
    }

    # Main application (default framework)
    location / {
        try_files $uri $uri/ @app;
    }

    # Application fallback
    location @app {
        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_pass http://chess_app;
    }

    # Health check endpoint (no rate limiting)
    location = /health {
        access_log off;
        proxy_pass http://chess_app;
    }

    # Block access to sensitive files
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    location ~ \.(env|log|sql)$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    # Custom error pages
    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;

    location = /404.html {
        root /var/www/chess-showcase/errors;
        internal;
    }

    location = /50x.html {
        root /var/www/chess-showcase/errors;
        internal;
    }
}

Development Configuration

# /etc/nginx/sites-available/chess-showcase-dev
server {
    listen 80;
    server_name localhost dev.chessgame.local;

    access_log /var/log/nginx/chess-dev.log main;
    error_log /var/log/nginx/chess-dev-error.log debug;

    # Allow larger uploads in development
    client_max_body_size 50M;

    # Development static files (no caching)
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires -1;
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";

        # CORS for development
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
        add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range";

        try_files $uri @dev_app;
    }

    # API with CORS
    location /api/ {
        # CORS preflight
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
            add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";
            add_header Access-Control-Max-Age 1728000;
            add_header Content-Type 'text/plain charset=UTF-8';
            add_header Content-Length 0;
            return 204;
        }

        # CORS headers
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Credentials true;

        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_pass http://127.0.0.1:3000;
    }

    # WebSocket for development
    location /socket.io/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $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_pass http://127.0.0.1:3001;
    }

    # Development servers for each framework
    location /react-dev/ {
        proxy_pass http://127.0.0.1:3010/;
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }

    location /vue-dev/ {
        proxy_pass http://127.0.0.1:3020/;
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }

    location /angular-dev/ {
        proxy_pass http://127.0.0.1:3030/;
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }

    # Default to main dev server
    location / {
        try_files $uri @dev_app;
    }

    location @dev_app {
        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;
    }
}

Security Configuration

SSL/TLS Settings

# /etc/nginx/snippets/ssl-params.conf

# SSL Protocol and Ciphers
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 Session Settings
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;
resolver_timeout 5s;

# DH Parameters
ssl_dhparam /etc/nginx/ssl/dhparam.pem;

# SSL Buffer Size
ssl_buffer_size 4k;

Security Headers

# /etc/nginx/snippets/security-headers.conf

# Strict Transport Security
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# Content Security Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://unpkg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' wss: ws:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;

# Other Security Headers
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 "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

# Remove server signature
server_tokens off;
more_clear_headers Server;

Rate Limiting Configuration

# /etc/nginx/conf.d/rate-limiting.conf

# Define rate limiting zones
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=signup:10m rate=2r/m;
limit_req_zone $binary_remote_addr zone=api_strict:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=api_general:10m rate=100r/m;

# Connection limiting
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;

# Define map for rate limiting bypass
map $http_user_agent $limit_key {
    ~*bot 0;
    ~*crawler 0;
    default $binary_remote_addr;
}

# Rate limiting for specific endpoints
map $request_uri $api_limit_zone {
    ~*/api/auth/login login;
    ~*/api/auth/signup signup;
    ~*/api/games/create api_strict;
    ~*/api/ api_general;
    default "";
}

Load Balancing

Advanced Upstream Configuration

# /etc/nginx/conf.d/advanced-upstreams.conf

# Main application with health checks
upstream chess_app_primary {
    # Load balancing method
    least_conn;

    # Primary servers
    server app1.internal:3000 weight=3 max_fails=2 fail_timeout=30s;
    server app2.internal:3000 weight=3 max_fails=2 fail_timeout=30s;
    server app3.internal:3000 weight=2 max_fails=2 fail_timeout=30s;

    # Backup server
    server app-backup.internal:3000 backup;

    # Keep-alive connections
    keepalive 32;
    keepalive_requests 100;
    keepalive_timeout 60s;
}

# Geographic load balancing
upstream chess_app_us {
    server us-east-app1.internal:3000;
    server us-east-app2.internal:3000;
    server us-west-app1.internal:3000;
}

upstream chess_app_eu {
    server eu-west-app1.internal:3000;
    server eu-west-app2.internal:3000;
}

# Map for geographic routing
map $geoip_country_code $backend_pool {
    default chess_app_primary;
    US chess_app_us;
    CA chess_app_us;
    GB chess_app_eu;
    DE chess_app_eu;
    FR chess_app_eu;
}

Health Checks

# Health check configuration (requires nginx-plus)
location /health-check {
    access_log off;
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;

    proxy_pass http://chess_app;
    proxy_connect_timeout 3s;
    proxy_read_timeout 3s;

    # Custom health check
    proxy_set_header Host $host;
    proxy_set_header User-Agent "nginx-health-check";
}

# Simple health check for open source nginx
location /nginx-health {
    access_log off;
    return 200 "healthy\n";
    add_header Content-Type text/plain;
}

Caching Configuration

Static File Caching

# /etc/nginx/conf.d/caching.conf

# Proxy cache path
proxy_cache_path /var/cache/nginx/chess
                 levels=1:2
                 keys_zone=chess_cache:10m
                 max_size=1g
                 inactive=60m
                 use_temp_path=off;

# FastCGI cache path (if using PHP)
fastcgi_cache_path /var/cache/nginx/fastcgi
                   levels=1:2
                   keys_zone=chess_fastcgi:10m
                   max_size=1g
                   inactive=60m;

# Cache configuration for different content types
map $sent_http_content_type $expires {
    default                    off;
    text/html                  5m;
    text/css                   1y;
    application/javascript     1y;
    ~image/                    1M;
    ~font/                     1y;
    application/pdf            1M;
    application/json           1h;
}

# Proxy cache configuration
server {
    # ... other configuration ...

    location /api/games/ {
        # Cache GET requests only
        proxy_cache chess_cache;
        proxy_cache_methods GET HEAD;
        proxy_cache_key $scheme$proxy_host$request_uri$is_args$args;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;

        # Cache headers
        add_header X-Cache-Status $upstream_cache_status;
        proxy_cache_bypass $http_pragma $http_authorization;
        proxy_no_cache $http_pragma $http_authorization;

        # Only cache for anonymous users
        set $no_cache 0;
        if ($http_authorization) {
            set $no_cache 1;
        }

        proxy_cache_bypass $no_cache;
        proxy_no_cache $no_cache;

        proxy_pass http://chess_app;
    }
}

Microcaching

# Microcaching for dynamic content
location / {
    proxy_cache chess_cache;
    proxy_cache_valid 200 1s;
    proxy_cache_key $scheme$proxy_host$request_uri$is_args$args$http_user_agent;

    # Add cache status header
    add_header X-Cache-Status $upstream_cache_status;

    # Bypass cache for logged-in users
    set $skip_cache 0;
    if ($http_cookie ~* "logged_in") {
        set $skip_cache 1;
    }

    proxy_cache_bypass $skip_cache;
    proxy_no_cache $skip_cache;

    proxy_pass http://chess_app;
}

WebSocket Configuration

Enhanced WebSocket Proxy

# WebSocket-specific configuration
location /socket.io/ {
    # Rate limiting
    limit_req zone=websocket burst=10 nodelay;

    # WebSocket headers
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $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;

    # Sticky sessions for WebSocket
    proxy_set_header X-Session-ID $cookie_sessionid;

    # Timeouts for persistent connections
    proxy_connect_timeout 4s;
    proxy_send_timeout 60s;
    proxy_read_timeout 3600s;

    # Buffer settings
    proxy_buffering off;
    proxy_request_buffering off;

    # Pass to WebSocket upstream
    proxy_pass http://chess_websocket;

    # Error handling
    proxy_intercept_errors on;
    error_page 502 503 504 /websocket_error.html;
}

# WebSocket error page
location = /websocket_error.html {
    root /var/www/chess-showcase/errors;
    internal;
}

Monitoring and Logging

Advanced Logging

# Custom log formats
log_format chess_performance '$remote_addr - $remote_user [$time_local] '
                            '"$request" $status $body_bytes_sent '
                            '"$http_referer" "$http_user_agent" '
                            'rt=$request_time '
                            'uct="$upstream_connect_time" '
                            'uht="$upstream_header_time" '
                            'urt="$upstream_response_time" '
                            'cache="$upstream_cache_status" '
                            'gzip_ratio="$gzip_ratio"';

log_format chess_security '$remote_addr - $remote_user [$time_local] '
                         '"$request" $status $body_bytes_sent '
                         '"$http_referer" "$http_user_agent" '
                         '"$http_x_forwarded_for" '
                         'ssl_protocol="$ssl_protocol" '
                         'ssl_cipher="$ssl_cipher" '
                         'request_length="$request_length" '
                         'connection="$connection" '
                         'connection_requests="$connection_requests"';

# Conditional logging
map $status $log_4xx_5xx {
    ~^[45] 1;
    default 0;
}

# Access log with conditions
access_log /var/log/nginx/chess-performance.log chess_performance;
access_log /var/log/nginx/chess-security.log chess_security;
access_log /var/log/nginx/chess-errors.log chess_performance if=$log_4xx_5xx;

# Real-time metrics endpoint
location /nginx_status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;
}

Prometheus Metrics

# Prometheus metrics (requires nginx-prometheus-exporter)
location /metrics {
    access_log off;
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;

    # Proxy to metrics exporter
    proxy_pass http://127.0.0.1:9113/metrics;
}

Performance Optimization

Connection Optimization

# nginx.conf additions for performance
events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
    accept_mutex off;
}

http {
    # Keep-alive settings
    keepalive_timeout 65;
    keepalive_requests 1000;

    # File handling
    sendfile on;
    sendfile_max_chunk 1m;
    tcp_nopush on;
    tcp_nodelay on;

    # Buffer settings
    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;

    # Timeouts
    client_header_timeout 3m;
    client_body_timeout 3m;
    send_timeout 3m;

    # Hash tables
    server_names_hash_bucket_size 64;
    types_hash_max_size 2048;
}

HTTP/2 Optimization

# HTTP/2 specific optimizations
server {
    listen 443 ssl http2;

    # HTTP/2 push
    location = /index.html {
        http2_push /css/main.css;
        http2_push /js/main.js;
        http2_push /fonts/chess-icons.woff2;
    }

    # HTTP/2 settings
    http2_max_field_size 4k;
    http2_max_header_size 16k;
    http2_max_requests 1000;
}

Administration Scripts

Nginx Management Script

#!/bin/bash
# nginx-chess-admin.sh

set -e

NGINX_CONF="/etc/nginx"
SITES_AVAILABLE="$NGINX_CONF/sites-available"
SITES_ENABLED="$NGINX_CONF/sites-enabled"
LOGDIR="/var/log/nginx"

case "$1" in
    enable)
        if [ -z "$2" ]; then
            echo "Usage: $0 enable <site-name>"
            exit 1
        fi

        SITE="$2"
        if [ ! -f "$SITES_AVAILABLE/$SITE" ]; then
            echo "Site $SITE not found in sites-available"
            exit 1
        fi

        ln -sf "$SITES_AVAILABLE/$SITE" "$SITES_ENABLED/"
        echo "Site $SITE enabled"
        nginx -t && systemctl reload nginx
        ;;

    disable)
        if [ -z "$2" ]; then
            echo "Usage: $0 disable <site-name>"
            exit 1
        fi

        SITE="$2"
        rm -f "$SITES_ENABLED/$SITE"
        echo "Site $SITE disabled"
        nginx -t && systemctl reload nginx
        ;;

    test)
        echo "Testing nginx configuration..."
        nginx -t
        ;;

    reload)
        echo "Reloading nginx..."
        nginx -t && systemctl reload nginx
        ;;

    status)
        systemctl status nginx
        ;;

    logs)
        SITE="${2:-chess-showcase}"
        tail -f "$LOGDIR/$SITE.log"
        ;;

    error-logs)
        SITE="${2:-chess-showcase}"
        tail -f "$LOGDIR/$SITE-error.log"
        ;;

    cert-renew)
        echo "Renewing SSL certificates..."
        certbot renew --quiet
        nginx -t && systemctl reload nginx
        ;;

    backup-config)
        BACKUP_DIR="/backup/nginx/$(date +%Y%m%d_%H%M%S)"
        mkdir -p "$BACKUP_DIR"
        cp -r "$NGINX_CONF" "$BACKUP_DIR/"
        echo "Configuration backed up to $BACKUP_DIR"
        ;;

    *)
        echo "Usage: $0 {enable|disable|test|reload|status|logs|error-logs|cert-renew|backup-config}"
        exit 1
        ;;
esac

SSL Certificate Management

#!/bin/bash
# ssl-management.sh

DOMAIN="chessgame.com"
SSL_DIR="/etc/nginx/ssl"
EMAIL="[email protected]"

setup_letsencrypt() {
    echo "Setting up Let's Encrypt for $DOMAIN..."

    # Install certbot
    sudo apt update
    sudo apt install certbot python3-certbot-nginx

    # Get certificate
    sudo certbot --nginx -d "$DOMAIN" -d "www.$DOMAIN" --email "$EMAIL" --agree-tos --non-interactive

    # Setup auto-renewal
    echo "0 12 * * * /usr/bin/certbot renew --quiet" | sudo crontab -
}

generate_dhparam() {
    echo "Generating DH parameters..."
    sudo openssl dhparam -out "$SSL_DIR/dhparam.pem" 2048
}

check_cert_expiry() {
    echo "Checking certificate expiry for $DOMAIN..."
    echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN":443 2>/dev/null | openssl x509 -noout -dates
}

case "$1" in
    setup)
        setup_letsencrypt
        generate_dhparam
        ;;
    renew)
        sudo certbot renew
        sudo systemctl reload nginx
        ;;
    check)
        check_cert_expiry
        ;;
    dhparam)
        generate_dhparam
        ;;
    *)
        echo "Usage: $0 {setup|renew|check|dhparam}"
        exit 1
        ;;
esac

Troubleshooting

Common Issues and Solutions

# Check nginx configuration
nginx -t

# Test specific site configuration
nginx -t -c /etc/nginx/sites-available/chess-showcase

# Check nginx status
systemctl status nginx

# View nginx error logs
tail -f /var/log/nginx/error.log

# Check listening ports
ss -tulpn | grep nginx

# Monitor nginx processes
ps aux | grep nginx

# Check file permissions
ls -la /etc/nginx/sites-available/
ls -la /var/www/chess-showcase/

# Test upstream connectivity
curl -I http://127.0.0.1:3000/health

# Check SSL certificate
openssl s_client -connect chessgame.com:443 -servername chessgame.com

Performance Monitoring

#!/bin/bash
# nginx-monitor.sh

echo "=== Nginx Status ==="
curl -s http://localhost/nginx_status

echo -e "\n=== Active Connections ==="
ss -tulpn | grep :80
ss -tulpn | grep :443

echo -e "\n=== Recent Errors ==="
tail -n 10 /var/log/nginx/error.log

echo -e "\n=== Top IPs by Request Count ==="
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -10

echo -e "\n=== Response Time Statistics ==="
awk '{print $NF}' /var/log/nginx/access.log | grep -E '^[0-9.]+$' | awk '{sum+=$1; count++} END {print "Average:", sum/count, "Count:", count}'

Next Steps

⚠️ **GitHub.com Fallback** ⚠️