nginx

nginx

listen vs server_name

| The listen directive can be set to:

location

location optional_modifier location_match {

    . . .

}

Optimizing nginx for high traffic loads

worker_processes  4;  # 2 * Number of CPUs

events {
    worker_connections  19000;  # It's the key to high performance - have a lot of connections available
}

worker_rlimit_nofile    20000;  # Each connection needs a filehandle (or 2 if you are proxying)


# Total amount of users you can serve = worker_processes * worker_connections

location


location / {
    root /var/www/html;                     # request file path
    index index.php index.html index.htm;   # default filename
    expires 7d;                             # static file expire time
    try_files $uri $uri/=404;               # try_files
}

location /temp {
    return 302 http://www.example.com;      # 兼容旧版本
}

location /chat {
    try_files $uri @chatapp;    # redirect to other app
}

location @chatapp {
    proxy_pass http://chat.app.com;
}

location /status {
    stub_status on;         # open nginx status page, default off; # depends on nginx_http_stub_status_module, installation requred
    allow 192.168.0.176;    # allowed list
    deny all;               # default deny all
}

location /download {
    alias /var/www/download;        # assets path, #NOTICE: index attribute not allowed
    autoindex on;                   # turn on inventory index
    autoindex_exact_size off;       # default on, display exact file size(bytes), OFF display (kB, MB)
    autoindex_localtime on;         # default off, show GMT time, ON show server time
}

location ~* /story(/|$) {
    auth_basic "Login Required";
    auth_basic_user_file /etc/nginx/.htpasswd;
    root /usr/share/nginx/html;
}

nginx auth

# ubuntu
sudo apt update && sudo apt install apache2-utils -y
# debian
sudo yum install httpd-tools -y

# -c:创建新文件;路径建议放在 Nginx 配置目录(非 web 根)
sudo htpasswd -c /etc/nginx/.htpasswd admin
# 按提示输入并确认密码(密码加密存储)

# add more users
sudo htpasswd /etc/nginx/.htpasswd user1

# 用更安全的 bcrypt 加密(推荐)
sudo htpasswd -B /etc/nginx/.htpasswd admin

# nginx all site enable auth
server {
    listen 80;
    server_name yourdomain.com;

    # 启用 Basic Auth,提示文字可自定义
    auth_basic "Restricted Access";
    # 指定密码文件路径(必须绝对路径)
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
    }
}

upstream


# default: round-robin
upstream shop {
    server 10.24.0.1:8000;
    server 10.24.0.1:8001;
    server 10.24.0.2:8000;
}

# weighted
upstream shop {
    server 10.24.0.1:8000 weight=1;
    server 10.24.0.1:8001 weight=2;
    server 10.24.0.2:8000 weight=3;
}

# ip_hash
upstream shop {
    ip_hash;
    server 10.24.0.1:8000;
    server 10.24.0.1:8001;
    server 10.24.0.2:8000;
}
# least_conn
# cookie
# route
# lean

# ntlm
# least_time
# queue
# stick

# fair
# url_hash

nginx variable

name description
$remote_add
$remote_user
$time_local
$request url + method
$status
$body_bytes_sent
$http_referer
$http_user_agent
$request_uri has search string
$uri no search string
$http_x_forwarded_for
$http_x_real_ip
$args $query_string
$host
$scheme http, https
$document_uri
$document_root

GeoIP

# 1. install dynamic module
sudo apt install libnginx-mod-http-geoip2
# confirm module loaded
nginx -V 2>&1 | grep geoip2

# 2. edit conf files

# 3. place mmdb files
sudo mkdir -p /etc/nginx
sudo cp GeoIP2-City.mmdb /etc/nginx/
sudo chmod 644 /etc/nginx/GeoIP2-City.mmdb

# 4. reload nginx
sudo nginx -t
sudo systemctl restart nginx

# 5. test from remote
curl http://your.domain.com/geo
http {

    geoip2 /etc/nginx/GeoIP2-City.mmdb {
        auto_reload 5m;

        $geoip2_city city names zh-CN;
        $geoip2_region subdivisions 0 iso_code;
        $geoip2_latitude location latitude;
        $geoip2_longitude location longitude;
    }
    geoip2 /etc/nginx/GeoIP2-Country.mmdb {
        $geoip2_country_code default=CN country iso_code;
        $geoip2_country_name country names zh-CN;
    }

    map $geoip2_country_code $allowed_country {
        default 0;  # Block all by default
        CN 1; # only specified countries are allowed
    }

    server {
        listen 80;

        location = /geo { # for testing purpose
            default_type application/json;
            #add_header Content-Type application/json;
            return 200 '{
                "ip":"$remote_addr",
                "lat":"$geoip2_latitude",
                "lng":"$geoip2_longitude",
                "city":"$geoip2_city",
                "country":"$geoip2_country_code",
                "region":"$geoip2_region"
            }';
        }

        location / {
            proxy_pass http://localhost:13030;
            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 3600;
            proxy_send_timeout 3600;
            proxy_read_timeout 3600;
            proxy_set_header X-Country-Code $geoip2_country_code;
            proxy_set_header X-Country-Name $geoip2_country_name;
            proxy_set_header X-City $geoip2_city;
            proxy_set_header X-Region $geoip2_region;
            proxy_set_header X-Latitude $geoip2_latitude;
            proxy_set_header X-Longitude $geoip2_longitude;
        }
    }
}

API Gateway

http {
    # 定义后端服务集群(可选,用于负载均衡)
    upstream user_service {
        server 192.168.1.10:8080;  # 用户服务实例1
        server 192.168.1.11:8080;  # 用户服务实例2
        least_conn;  # 优先转发到连接数最少的实例
    }

    upstream order_service {
        server 192.168.1.20:8080;  # 订单服务实例
    }

    server {
        listen 80;
        server_name api.example.com;  # API 网关域名

        # 转发 /api/v1/user 到用户服务
        location /api/v1/user {
            # 传递真实客户端 IP 和 Host 到后端
            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://user_service;  # 转发到 upstream 定义的集群
        }

        # 转发 /api/v1/order 到订单服务
        location /api/v1/order {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_pass http://order_service;
        }

        # URL 重写示例:将 /v2/xxx 转发到新服务,并修改路径
        location /v2 {
            # 重写路径:/v2/user/123 → /user/123
            rewrite ^/v2/(.*)$ /$1 break;
            proxy_pass http://new_service;
        }
    }
}

QPS limit

http {
    # 定义限流规则:基于客户端 IP,共享 10MB 内存,限制 10r/s(每秒 10 个请求)
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

    server {
        listen 80;
        server_name api.example.com;

        location /api {
            # 应用限流规则:允许突发 20 个请求,超过的直接拒绝(nodelay)
            limit_req zone=api_limit burst=20 nodelay;

            proxy_pass http://backend_service;
        }
    }
}

JWT auth, based on OpenResty + Lua

# 需安装 OpenResty(集成 ngx_lua 和 lua-resty-jwt 库)
http {
    lua_package_path "/usr/local/openresty/lualib/?.lua;;";  # 指定 Lua 库路径

    server {
        listen 80;
        server_name api.example.com;

        location /api {
            # 在请求处理前验证 JWT
            access_by_lua_block {
                local jwt = require "resty.jwt"
                local auth_header = ngx.req.get_headers()["Authorization"]

                # 检查 Authorization 头是否存在且格式为 "Bearer <token>"
                if not auth_header or not string.find(auth_header, "Bearer ") then
                    ngx.status = 401
                    ngx.say('{"error": "Missing or invalid Authorization header"}')
                    ngx.exit(ngx.HTTP_UNAUTHORIZED)
                end

                # 提取 JWT 令牌(去掉 "Bearer " 前缀)
                local token = string.sub(auth_header, 8)
                # 验证 JWT(使用签名密钥,需与签发方一致)
                local jwt_obj = jwt:verify("your-jwt-secret-key", token)

                if not jwt_obj.valid then
                    ngx.status = 401
                    ngx.say('{"error": "Invalid JWT token: ' .. jwt_obj.reason .. '"}')
                    ngx.exit(ngx.HTTP_UNAUTHORIZED)
                end
            }

            proxy_pass http://backend_service;
        }
    }
}

CORS

server {
    listen 80;
    server_name api.example.com;

    location /api {
        # 允许的来源(* 表示所有,生产环境建议指定具体域名)
        add_header 'Access-Control-Allow-Origin' '*';
        # 允许的请求方法
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
        # 允许的请求头
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
        # 预检请求(OPTIONS)直接返回 204(无内容)
        if ($request_method = 'OPTIONS') {
            return 204;
        }

        proxy_pass http://backend_service;
    }
}

Page Source