Skip to content

Nginx

1. Nginx 简介

Nginx(读作 "engine-x")是一款高性能的 HTTP 服务器、反向代理服务器及通用 TCP/UDP 代理,由俄罗斯工程师 Igor Sysoev 于 2004 年发布。

架构原理

传统服务器(Apache 等):
  每个请求 → 创建/分配一个线程/进程处理
  高并发时:大量线程切换 → 内存和 CPU 开销巨大

Nginx 架构:
  Master 进程(1个):读取配置、管理 Worker
  Worker 进程(N个,通常等于 CPU 核心数):
    每个 Worker 用 epoll/kqueue 事件驱动
    单线程处理数千个并发连接(无线程切换开销)

  Master
    ├── Worker 1  (epoll: conn1, conn2, conn3...)
    ├── Worker 2  (epoll: conn4, conn5, conn6...)
    └── Worker N  (epoll: ...)

Nginx vs Apache

维度 Nginx Apache
架构 事件驱动,异步非阻塞 进程/线程模型
高并发性能 极强(万级并发轻松应对) 较弱(线程开销大)
内存占用 极低 较高
动态内容 需配合 FastCGI/uWSGI 内置模块直接处理(mod_php)
配置灵活性 较强(Location 精细控制) 支持 .htaccess 分目录配置
模块热加载 不支持(需重编译) 支持动态加载模块
适用场景 高并发、反向代理、静态资源 传统 Web 应用、共享主机

2. 安装与目录结构

安装

# Ubuntu / Debian
sudo apt update && sudo apt install nginx

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

# macOS(Homebrew)
brew install nginx

# 编译安装(自定义模块)
./configure --prefix=/etc/nginx \
            --with-http_ssl_module \
            --with-http_v2_module \
            --with-http_gzip_static_module \
            --with-stream \
            --with-stream_ssl_module
make && make install

# 验证安装
nginx -v          # 查看版本
nginx -V          # 查看版本及编译参数

目录结构

/etc/nginx/
├── nginx.conf              # 主配置文件
├── conf.d/                 # 子配置目录(include 进主配置)
│   ├── default.conf
│   └── myapp.conf
├── sites-available/        # Debian/Ubuntu 风格:可用站点
│   └── mysite
├── sites-enabled/          # 软链接到 sites-available 中已启用的站点
│   └── mysite -> ../sites-available/mysite
├── snippets/               # 可复用的配置片段
│   └── ssl-params.conf
├── mime.types              # 文件扩展名 → Content-Type 映射
├── fastcgi_params          # FastCGI 参数
├── uwsgi_params            # uWSGI 参数
└── modules-enabled/        # 动态模块

/var/log/nginx/
├── access.log              # 访问日志
└── error.log               # 错误日志

/var/www/html/              # 默认网站根目录(Ubuntu)
/usr/share/nginx/html/      # 默认网站根目录(CentOS)

/usr/lib/nginx/modules/     # 动态模块(.so 文件)

3. 核心配置语法

配置文件结构

# 全局块(影响整个 Nginx 进程)
user              nginx;
worker_processes  auto;
error_log         /var/log/nginx/error.log warn;
pid               /var/run/nginx.pid;

# events 块(网络连接配置)
events {
    worker_connections 1024;   # 每个 Worker 最大并发连接数
    use epoll;                 # 事件模型(Linux 推荐 epoll)
    multi_accept on;           # 一次尽可能多地接受连接
}

# http 块(HTTP 服务配置)
http {
    include      mime.types;
    default_type application/octet-stream;

    # server 块(虚拟主机)
    server {
        listen      80;
        server_name example.com;

        # location 块(URL 匹配规则)
        location / {
            root  /var/www/html;
            index index.html;
        }

        location /api {
            proxy_pass http://backend:8080;
        }
    }
}

# stream 块(TCP/UDP 代理,需要 --with-stream 模块)
stream {
    server {
        listen     3306;
        proxy_pass mysql_backend;
    }
}

指令类型

# 简单指令:名称 值;
worker_processes 4;
error_log /var/log/nginx/error.log;

# 块指令:名称 { ... }
events { }
http { }
server { }
location / { }

# include:引入其他配置文件
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

变量

# Nginx 内置变量(常用)
$host           # 请求的 Host 头(域名)
$uri            # 当前请求的 URI(不含查询串)
$args           # 查询字符串(? 后面的部分)
$request_uri    # 完整 URI(含查询串)
$request_method # 请求方法(GET/POST/...)
$remote_addr    # 客户端 IP
$server_port    # 服务器端口
$server_protocol# 协议版本(HTTP/1.1)
$scheme         # 协议(http/https)
$status         # 响应状态码
$body_bytes_sent# 响应体字节数
$http_referer   # Referer 头
$http_user_agent# User-Agent 头
$http_x_forwarded_for  # X-Forwarded-For 头
$time_local     # 本地时间
$request_time   # 请求处理耗时(秒)
$upstream_addr  # 上游服务器地址
$upstream_response_time # 上游响应时间
$ssl_protocol   # SSL 协议版本
$ssl_cipher     # SSL 加密算法

# 自定义变量
set $my_var "hello";
set $is_mobile 0;

if 条件判断

# 注意:Nginx 的 if 有很多"坑",尽量用 map 或 try_files 替代
location / {
    # 正则匹配(~区分大小写,~*不区分)
    if ($http_user_agent ~* "Mobile") {
        set $is_mobile 1;
    }

    # 文件不存在
    if (!-f $request_filename) {
        return 404;
    }

    # 等值判断
    if ($request_method = POST) {
        return 405;
    }
}

map 模块(if 的更好替代)

http {
    # 根据 User-Agent 设置变量
    map $http_user_agent $device_type {
        default          "desktop";
        "~*Mobile"       "mobile";
        "~*iPad|Tablet"  "tablet";
    }

    # 根据 IP 段设置变量
    map $remote_addr $is_internal {
        default         0;
        "~^10\."        1;
        "~^192\.168\."  1;
        "~^172\.(1[6-9]|2[0-9]|3[01])\." 1;
    }

    server {
        location / {
            add_header X-Device $device_type;
            if ($is_internal = 0) {
                return 403;
            }
        }
    }
}

4. 静态文件服务

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

    root /var/www/static;   # 网站根目录

    # 首页文件(按顺序查找)
    index index.html index.htm;

    location / {
        # try_files:按顺序尝试,都不存在则返回 404
        try_files $uri $uri/ /index.html;
        # 含义:先找文件,再找目录(目录下的 index),再回退到 /index.html(SPA 路由)
    }

    # 图片、JS、CSS 等静态资源缓存
    location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
        expires    30d;                        # 浏览器缓存 30 天
        add_header Cache-Control "public, immutable";
        add_header Vary Accept-Encoding;
        access_log off;                        # 静态资源不记录访问日志(减少 I/O)
    }

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

    location ~* \.(woff|woff2|ttf|eot)$ {
        expires    365d;
        add_header Access-Control-Allow-Origin "*";  # 字体跨域
    }

    # 禁止访问隐藏文件(.git、.env 等)
    location ~ /\. {
        deny all;
        return 404;
    }

    # 目录浏览(谨慎开启)
    location /files/ {
        autoindex on;
        autoindex_exact_size off;   # 显示人类可读的文件大小
        autoindex_localtime  on;    # 显示本地时区时间
    }
}

别名(alias)

# root:将 location 路径附加到 root 后
# alias:将 location 路径替换为 alias 路径

# root 示例
location /images/ {
    root /var/www;
    # 实际路径:/var/www/images/foo.jpg
}

# alias 示例(注意末尾斜杠)
location /images/ {
    alias /data/pics/;
    # 实际路径:/data/pics/foo.jpg(替换了 /images/)
}

5. 反向代理

基础反向代理

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

    location / {
        proxy_pass http://127.0.0.1:8080;  # 转发到后端

        # 必加的 Header,让后端知道真实客户端信息
        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  10s;   # 连接上游超时
        proxy_send_timeout     60s;   # 发送请求超时
        proxy_read_timeout     60s;   # 读取响应超时

        # HTTP 版本(默认 1.0,建议改 1.1 以支持长连接)
        proxy_http_version 1.1;
        proxy_set_header   Connection "";

        # 缓冲区(避免后端响应慢时占用连接)
        proxy_buffering         on;
        proxy_buffer_size       4k;
        proxy_buffers           8 16k;
        proxy_busy_buffers_size 32k;
    }
}

proxy_pass 路径拼接规则

# 规则:proxy_pass 末尾有无 / 决定是否保留 location 前缀

# ❶ proxy_pass 不带路径(末尾无/):完整转发 URI
location /api/ {
    proxy_pass http://backend;
    # 请求 /api/users → 转发 /api/users
}

# ❷ proxy_pass 带路径(末尾有/):替换 location 前缀
location /api/ {
    proxy_pass http://backend/;
    # 请求 /api/users → 转发 /users(去掉了 /api 前缀)
}

# ❸ proxy_pass 带具体路径:替换 location 前缀
location /api/ {
    proxy_pass http://backend/v2/;
    # 请求 /api/users → 转发 /v2/users
}

转发到 Unix Socket

# 比 TCP 更快(无网络栈开销),用于本机进程间通信
location / {
    proxy_pass http://unix:/var/run/app.sock;
}

6. 负载均衡

upstream 块

http {
    # 定义上游服务器组
    upstream backend {
        # 负载均衡算法(默认:轮询)
        server 10.0.0.1:8080;
        server 10.0.0.2:8080;
        server 10.0.0.3:8080;
    }

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

负载均衡算法

# ---- 1. 轮询(Round Robin,默认)----
upstream backend_rr {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

# ---- 2. 加权轮询(按 weight 分配流量)----
upstream backend_weight {
    server 10.0.0.1:8080 weight=5;   # 分配 50% 流量(高配机器)
    server 10.0.0.2:8080 weight=3;   # 分配 30%
    server 10.0.0.3:8080 weight=2;   # 分配 20%
}

# ---- 3. IP Hash(同一 IP 固定到同一服务器,解决 Session 问题)----
upstream backend_iphash {
    ip_hash;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

# ---- 4. least_conn(最少连接数,适合处理时长差异大的场景)----
upstream backend_lc {
    least_conn;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

# ---- 5. 一致性哈希(按 URI 哈希,相同 URI 固定到同一服务器,利于缓存)----
upstream backend_hash {
    hash $request_uri consistent;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

服务器参数详解

upstream backend {
    server 10.0.0.1:8080 weight=3 max_fails=3 fail_timeout=30s;
    #                     权重=3  最大失败次数  失败后暂停时间

    server 10.0.0.2:8080;

    # 备用服务器(主服务器全挂才启用)
    server 10.0.0.3:8080 backup;

    # 永久下线(保留配置但不使用)
    server 10.0.0.4:8080 down;

    # 长连接池(避免每次请求都建立 TCP 连接)
    keepalive 32;              # 保持 32 个空闲长连接
    keepalive_requests 1000;   # 单个长连接最多复用 1000 次请求
    keepalive_timeout  60s;    # 长连接空闲超时
}

健康检查(商业版 nginx_upstream_check_module / 开源替代)

# 开源版:被动健康检查(通过 max_fails / fail_timeout)
upstream backend {
    server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
    # 30s 内失败 3 次 → 标记为不可用,30s 后重试
}

# nginx-upstream-check-module(第三方模块,需编译)
upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;

    check interval=3000 rise=2 fall=3 timeout=1000 type=http;
    check_http_send "HEAD /health HTTP/1.0\r\n\r\n";
    check_http_expect_alive http_2xx;
}

7. HTTPS 与 SSL

基础 HTTPS 配置

server {
    listen 443 ssl http2;         # 开启 SSL 和 HTTP/2
    server_name example.com;

    # 证书文件(Let's Encrypt 免费证书)
    ssl_certificate     /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    # 只允许安全协议版本
    ssl_protocols TLSv1.2 TLSv1.3;

    # 加密套件(Mozilla 推荐的中级配置)
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;   # TLSv1.3 不需要服务端优先

    # SSL Session 缓存(减少握手次数)
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;    # 禁用 Session Ticket(前向安全)

    # OCSP Stapling(加速证书验证)
    ssl_stapling        on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;

    # HSTS(强制浏览器使用 HTTPS,一年)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    location / {
        root /var/www/html;
        index index.html;
    }
}

# HTTP → HTTPS 永久重定向
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

Let's Encrypt 自动证书(Certbot)

# 安装 Certbot
sudo apt install certbot python3-certbot-nginx

# 自动申请证书并配置 Nginx
sudo certbot --nginx -d example.com -d www.example.com

# 测试自动续期
sudo certbot renew --dry-run

# 手动续期
sudo certbot renew

# 查看证书有效期
sudo certbot certificates

DH 参数(增强安全性)

# 生成 DH 参数(一次性,较慢)
sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
ssl_dhparam /etc/nginx/ssl/dhparam.pem;

8. 虚拟主机

基于域名的虚拟主机

# 站点 1
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example;
}

# 站点 2
server {
    listen 80;
    server_name blog.example.com;
    root /var/www/blog;
}

# 站点 3
server {
    listen 80;
    server_name shop.example.com;
    root /var/www/shop;
}

# 默认服务器(无匹配域名时使用)
server {
    listen 80 default_server;
    server_name _;        # _ 是无效域名,用来表示"兜底"
    return 444;           # 444:Nginx 特有,直接关闭连接(无响应)
}

基于端口的虚拟主机

server {
    listen 8080;
    server_name localhost;
    root /var/www/app1;
}

server {
    listen 9090;
    server_name localhost;
    root /var/www/app2;
}

server_name 匹配规则

# 精确匹配(优先级最高)
server_name example.com;

# 通配符前缀
server_name *.example.com;

# 通配符后缀
server_name example.*;

# 正则(最低优先级,以 ~ 开头)
server_name ~^(www|api|static)\.example\.com$;

# 优先级:精确 > 前缀通配 > 后缀通配 > 正则 > default_server

9. Location 匹配规则

匹配语法与优先级

server {
    # ❶ = 精确匹配(最高优先级)
    location = / {
        # 只匹配根路径 "/",精确到字符
        return 200 "exact match";
    }

    # ❷ ^~ 前缀匹配(比正则优先,找到后不再尝试正则)
    location ^~ /static/ {
        root /var/www;
        # 请求 /static/a.js → 匹配此处,不再检查正则
    }

    # ❸ ~ 正则匹配(区分大小写)
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm.sock;
    }

    # ❹ ~* 正则匹配(不区分大小写)
    location ~* \.(jpg|jpeg|png|gif)$ {
        expires 30d;
    }

    # ❺ 普通前缀匹配(最低优先级,作为兜底)
    location / {
        try_files $uri $uri/ =404;
    }
}

优先级总结

1. =    精确匹配          ← 最高
2. ^~   前缀匹配(阻止正则)
3. ~    正则(区分大小写) ← 按配置顺序,先匹配先用
   ~*   正则(不区分大小写)
4. /xxx 普通前缀匹配      ← 最低(取最长匹配)

实际场景示例

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

    # 根路径精确匹配,快速响应
    location = / {
        try_files /index.html =404;
    }

    # API 反向代理
    location /api/ {
        proxy_pass http://backend:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 静态资源(阻止正则,直接用文件系统)
    location ^~ /assets/ {
        expires 365d;
        add_header Cache-Control "public, immutable";
    }

    # PHP 处理
    location ~ \.php$ {
        include        fastcgi_params;
        fastcgi_pass   unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    # 禁止访问敏感文件
    location ~ /\.(git|env|htaccess|DS_Store) {
        deny all;
        return 404;
    }

    # SPA 前端(兜底)
    location / {
        try_files $uri $uri/ /index.html;
    }
}

10. Rewrite 与重定向

return 指令

# 重定向
return 301 https://example.com$request_uri;   # 永久重定向
return 302 https://example.com$request_uri;   # 临时重定向

# 直接返回状态码和内容
return 200 "OK";
return 404 "Not Found";
return 403;

# Nginx 特有:关闭连接(不返回任何响应,用于拒绝恶意请求)
return 444;

rewrite 指令

# 语法:rewrite 正则 替换 [flag]
# flag:
#   last      :重写后重新搜索 location(类似内部跳转)
#   break     :重写后停止,在当前 location 继续处理
#   redirect  :302 临时重定向
#   permanent :301 永久重定向

# 去掉 URL 中的 index.php
rewrite ^/index\.php/(.*)$ /$1 permanent;

# 旧路径迁移到新路径
rewrite ^/old-blog/(.*)$ /blog/$1 permanent;

# 强制带 www
server {
    server_name example.com;
    return 301 $scheme://www.example.com$request_uri;
}

# 强制去掉 www
server {
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}

# 删除尾部斜杠(/about/ → /about)
rewrite ^/(.*)/$ /$1 permanent;

实际场景

server {
    listen 80;
    server_name example.com;

    # 维护模式(屏蔽除特定 IP 外的所有请求)
    set $maintenance 0;
    if ($remote_addr != "1.2.3.4") {
        set $maintenance 1;
    }
    if ($maintenance = 1) {
        return 503;
    }

    # 移动端重定向
    location / {
        if ($http_user_agent ~* "Mobile|Android|iPhone") {
            return 302 https://m.example.com$request_uri;
        }
        root /var/www/html;
    }

    # 防盗链(Referer 校验)
    location ~* \.(jpg|png|gif|mp4)$ {
        valid_referers none blocked example.com *.example.com;
        if ($invalid_referer) {
            return 403;
        }
        root /var/www/media;
    }
}

11. 缓存

代理缓存(proxy_cache)

http {
    # 定义缓存区域
    proxy_cache_path /var/cache/nginx
        levels=1:2              # 目录层级(减少单目录文件数)
        keys_zone=my_cache:10m  # 共享内存区域名称和大小(存储缓存键)
        max_size=10g            # 磁盘最大占用
        inactive=60m            # 60分钟无访问则删除
        use_temp_path=off;      # 直接写入缓存目录(避免额外拷贝)

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

            proxy_cache          my_cache;           # 使用哪个缓存区
            proxy_cache_key      "$scheme$host$uri$args"; # 缓存键
            proxy_cache_valid    200 302 10m;        # 200/302 响应缓存 10 分钟
            proxy_cache_valid    404      1m;        # 404 缓存 1 分钟
            proxy_cache_valid    any      5m;        # 其他状态缓存 5 分钟
            proxy_cache_use_stale error timeout updating; # 后端故障时返回旧缓存
            proxy_cache_lock     on;                 # 防缓存击穿(同一 key 只有一个请求穿透到后端)

            # 在响应头中显示缓存状态(调试用)
            add_header X-Cache-Status $upstream_cache_status;
            # $upstream_cache_status 可能的值:
            # HIT(命中), MISS(未命中), EXPIRED(过期),
            # BYPASS(绕过), STALE(陈旧), REVALIDATED(重验证)
        }

        # 强制跳过缓存(管理后台、带 Cookie 的请求等)
        location /admin {
            proxy_pass  http://backend;
            proxy_cache_bypass $http_cookie;    # 有 Cookie 时不用缓存
            proxy_no_cache     $http_cookie;    # 有 Cookie 时不缓存
        }
    }
}

浏览器缓存控制

# 不缓存 HTML(确保用户获取最新页面)
location ~* \.html$ {
    add_header Cache-Control "no-cache, no-store, must-revalidate";
    add_header Pragma        "no-cache";
    expires 0;
}

# 强缓存静态资源(文件名带 hash,可永久缓存)
location ~* \.(js|css)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# 图片缓存 30 天
location ~* \.(jpg|jpeg|png|gif|webp|svg|ico)$ {
    expires 30d;
    add_header Cache-Control "public";
}

12. 限流与限速

请求频率限制(limit_req)

http {
    # 定义限流区域
    # $binary_remote_addr:以客户端 IP 作为限流 key(binary 格式节省内存)
    # zone=req_limit:10m:共享内存区名称和大小(10MB 约存 16万个 IP 状态)
    # rate=10r/s:每秒最多 10 个请求
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;

    # 也可以按接口限流
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;

    # 全局限流(按 IP)
    limit_req_zone $binary_remote_addr zone=global:10m rate=100r/s;

    server {
        # 应用限流
        location /api/ {
            limit_req zone=req_limit burst=20 nodelay;
            # burst=20:允许突发 20 个请求(存入桶中排队)
            # nodelay:突发请求立即处理,不排队等待(超出 burst 才返回 503)
            proxy_pass http://backend;
        }

        # 登录接口严格限流
        location /api/login {
            limit_req zone=login_limit burst=5;
            # 无 nodelay:超过 rate 的请求进队列等待
            proxy_pass http://backend;
        }

        # 自定义限流错误页面
        limit_req_status 429;
        error_page 429 /rate_limit.html;
    }
}

并发连接数限制(limit_conn)

http {
    # 限制同一 IP 的并发连接数
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    server {
        # 同一 IP 最多 10 个并发连接
        limit_conn conn_limit 10;

        # 下载接口限连
        location /download/ {
            limit_conn conn_limit 2;   # 每个 IP 最多 2 个并发下载
            limit_rate 500k;           # 每个连接限速 500KB/s
            limit_rate_after 1m;       # 前 1MB 不限速,之后开始限速
            root /var/www/files;
        }
    }
}

带宽限速(limit_rate)

location /download/ {
    limit_rate_after 10m;    # 前 10MB 全速
    limit_rate       1m;     # 之后限速 1MB/s
}

# 根据用户等级动态限速
map $cookie_plan $rate_limit {
    default  "500k";
    "vip"    "0";      # 0 表示不限速
    "pro"    "2m";
}

location /media/ {
    limit_rate $rate_limit;
    proxy_pass http://media_backend;
}

13. 访问控制与安全

IP 访问控制

location /admin/ {
    # 白名单(只允许特定 IP)
    allow 192.168.1.0/24;
    allow 10.0.0.0/8;
    allow 1.2.3.4;
    deny  all;          # 其他全部拒绝

    proxy_pass http://admin_backend;
}

# 黑名单(屏蔽特定 IP)
location / {
    deny  1.2.3.4;
    deny  5.6.7.0/24;
    allow all;
}

HTTP Basic 认证

# 生成密码文件
sudo htpasswd -c /etc/nginx/.htpasswd admin
sudo htpasswd    /etc/nginx/.htpasswd user1   # 追加用户(不加 -c)
location /private/ {
    auth_basic           "请输入凭据";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

安全响应头

server {
    # 防点击劫持(禁止在 iframe 中加载)
    add_header X-Frame-Options "SAMEORIGIN" always;

    # 禁止 MIME 类型嗅探
    add_header X-Content-Type-Options "nosniff" always;

    # XSS 保护(旧浏览器)
    add_header X-XSS-Protection "1; mode=block" always;

    # 内容安全策略(CSP)
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;

    # 引用策略
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # 权限策略(禁用不必要的浏览器功能)
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

    # HSTS(仅 HTTPS)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # 隐藏 Nginx 版本号
    server_tokens off;
}

防 DDoS 基础配置

http {
    # 限制请求体大小(防上传攻击)
    client_max_body_size    10m;
    client_body_timeout     12s;
    client_header_timeout   12s;
    send_timeout            10s;

    # 限制连接数和请求频率
    limit_conn_zone $binary_remote_addr zone=ddos_conn:10m;
    limit_req_zone  $binary_remote_addr zone=ddos_req:10m rate=20r/s;

    server {
        limit_conn ddos_conn 20;
        limit_req  zone=ddos_req burst=50 nodelay;

        # 屏蔽空 User-Agent(爬虫、扫描器)
        if ($http_user_agent = "") {
            return 444;
        }

        # 屏蔽常见扫描器 UA
        if ($http_user_agent ~* "sqlmap|nikto|nmap|masscan|zgrab") {
            return 444;
        }
    }
}

14. 日志

日志格式

http {
    # 定义日志格式
    log_format main '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

    # JSON 格式(便于 ELK/Loki 收集)
    log_format json_log escape=json
        '{'
            '"timestamp":"$time_iso8601",'
            '"remote_addr":"$remote_addr",'
            '"method":"$request_method",'
            '"uri":"$uri",'
            '"args":"$args",'
            '"status":$status,'
            '"bytes_sent":$body_bytes_sent,'
            '"request_time":$request_time,'
            '"upstream_addr":"$upstream_addr",'
            '"upstream_time":"$upstream_response_time",'
            '"http_referer":"$http_referer",'
            '"http_user_agent":"$http_user_agent",'
            '"request_id":"$request_id"'
        '}';

    # 全局访问日志
    access_log /var/log/nginx/access.log json_log buffer=32k flush=5s;
    # buffer:缓冲写入(减少磁盘 I/O)
    # flush:最迟 5 秒刷盘

    # 错误日志级别:debug | info | notice | warn | error | crit | alert | emerg
    error_log /var/log/nginx/error.log warn;

    server {
        # 单个站点日志
        access_log /var/log/nginx/example.access.log json_log;
        error_log  /var/log/nginx/example.error.log  warn;

        # 关闭特定路径的日志(减少噪音)
        location /health {
            access_log off;
        }

        # 只记录错误
        location /static/ {
            access_log /var/log/nginx/static.log combined if=$upstream_cache_status;
        }
    }
}

日志轮转(logrotate)

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily                   # 每天轮转
    missingok               # 文件不存在不报错
    rotate 14               # 保留 14 天
    compress                # gzip 压缩
    delaycompress           # 延迟压缩(先保留昨天的未压缩,明天再压)
    notifempty              # 空文件不轮转
    create 0640 nginx adm   # 新日志文件权限
    sharedscripts
    postrotate
        # 轮转后发送 USR1 信号,Nginx 重新打开日志文件
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 `cat /var/run/nginx.pid`
        fi
    endscript
}

15. Gzip 压缩

http {
    gzip              on;
    gzip_vary         on;           # 添加 Vary: Accept-Encoding 响应头
    gzip_proxied      any;          # 代理请求也压缩
    gzip_comp_level   6;            # 压缩级别 1-9(6 是速度与压缩率的平衡点)
    gzip_buffers      16 8k;
    gzip_http_version 1.1;
    gzip_min_length   256;          # 小于 256 字节不压缩(压缩反而变大)

    # 需要压缩的 MIME 类型
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/x-javascript
        application/xml
        application/xml+rss
        application/atom+xml
        image/svg+xml
        font/truetype
        font/opentype
        application/vnd.ms-fontobject;

    # 不压缩 IE6(IE6 的 gzip 有 bug)
    gzip_disable "MSIE [1-6]\.";
}

预压缩静态文件(gzip_static)

# 提前生成 .gz 文件(比动态压缩更快)
gzip -9 /var/www/html/app.js        # 生成 app.js.gz
gzip -9 /var/www/html/style.css     # 生成 style.css.gz
location ~* \.(js|css)$ {
    gzip_static on;    # 优先使用预压缩的 .gz 文件
    expires 1y;
    add_header Cache-Control "public, immutable";
}

16. WebSocket 代理

http {
    # 升级连接映射(必须放在 http 块)
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

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

        location /ws/ {
            proxy_pass http://websocket_backend;

            # WebSocket 必须的 Header
            proxy_http_version 1.1;
            proxy_set_header Upgrade    $http_upgrade;
            proxy_set_header Connection $connection_upgrade;

            # 传递真实 IP
            proxy_set_header Host       $host;
            proxy_set_header X-Real-IP  $remote_addr;

            # WebSocket 超时(心跳间隔 < 此值)
            proxy_read_timeout 3600s;   # 1 小时
            proxy_send_timeout 3600s;
        }
    }
}

17. Stream 模块(TCP/UDP 代理)

# nginx.conf 顶层(与 http 块平级)
stream {
    # ---- MySQL 负载均衡 ----
    upstream mysql_pool {
        server 10.0.0.1:3306 weight=3;
        server 10.0.0.2:3306 weight=1;
    }

    server {
        listen 3306;
        proxy_pass    mysql_pool;
        proxy_timeout 600s;
    }

    # ---- Redis 代理 ----
    server {
        listen 6379;
        proxy_pass 10.0.0.10:6379;
    }

    # ---- TCP SSL 终止 ----
    server {
        listen 443 ssl;
        ssl_certificate     /etc/nginx/ssl/cert.pem;
        ssl_certificate_key /etc/nginx/ssl/key.pem;
        proxy_pass backend_servers;
    }

    # ---- UDP(DNS 代理)----
    server {
        listen 53 udp;
        proxy_pass 8.8.8.8:53;
        proxy_timeout 1s;
        proxy_responses 1;
    }
}

18. 性能调优

Worker 进程调优

# 通常设为 CPU 核心数,或 auto(自动检测)
worker_processes auto;

# 绑定 Worker 到指定 CPU 核心(减少 CPU 缓存失效)
worker_cpu_affinity auto;

# 单个 Worker 最大文件描述符数(需配合系统 ulimit)
worker_rlimit_nofile 65535;

events {
    # 每个 Worker 最大并发连接数
    # 总并发 = worker_processes × worker_connections
    worker_connections 4096;

    # 使用 epoll(Linux 最高效的 I/O 多路复用)
    use epoll;

    # 一次尽可能多接受新连接
    multi_accept on;
}

系统级调优

# /etc/sysctl.conf
# 扩大 TCP 连接队列
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# 快速回收 TIME_WAIT 连接
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

# 扩大端口范围(反向代理需要大量临时端口)
net.ipv4.ip_local_port_range = 1024 65535

# 文件描述符限制
# /etc/security/limits.conf
nginx soft nofile 65535
nginx hard nofile 65535

HTTP 连接优化

http {
    # 开启 sendfile(零拷贝,内核直接将文件发到网卡,不经用户空间)
    sendfile    on;

    # 配合 sendfile 使用,将 TCP 包凑满再发送(减少包数量)
    tcp_nopush  on;

    # 禁用 Nagle 算法,减少延迟(适合小数据频繁发送,如 API)
    tcp_nodelay on;

    # Keep-Alive(复用 TCP 连接)
    keepalive_timeout  65s;      # 连接保持时长
    keepalive_requests 1000;     # 单连接最多处理请求数

    # 请求头缓冲区
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k;

    # 响应缓冲区
    output_buffers   2 32k;
    postpone_output  1460;

    # 隐藏版本号
    server_tokens off;

    # 哈希表大小(影响 server_name 查找速度)
    server_names_hash_bucket_size 64;
    server_names_hash_max_size    512;
}

上游连接池

upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;

    # 连接池:避免每次请求都建立 TCP 连接
    keepalive         64;       # 保持 64 个空闲连接
    keepalive_requests 10000;
    keepalive_timeout  60s;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";  # 清除 Connection: close(启用长连接)
    }
}

19. 常用运维命令

# ---- 进程管理 ----
nginx                          # 启动
nginx -s stop                  # 立即停止(强制)
nginx -s quit                  # 优雅停止(等待处理中的请求完成)
nginx -s reload                # 热重载配置(不中断服务)
nginx -s reopen                # 重新打开日志文件(配合 logrotate)

# systemd 管理(推荐)
systemctl start   nginx
systemctl stop    nginx
systemctl reload  nginx        # 热重载
systemctl restart nginx        # 重启
systemctl status  nginx
systemctl enable  nginx        # 开机自启

# ---- 配置测试 ----
nginx -t                       # 测试配置文件语法
nginx -T                       # 测试并打印完整配置

# ---- 信号 ----
kill -HUP  $(cat /var/run/nginx.pid)   # 热重载
kill -USR1 $(cat /var/run/nginx.pid)   # 重新打开日志文件
kill -USR2 $(cat /var/run/nginx.pid)   # 热升级(新版本 binary 替换)
kill -WINCH $(cat /var/run/nginx.pid)  # 优雅停止 Worker

# ---- 查看状态(需开启 stub_status 模块)----
curl http://localhost/nginx_status
# Active connections: 291
# server accepts handled requests
#  16630948 16630948 31070465
# Reading: 6 Writing: 179 Waiting: 106

# ---- 日志分析 ----
# 实时查看访问日志
tail -f /var/log/nginx/access.log

# 统计 TOP 10 访问 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

# 统计 TOP 10 访问 URI
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

# 统计响应状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

# 统计每分钟请求数
awk '{print $4}' /var/log/nginx/access.log | cut -c 1-17 | sort | uniq -c

# 查找耗时超过 3 秒的请求(JSON 格式日志)
cat /var/log/nginx/access.log | jq 'select(.request_time > 3)'

stub_status 监控接口

server {
    listen 8080;
    allow 127.0.0.1;
    deny  all;

    location /nginx_status {
        stub_status;
    }
}

20. 实战:微服务网关配置

这是一份完整的生产级 Nginx 配置,整合了前面所有知识点。

# /etc/nginx/nginx.conf
user  nginx;
worker_processes  auto;
worker_rlimit_nofile 65535;

error_log /var/log/nginx/error.log warn;
pid       /var/run/nginx.pid;

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

http {
    include      mime.types;
    default_type application/octet-stream;

    # ---- 性能 ----
    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    keepalive_timeout  65;
    keepalive_requests 1000;
    server_tokens  off;

    # ---- 缓冲区 ----
    client_max_body_size        20m;
    client_body_buffer_size     128k;
    client_header_buffer_size   1k;
    large_client_header_buffers 4 8k;
    proxy_buffer_size           4k;
    proxy_buffers               8 16k;
    proxy_busy_buffers_size     32k;

    # ---- 超时 ----
    client_body_timeout   12s;
    client_header_timeout 12s;
    send_timeout          10s;

    # ---- 日志格式 ----
    log_format json escape=json
        '{"ts":"$time_iso8601","ip":"$remote_addr","method":"$request_method",'
        '"uri":"$uri","status":$status,"bytes":$body_bytes_sent,'
        '"rt":"$request_time","urt":"$upstream_response_time",'
        '"ua":"$http_user_agent","rid":"$request_id"}';

    access_log /var/log/nginx/access.log json buffer=32k flush=5s;

    # ---- Gzip ----
    gzip              on;
    gzip_vary         on;
    gzip_comp_level   6;
    gzip_min_length   256;
    gzip_types text/plain text/css application/json application/javascript
                text/xml application/xml image/svg+xml;

    # ---- 代理缓存 ----
    proxy_cache_path /var/cache/nginx levels=1:2
        keys_zone=api_cache:20m max_size=5g inactive=30m use_temp_path=off;

    # ---- 限流 ----
    limit_req_zone  $binary_remote_addr zone=global:20m    rate=200r/s;
    limit_req_zone  $binary_remote_addr zone=login:10m     rate=5r/m;
    limit_req_zone  $binary_remote_addr zone=api:20m       rate=50r/s;
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    limit_req_status  429;
    limit_conn_status 429;

    # ---- 上游服务 ----
    upstream user_service {
        least_conn;
        server user-service-1:8080 weight=3 max_fails=3 fail_timeout=30s;
        server user-service-2:8080 weight=3 max_fails=3 fail_timeout=30s;
        server user-service-3:8080 weight=1 backup;
        keepalive 64;
    }

    upstream order_service {
        least_conn;
        server order-service-1:8080 max_fails=3 fail_timeout=30s;
        server order-service-2:8080 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }

    upstream pay_service {
        server pay-service-1:8080 max_fails=2 fail_timeout=60s;
        server pay-service-2:8080 max_fails=2 fail_timeout=60s;
        keepalive 16;
    }

    upstream static_service {
        server static-1:8080;
        server static-2:8080;
        keepalive 128;
    }

    # ---- WebSocket 升级 ----
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    # ---- HTTP → HTTPS 重定向 ----
    server {
        listen 80;
        server_name example.com api.example.com;
        return 301 https://$host$request_uri;
    }

    # ---- 主 HTTPS 服务器 ----
    server {
        listen 443 ssl http2;
        server_name example.com;

        # SSL
        ssl_certificate     /etc/nginx/ssl/example.com.crt;
        ssl_certificate_key /etc/nginx/ssl/example.com.key;
        ssl_protocols       TLSv1.2 TLSv1.3;
        ssl_ciphers         ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305;
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 1d;
        ssl_stapling        on;
        ssl_stapling_verify on;

        # 安全头
        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;
        add_header X-Request-ID              $request_id                            always;

        # 全局限流
        limit_conn conn_limit 30;
        limit_req  zone=global burst=100 nodelay;

        # 健康检查
        location = /health {
            access_log off;
            return 200 '{"status":"ok"}';
            add_header Content-Type application/json;
        }

        # 前端静态资源
        location / {
            root /var/www/html;
            try_files $uri $uri/ /index.html;

            location ~* \.(js|css)$ {
                expires 1y;
                add_header Cache-Control "public, immutable";
                access_log off;
            }

            location ~* \.(jpg|jpeg|png|gif|webp|svg|ico|woff|woff2)$ {
                expires 30d;
                add_header Cache-Control "public";
                access_log off;
            }
        }

        # 用户服务 API
        location /api/v1/users {
            limit_req zone=api burst=30 nodelay;

            proxy_pass         http://user_service;
            proxy_http_version 1.1;
            proxy_set_header   Connection         "";
            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-Request-ID       $request_id;
            proxy_connect_timeout 5s;
            proxy_read_timeout    30s;

            # GET 请求缓存
            proxy_cache            api_cache;
            proxy_cache_methods    GET HEAD;
            proxy_cache_key        "$scheme$host$uri$args";
            proxy_cache_valid      200 5m;
            proxy_cache_bypass     $http_authorization;
            proxy_no_cache         $http_authorization;
            proxy_cache_use_stale  error timeout updating;
            proxy_cache_lock       on;
            add_header X-Cache-Status $upstream_cache_status;
        }

        # 登录接口严格限流
        location = /api/v1/auth/login {
            limit_req zone=login burst=3;

            proxy_pass         http://user_service;
            proxy_http_version 1.1;
            proxy_set_header   Connection        "";
            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_connect_timeout 5s;
            proxy_read_timeout    10s;
        }

        # 订单服务 API
        location /api/v1/orders {
            limit_req zone=api burst=20 nodelay;

            proxy_pass         http://order_service;
            proxy_http_version 1.1;
            proxy_set_header   Connection        "";
            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-Request-ID      $request_id;
            proxy_connect_timeout 5s;
            proxy_read_timeout    60s;   # 订单处理可能较慢
        }

        # 支付服务(严格超时控制)
        location /api/v1/pay {
            limit_req zone=api burst=10 nodelay;

            proxy_pass         http://pay_service;
            proxy_http_version 1.1;
            proxy_set_header   Connection        "";
            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 3s;
            proxy_read_timeout    30s;
            client_max_body_size  1m;
        }

        # WebSocket
        location /ws/ {
            proxy_pass         http://user_service;
            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_read_timeout 3600s;
            proxy_send_timeout 3600s;
        }

        # 禁止访问敏感路径
        location ~ /\.(git|env|htpasswd|DS_Store) {
            deny all;
            return 404;
        }

        # 自定义错误页
        error_page 429 /errors/429.json;
        error_page 502 503 504 /errors/5xx.json;

        location /errors/ {
            internal;
            root /var/www;
        }
    }

    # ---- API 子域 ----
    server {
        listen 443 ssl http2;
        server_name api.example.com;

        ssl_certificate     /etc/nginx/ssl/example.com.crt;
        ssl_certificate_key /etc/nginx/ssl/example.com.key;
        ssl_protocols       TLSv1.2 TLSv1.3;
        ssl_session_cache   shared:SSL:10m;

        location / {
            proxy_pass         http://user_service;
            proxy_http_version 1.1;
            proxy_set_header   Connection        "";
            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;
        }
    }

    # ---- 监控接口(内网访问)----
    server {
        listen 8080;
        allow 10.0.0.0/8;
        allow 127.0.0.1;
        deny  all;

        location /nginx_status {
            stub_status;
            access_log off;
        }

        location /metrics {
            proxy_pass http://prometheus_exporter:9113;
        }
    }
}

快速参考

# 常用操作速查
nginx -t                        # 检查配置
nginx -s reload                 # 热重载(不中断服务)
systemctl reload nginx          # 推荐方式

# 查看当前连接数
ss -s
netstat -an | grep :80 | wc -l

# 查看 Nginx 进程
ps aux | grep nginx

# 实时错误日志
tail -f /var/log/nginx/error.log

# 查找配置中的语法问题
nginx -T 2>&1 | grep -n "error\|warn"
配置加载优先级(从高到低):
  location = /exact          # 精确匹配
  location ^~ /prefix        # 前缀阻止正则
  location ~ \.php$          # 正则(按顺序)
  location /prefix           # 最长前缀匹配