Nginx 服务

本文合并自:01-Nginx完全指南.md、03-Nginx-HTTPS配置与systemd管理.md

本文覆盖 Nginx 的简介与架构特性、YUM 与源码两种安装方式、目录结构与命令行参数、nginx.conf 逐段解析(main / events / http / server / location)、虚拟主机、静态资源与目录浏览、访问控制、日志与切割、反向代理、负载均衡、代理缓存、rewrite 重写、与 PHP 集成、HTTPS 证书配置、systemd 服务管理、性能调优、故障排查与课后练习。


1. Nginx 简介与特性

Nginx(读作 "engine x")是一款轻量级的 Web 服务器 / 反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在 BSD-like 协议下发行。由俄罗斯程序设计师 Igor Sysoev 开发,最初供俄国大型入口网站及搜索引擎 Rambler 使用。

特点:

  • CPU、内存等资源消耗非常低,运行稳定
  • 官方测试能够支撑 5 万并发连接
  • 中国大陆使用 Nginx 的网站有:百度、新浪、网易、腾讯等
  • 配置简洁,模块化设计,支持热加载配置(nginx -s reload 不中断服务)

Nginx 官网: http://nginx.org/

常见架构缩写:

缩写 含义
LNMP Linux + Nginx + MySQL/MariaDB + PHP
LAMP Linux + Apache + MySQL/MariaDB + PHP

反向代理与缓存相关软件: Squid、Varnish、ATS、Nginx

负载均衡(LB)相关软件: Nginx、HAProxy、LVS

可以通过以下命令查看各大网站使用的 Web 服务器:

curl -I www.sina.com.cn
HTTP/1.1 302 Moved Temporarily
Server: nginx
...

现在大型网站几乎统一使用 Nginx 或 Nginx 的二次开发版本(如 Tengine、OpenResty 等)。Nginx 相对于 Apache 的优势:资源占用少、支持大并发。

1.1 与 Apache 的架构对比

对比项 Apache(prefork / worker) Nginx
并发模型 一个连接对应一个进程或线程 异步非阻塞事件驱动,一个 worker 处理成千上万连接
资源消耗 连接数增长时进程/线程数线性增长,内存占用高 内存占用低且基本恒定
静态资源 处理能力一般 处理静态文件、反向代理性能强(sendfile 零拷贝)
动态内容 内置模块(如 mod_php)直接解析 自身不解析动态语言,通过 fastcgi_pass / proxy_pass 转发
稳定性 成熟稳定,模块生态庞大 稳定,配置修改可热加载
配置方式 httpd.conf + .htaccess(目录级) nginx.conf 单一体系,无 .htaccess
适用场景 复杂的动态应用、需要 .htaccess 的虚拟主机 高并发入口、静态资源服务、反向代理与负载均衡

生产上常见组合是 Nginx 在前做静态资源与反向代理,Apache/Tomcat/PHP-FPM 在后处理动态请求,即动静分离。

1.2 epoll 事件驱动模型

Nginx 在 Linux 下默认使用 epoll 事件模型:

  • select/poll:每次调用都要把全部待监听的文件描述符集合从用户态拷贝到内核态,并线性遍历所有描述符,复杂度 O(n),且受 FD_SETSIZE(通常 1024)限制。
  • epoll:在内核中维护一棵红黑树保存被监听的描述符,通过回调把"就绪"的描述符放入就绪链表,epoll_wait 只返回真正就绪的事件,复杂度接近 O(1),描述符数量只受系统 ulimit -n 限制。

因此 Nginx 用少量 worker 进程就能承载海量并发连接,这是它资源占用低的根本原因。

事件模型选择:

操作系统 推荐模型
Linux epoll(默认)
FreeBSD / OpenBSD kqueue
Solaris /dev/poll、eventport
不支持高效模型时 select

配置方式:

events {
    use epoll;
}

1.3 master / worker 进程模型

Nginx 启动后有两类进程:

nginx: master process   ← root 启动,负责读配置、绑定端口、管理 worker
 ├── nginx: worker process   ← 以 user 指令指定的普通用户运行,真正处理请求
 ├── nginx: worker process
 ├── nginx: cache manager process   ← 启用 proxy_cache 时出现,清理过期缓存
 └── nginx: cache loader process    ← 启动时把磁盘缓存元数据载入共享内存
  • master 进程:不处理业务请求,只负责加载 / 校验配置文件、绑定监听套接字、fork 与监控 worker 进程、接收管理信号(HUP / TERM / USR1 / USR2)。因为要绑定 80/443 等特权端口,master 通常以 root 运行。
  • worker 进程:数量由 worker_processes 决定,各自独立、平等地争抢并处理连接;单个 worker 崩溃不会影响其他 worker,master 会立即再拉起一个新的。

信号与命令的对应关系:

信号 等价命令 作用
TERM / INT nginx -s stop 快速停止
QUIT nginx -s quit 优雅停止(处理完当前请求)
HUP nginx -s reload 重读配置,启动新 worker,旧 worker 处理完请求后退出
USR1 nginx -s reopen 重新打开日志文件(日志切割用)
USR2 — 平滑升级二进制程序

启动后可以观察进程关系:

ps -ef | grep nginx
root     84432     1  0 10:01 ?  00:00:00 nginx: master process nginx
nobody   84433 84432  0 10:01 ?  00:00:00 nginx: worker process

2. Nginx 安装

2.1 YUM 安装

# 安装 epel 源
yum install epel-release -y
yum clean all
yum makecache

# 安装 nginx
yum install nginx -y

# 启动
systemctl start nginx
systemctl enable nginx

YUM 安装的关键路径:

项目 路径
主配置文件 /etc/nginx/nginx.conf
子配置目录 /etc/nginx/conf.d/*.conf
网站根目录 /usr/share/nginx/html
日志目录 /var/log/nginx/
PID 文件 /run/nginx.pid
模块目录 /usr/share/nginx/modules/*.conf

2.2 源码编译安装

源码安装可以自定义模块、路径和编译参数,是生产环境推荐的方式。

1)下载源码包

wget http://nginx.org/download/nginx-1.16.0.tar.gz

2)创建 Nginx 用户和组

groupadd nginx
useradd -r -s /sbin/nologin -g nginx nginx
id nginx
uid=987(nginx) gid=1001(nginx) 组=1001(nginx)

3)解压

tar -xf nginx-1.16.0.tar.gz -C /usr/src
cd /usr/src/nginx-1.16.0/
ls
auto  CHANGES  CHANGES.ru  conf  configure  contrib  html  LICENSE  man  README  src

4)安装编译依赖

依赖库 对应的 Nginx 模块 说明
pcre / pcre-devel http_rewrite_module Perl 兼容正则表达式库
zlib / zlib-devel http_gzip_module 数据压缩库
openssl / openssl-devel http_ssl_module SSL/TLS 加密通信
gcc / gcc-c++ 编译工具 C/C++ 编译器
yum install -y pcre pcre-devel zlib zlib-devel openssl openssl-devel gcc gcc-c++
yum install -y make perl net-tools

5)配置、编译、安装

# 配置(添加 SSL 支持)
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module

# 可通过以下命令查看所有配置参数
./configure --help

# 编译并安装
make && make install

6)确认安装结果

ls /usr/local/nginx/
conf  html  logs  sbin

7)添加环境变量

echo "export PATH=$PATH:/usr/local/nginx/sbin" >> /etc/profile
source /etc/profile

2.3 手动编译依赖库(不使用 yum 开发包)

如果不能用 yum 安装开发库,也可以逐个下载依赖源码编译,再在 ./configure 时用 --with-pcre / --with-zlib / --with-openssl 指定依赖的源码目录。

Nginx 及依赖包下载地址:

组件 说明 下载地址
Nginx Web 服务器 / 反向代理 http://nginx.org/download/nginx-1.8.0.tar.gz
zlib gzip 模块依赖 http://zlib.net/zlib-1.2.8.tar.gz
PCRE rewrite 模块依赖 http://www.pcre.org/
OpenSSL / openssl-fips SSL 功能依赖 http://www.openssl.org/source/openssl-fips-2.0.9.tar.gz

将安装包放到 /home/zwr/nginx_install 目录下,依次编译安装(注意根据实际下载的版本替换文件名):

cd /home/zwr/nginx_install

# 1. 安装 openssl-fips-2.0.9
tar -zxvf openssl-fips-2.0.9.tar.gz
cd openssl-fips-2.0.9
./config --prefix=/opt/openssl-fips-2.0.9
make && make install

# 2. 安装 zlib-1.2.8
cd /home/zwr/nginx_install
tar -zxvf zlib-1.2.8.tar.gz
cd zlib-1.2.8
./configure --prefix=/opt/zlib-1.2.8
make && make install

# 3. 安装 pcre-8.37
cd /home/zwr/nginx_install
tar -zxvf pcre-8.37.tar.gz
cd pcre-8.37
./configure --prefix=/opt/pcre-8.37
make && make install

依赖就绪后,进入 Nginx 源码目录编译安装。关键是必须带上 --with-http_ssl_module,否则 Nginx 无法支持 HTTPS。

# 4. 安装 nginx
cd /home/zwr/nginx_install
tar -zxvf nginx-1.8.0.tar.gz
cd nginx-1.8.0

./configure \
    --prefix=/opt/nginx-1.8.0 \
    --with-pcre=../pcre-8.37 \
    --with-zlib=../zlib-1.2.8 \
    --with-openssl=../openssl-fips-2.0.9 \
    --with-http_ssl_module
make && make install

如果使用 yum 安装开发库,则编译命令更简洁:

cd /usr/src/nginx-1.8.0
./configure --prefix=/usr/local/nginx --with-http_ssl_module
make && make install

编译安装完成后,校验配置并启动 Nginx:

cd /opt/nginx-1.8.0/sbin      # 或 /usr/local/nginx/sbin
./nginx -t                    # 检测配置是否正确
./nginx                       # 启动
netstat -ntlp | grep nginx    # 查看监听端口

2.4 编译参数与常用模块

参数 说明
--prefix=/usr/local/nginx 指定安装路径
--user=nginx 运行 worker 进程的用户
--group=nginx 运行 worker 进程的组
--with-http_ssl_module 启用 HTTPS/SSL 支持
--with-http_gzip_static_module 启用 gzip 静态压缩
--with-http_stub_status_module 启用状态监控页面
--with-http_realip_module 获取代理链后的客户端真实 IP
--with-http_v2_module 启用 HTTP/2
--with-stream 启用四层(TCP/UDP)代理
--with-pcre=../pcre-8.37 指定 PCRE 源码目录(手动编译依赖时使用)
--with-zlib=../zlib-1.2.8 指定 zlib 源码目录
--with-openssl=../openssl-fips-2.0.9 指定 OpenSSL 源码目录
--add-module=../ngx_cache_purge-2.3/ 添加第三方模块(如缓存清除)

2.5 启动与验证

# 启动
nginx

# 检查端口
lsof -i:80
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx   84432   root    6u  IPv4 142705      0t0  TCP *:http (LISTEN)
nginx   84433 nobody    6u  IPv4 142705      0t0  TCP *:http (LISTEN)
netstat -tunlp | grep nginx
tcp        0      0 0.0.0.0:80    0.0.0.0:*    LISTEN    84432/nginx: master

如果没有 lsof 和 netstat 命令,安装:yum install -y lsof net-tools

浏览器输入服务器 IP,看到 "Welcome to nginx!" 表示启动成功。


3. 目录结构与命令行参数

3.1 源码安装的目录结构

/usr/local/nginx/
├── conf/          配置文件目录
│   ├── nginx.conf         主配置文件
│   ├── nginx.conf.default 主配置文件默认备份
│   ├── mime.types         MIME 类型映射表
│   ├── fastcgi_params     FastCGI 参数
│   └── cert/              (自建)证书存放目录
├── html/          默认网站根目录(index.html、50x.html)
├── logs/          日志目录(access.log、error.log、nginx.pid)
└── sbin/nginx     主程序

3.2 命令行参数

nginx -h
nginx version: nginx/1.16.0
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]

Options:
  -?,-h         : this help
  -v            : show version and exit
  -V            : show version and configure options then exit
  -t            : test configuration and exit
  -T            : test configuration, dump it and exit
  -q            : suppress non-error messages during configuration testing
  -s signal     : send signal to a master process: stop, quit, reopen, reload
  -p prefix     : set prefix path (default: /usr/local/nginx/)
  -c filename   : set configuration file (default: conf/nginx.conf)
  -g directives : set global directives out of configuration file

常用命令:

命令 说明
nginx -v 查看版本
nginx -V 查看版本和编译参数(用于确认是否含 --with-http_ssl_module)
nginx -t 测试配置文件语法
nginx -T 测试配置并输出完整配置内容(含 include 展开)
nginx -s stop 快速停止 Nginx
nginx -s quit 优雅停止 Nginx(处理完当前请求)
nginx -s reload 平滑重载配置(不中断服务)
nginx -s reopen 重新打开日志文件
nginx -c /path/nginx.conf 指定配置文件启动
# 确认已编译进 SSL 模块
/opt/nginx-1.8.0/sbin/nginx -V

修改配置文件后先 nginx -t 检查语法,再使用 nginx -s reload 即可在不重启的情况下使配置生效。


4. nginx.conf 结构详解

4.1 配置文件结构总览

Nginx 配置文件主要分为 4 个部分:

# 全局块(main):影响所有其他部分的设置
user nginx;
worker_processes auto;
error_log logs/error.log;
pid logs/nginx.pid;

# events 块:影响 Nginx 服务器与客户端的网络连接
events {
    worker_connections 1024;
}

# http 块:HTTP 服务相关全局配置
http {
    include mime.types;
    default_type application/octet-stream;

    # upstream 块:上游服务器(反向代理 / 负载均衡)
    upstream backend {
        server 192.168.1.10:8080;
        server 192.168.1.11:8080;
    }

    # server 块:虚拟主机配置
    server {
        listen 80;
        server_name localhost;

        # location 块:URL 匹配后的处理
        location / {
            root html;
            index index.html index.htm;
        }
    }
}

各部分的关系:

  • server 继承 main
  • location 继承 server
  • upstream 既不会继承指令,也不会被继承,它有自己的特殊指令

配置文件层级关系图:

main(全局配置)
├── events(网络连接事件)
└── http(HTTP 服务全局配置)
    ├── upstream(后端服务器组)
    └── server(虚拟主机)
        └── location(URL 匹配)

4.2 main 段(全局块)

全局块设置的指令将影响其他所有部分。

# 运行 worker 进程的用户和组
user nginx nginx;

# worker 工作进程数,auto 表示自动匹配 CPU 核心数
worker_processes auto;

# CPU 粘性(高并发时减少多核切换开销)
# 四核示例:worker_cpu_affinity 0001 0010 0100 1000;
worker_cpu_affinity auto;

# 错误日志路径和级别(debug | info | notice | warn | error | crit)
error_log logs/error.log;
# error_log logs/error.log notice;
# error_log logs/error.log info;

# PID 文件路径
pid logs/nginx.pid;

# worker 进程可以打开的最大文件描述符数(默认未设置,系统上限为 65535)
worker_rlimit_nofile 65535;
指令 说明
user 指定 worker 进程运行的用户和组,决定了对网站目录的读写权限
worker_processes worker 进程数量,auto 自动等于 CPU 核心数
worker_cpu_affinity 把 worker 绑定到指定 CPU 核心(CPU 亲和性)
error_log 错误日志文件与级别
pid master 进程 PID 文件路径,systemd 的 PIDFile 必须与之一致
worker_rlimit_nofile 单个 worker 可打开的最大文件描述符数

worker_processes 设置建议:

  • 可设为 CPU 核心数:grep ^processor /proc/cpuinfo | wc -l
  • 设为 auto 自动检测
  • 如果开启了 SSL 和 gzip,可设为逻辑 CPU 数量甚至 2 倍以减少 I/O 操作
  • 如果服务器还有其他服务,应适当减少

worker_cpu_affinity: 在高并发情况下,通过设置 CPU 粘性来降低多核切换造成的寄存器现场重建开销。四核显式写法:

worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;

error_log 级别由低到高:debug、info、notice、warn、error、crit、alert、emerg。生产环境建议 warn 或 error,排错时临时调为 info / debug(debug 需编译时带 --with-debug)。

4.3 events 段

events 块配置 Nginx 服务器与客户端的网络连接行为。

events {
    # 使用 epoll 事件模型(Linux 下高效模型)
    use epoll;

    # 每个 worker 进程的最大并发连接数
    worker_connections 65535;

    # 一个 worker 同时接收多个新连接(可选)
    multi_accept on;
}

worker_connections: 每个 worker 进程能并发处理的最大连接数(包含与客户端或后端被代理服务器间的所有连接数)。

最大连接数计算公式:

Nginx 角色 公式
HTTP 服务器 worker_processes × worker_connections / 2
反向代理服务器 worker_processes × worker_connections / 4

作为 HTTP 服务器时,浏览器的一个连接会占用 Nginx 2 个描述符(客户端连接 + 打开的文件);作为反向代理时,还要额外占用与后端的连接,所以除以 4。

worker_connections 值不能超过 worker_rlimit_nofile 的设置。

4.4 http 段基础指令

http 块包含 HTTP 服务相关的全局配置,如文件传输、日志、压缩、代理等。

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

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # 客户端请求体大小限制(上传文件时需调整)
    client_max_body_size 10m;
    client_body_buffer_size 128k;

    # 响应客户端超时
    send_timeout 60;

    # 隐藏版本号
    server_tokens off;

    # 引入子配置文件
    include /etc/nginx/conf.d/*.conf;
}

指令说明:

指令 说明
include mime.types 引入 MIME 类型映射表;也可用 include conf.d/*.conf 拆分虚拟主机配置
default_type application/octet-stream 默认 MIME 类型(二进制流)
sendfile on 开启高效文件传输(零拷贝),减少内核态到用户态的上下文切换。下载等磁盘 I/O 重负载应用可设为 off
tcp_nopush on 在 sendfile 开启时有效,合并数据包一次发送,防止网络阻塞
tcp_nodelay on 长连接时禁用 Nagle 算法,提高数据实时性
keepalive_timeout 65 长连接超时时间(秒)。大量小文件可减少重建连接开销;大文件上传需注意超时
send_timeout 响应客户端超时,仅限于两个连接活动之间的时间
client_max_body_size 允许客户端请求的最大单文件字节数(上传大文件必须调大,否则报 413)
client_body_buffer_size 缓冲区代理缓冲客户端请求的最大字节数
client_header_timeout 读取客户端请求头超时
client_body_timeout 读取客户端请求体超时
types_hash_max_size MIME 类型哈希表大小
server_tokens off 响应头中隐藏 Nginx 版本号

生产环境常见的一组客户端与超时参数(来自反向代理实战配置):

http {
    # 缓冲区代理缓冲用户端请求的最大字节数,可理解为保存到本地再传给用户
    client_max_body_size 50m;
    client_body_buffer_size 256k;
    client_header_timeout 3m;
    client_body_timeout   3m;
    send_timeout          3m;
}

4.5 MIME 类型

mime.types 文件定义了文件扩展名与 MIME 类型的映射,Nginx 根据它设置 Content-Type 响应头。当请求的文件类型未匹配到时,使用 default_type。

4.6 gzip 压缩

http {
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.0;
    gzip_comp_level 6;
    gzip_types text/html text/plain text/css text/javascript
               application/json application/javascript
               application/x-javascript application/xml;
    gzip_vary on;
    gzip_proxied any;
    gzip_disable "MSIE [1-6]\.";
}

指令说明:

指令 说明
gzip on 开启 gzip 压缩输出
gzip_min_length 1k 允许压缩的最小字节数(从 Content-Length 获取),建议大于 1k,小于 1k 可能越压越大
gzip_buffers 4 16k 以 16k 为单位,申请原始数据大小 4 倍的内存
gzip_http_version 1.0 识别 HTTP 协议版本,默认 1.1
gzip_comp_level 6 压缩比 1-9,1 最快压缩率最低,9 最慢压缩率最高(消耗 CPU),推荐 4-6
gzip_types 匹配 MIME 类型压缩,text/html 总是会被压缩
gzip_vary on 在响应头添加 Vary: Accept-Encoding,让前端缓存服务器缓存压缩后的页面
gzip_proxied any 反向代理时启用,后端返回含 Via 头时压缩
gzip_disable 对匹配 User-Agent 的客户端禁用压缩(如低版本 IE)

验证压缩是否生效:

curl -I -H "Accept-Encoding: gzip" http://www.example.com/index.html
# 响应头出现 Content-Encoding: gzip 即成功

4.7 server 段(虚拟主机)

每个 server 块代表一个虚拟主机,通过监听地址/端口和域名来区分。

server {
    listen       80;
    server_name  localhost;
    root         /usr/local/nginx/html;
    index        index.html index.htm;
    charset      utf-8;
    access_log   logs/host.access.log main;
}
指令 说明
listen 80 监听端口,小于 1024 需要 root 启动。可写为 listen *:80、listen 127.0.0.1:80、listen 80 default_server、listen [::]:80
server_name 服务器域名,支持多个域名、通配符(*.example.com)和正则匹配
root 网站根目录
index 默认首页文件,按顺序查找
charset 页面默认字符集
access_log 该虚拟主机独立的访问日志
error_page 自定义错误页面

4.8 location 段(基本用法)

location 用于匹配特定 URL 并定义处理方式。

语法:

location [ = | ~ | ~* | ^~ ] /uri/ {
    # 配置
}
修饰符 说明
= 精确匹配,优先级最高
^~ 普通字符匹配,匹配成功后不再匹配正则
~ 区分大小写的正则匹配
~* 不区分大小写的正则匹配
/uri/ 普通前缀匹配
/ 通用匹配,默认匹配

匹配优先级(从高到低):

  1. = 精确匹配
  2. ^~ 前缀匹配(匹配后停止正则匹配)
  3. ~ / ~* 正则匹配(按配置文件中的顺序,第一个匹配生效)
  4. 普通前缀匹配(最长匹配优先)
  5. / 通用匹配

示例:

# 精确匹配
location = / {
    # 只匹配根目录
}

# 区分大小写的正则匹配
location ~ \.php$ {
    # 匹配 .php 结尾的请求
}

# 不区分大小写的正则匹配
location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
    # 匹配静态资源
}

# 前缀匹配
location ^~ /images/ {
    # 匹配 /images/ 开头的请求,不再进行正则匹配
}

# 通用匹配
location / {
    # 所有未匹配的请求
}

详见 10-03-Nginx-location匹配规则.md


5. 虚拟主机

5.1 基于 IP 的虚拟主机

为服务器绑定多个 IP,每个 IP 对应一个虚拟主机:

server {
    listen 80;
    server_name 192.168.0.30;

    location / {
        root test1;
        index index.html index.htm;
    }
}

server {
    listen 80;
    server_name 192.168.0.65;

    location / {
        root test2;
        index index.html index.htm;
    }
}
# 绑定虚拟 IP
ifconfig ens33:0 192.168.0.65/24

# 创建测试页面
mkdir /usr/local/nginx/test1 /usr/local/nginx/test2
echo "test1" > /usr/local/nginx/test1/index.html
echo "test2" > /usr/local/nginx/test2/index.html

nginx -s reload

验证:

curl 192.168.0.30   # 输出 test1
curl 192.168.0.65   # 输出 test2

5.2 基于端口的虚拟主机

同一 IP 的不同端口对应不同虚拟主机:

server {
    listen 80;
    server_name 1.1.1.3;

    location / {
        root test1;
        index index.html index.htm;
    }
}

server {
    listen 8080;
    server_name 1.1.1.3;

    location / {
        root test2;
        index index.html index.htm;
    }
}

验证:

curl 1.1.1.3        # test1
curl 1.1.1.3:8080   # test2

5.3 基于域名的虚拟主机

通过 DNS 解析将不同域名指向同一 IP,Nginx 根据 Host 头区分:

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

    location / {
        root /nginx/web;
        index index.html index.htm;
    }
}

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

    location / {
        root /nginx/web2;
        index index.html index.htm;
    }
}

本地测试可在 /etc/hosts 添加:

1.1.1.3  www.example.com api.example.com

验证:

curl http://www.example.com   # web 目录
curl http://api.example.com   # web2 目录

基于域名的虚拟主机是生产环境最常用的方式,只需一个 IP 即可承载大量站点。


6. 静态资源与目录浏览

6.1 root 与 index

location / {
    root   /var/www/html;
    index  index.html index.htm index.php;
}
  • root:定义请求对应的文件根目录,最终文件路径 = root + URI
  • index:定义默认访问的文件名,按顺序查找

6.2 静态资源缓存与过期时间

location ~* \.(gif|jpg|jpeg|png|ico|css|js|svg|woff2?)$ {
    expires 30d;
    add_header Cache-Control "public, no-transform";
}

expires 设置 Expires 与 Cache-Control 响应头,让浏览器在有效期内直接使用本地缓存,减少回源请求。

6.3 目录浏览 autoindex

Nginx 默认不允许列出目录内容。开启方式:

location /images/ {
    root /var/www/nginx-default/images;
    autoindex on;
    autoindex_exact_size off;
    autoindex_localtime on;
}
指令 说明
autoindex on 开启目录浏览
autoindex_exact_size off on 显示精确字节数;off 显示 kB/MB/GB
autoindex_localtime on on 显示服务器本地时间;off 显示 GMT 时间

生产环境的网站根目录一般应关闭 autoindex,仅在内部下载站点开启。

6.4 错误页面

error_page 404 /404.html;

error_page 500 502 503 504 /50x.html;
location = /50x.html {
    root html;
}

可以自定义错误页面,将不同错误码重定向到指定页面。


7. 访问控制

7.1 基于 IP 的 allow / deny

访问控制可以放在 location、server 或 http 块中。规则按顺序匹配,满足任一规则即停止。

写法一:location 级控制

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

    location / {
        root /nginx/web;
        index index.html index.htm;
        deny 192.168.0.117;
        allow all;
    }
}

写法二:server 级控制

server {
    listen 80;
    server_name www.example.com;
    deny 192.168.0.117;
    allow all;

    location / {
        root /nginx/web;
        index index.html index.htm;
    }
}

写法三:http 全局控制

http {
    deny 192.168.0.117;
    allow all;

    server {
        listen 80;
        server_name www.example.com;
        location / {
            root /nginx/web;
            index index.html index.htm;
        }
    }
}

注意: 如果全局和局部都配置了且冲突,局部配置优先。

IP 段控制示例:

location /nginx-status {
    stub_status on;
    access_log off;
    allow 192.168.10.100;
    allow 172.29.73.0/24;
    deny all;
}

常见的禁止访问规则:

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

# 禁止访问敏感目录
location ~ ^/(WEB-INF)/ {
    deny all;
}

7.2 基本认证 basic auth 与 htpasswd

使用 htpasswd 生成密码文件(该命令来自 httpd-tools / httpd-devel 包):

yum install -y httpd-tools

# 创建密码文件并添加用户 admin(-c 表示新建文件,会覆盖已有文件)
htpasswd -c /usr/local/nginx/htpasswd admin

# 再添加用户 sean(不要加 -c,否则覆盖)
htpasswd /usr/local/nginx/htpasswd sean

# 查看
cat /usr/local/nginx/htpasswd

在配置中启用:

location /nginxstatus {
    stub_status on;
    access_log off;
    auth_basic "NginxStatus";
    auth_basic_user_file /usr/local/nginx/htpasswd;
    allow 192.168.10.0/24;
    deny all;
}
指令 说明
auth_basic "提示语" 开启基本认证,字符串为弹窗提示;设为 off 可在子 location 中关闭继承的认证
auth_basic_user_file 密码文件路径,需 Nginx 运行用户可读,建议权限 640

访问时浏览器会弹出用户名密码框;命令行验证:

curl -u admin:123456 http://192.168.10.10/nginxstatus

7.3 stub_status 状态监控

编译时需添加 --with-http_stub_status_module 参数。

location /nginxstatus {
    stub_status on;
    access_log on;
    auth_basic "nginxstatus";
}

访问 http://your-server/nginxstatus:

Active connections: 3
server accepts handled requests
 206909 206909 207011
Reading: 0 Writing: 1 Waiting: 2
指标 说明
Active connections 当前活动连接数
accepts 已接收的连接总数
handled 成功创建握手的连接总数
requests 总共处理的请求数
Reading 正在读取请求头的连接数
Writing 正在发送响应的连接数
Waiting 等待中的空闲连接数(长连接)

若 accepts 与 handled 不相等,说明有连接被丢弃,通常是超过了 worker_connections 或文件描述符上限。


8. 日志管理

8.1 log_format 与 access_log

在 nginx.conf 的 http 段中定义日志格式并启用:

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  logs/access.log  main;

    # 关闭某个 location 的日志
    # access_log off;
}

access_log 可以出现在 http、server、location 中,就近生效;log_format 只能定义在 http 段。

8.2 日志变量全表

变量 说明
$remote_addr 客户端 IP 地址
$remote_user 客户端用户名(基本认证)
$time_local 访问时间和时区
$request 请求的 URI 和 HTTP 协议
$status HTTP 响应状态码
$body_bytes_sent 发送给客户端的文件体内容字节数
$bytes_sent 发送给客户端的总字节数(含响应头)
$http_referer 来源页面 URL
$http_user_agent 客户端浏览器信息
$http_x_forwarded_for 经过代理时的客户端真实 IP
$request_time 请求处理总耗时(秒,精度毫秒)
$upstream_addr 后端服务器地址(反向代理时)
$upstream_status 后端返回的状态码
$upstream_response_time 后端响应耗时
$upstream_cache_status 缓存状态(HIT / MISS / EXPIRED / BYPASS / UPDATING / STALE)
$host 请求头中的 Host
$server_name 匹配到的虚拟主机名
$request_uri 完整的原始请求 URI(含参数)
$uri 规范化后的 URI(不含参数)
$args / $query_string 请求参数
$is_args 若有参数则为 ?,否则为空
$scheme 协议(http / https)
$request_method 请求方法(GET / POST 等)

排查性能问题时常用的扩展格式:

log_format  timed  '$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 '
                   'cache=$upstream_cache_status';

8.3 日志切割脚本

vim /usr/local/nginx/sbin/nginx_logrotate.sh
#!/bin/bash
logpath="/usr/local/nginx/logs"

year=$(date -d 'yesterday' +'%Y')
month=$(date -d 'yesterday' +'%m')
day=$(date -d 'yesterday' +'%Y-%m-%d')

mkdir -p "$logpath/$year/$month"
mv "$logpath/access.log" "$logpath/$year/$month/$day.accesslog"

# 发送 USR1 信号让 Nginx 重新生成空日志文件
kill -USR1 $(cat /usr/local/nginx/logs/nginx.pid)

加入定时任务:

crontab -e
01 2 * * *  sh /usr/local/nginx/sbin/nginx_logrotate.sh

关键点:mv 之后旧的文件句柄仍被 worker 持有,必须发送 USR1(等价 nginx -s reopen)让 Nginx 重新创建并打开新的日志文件,否则新日志会继续写入被改名的文件。

8.4 使用 logrotate 切割

也可以使用系统自带的 logrotate 工具(YUM 安装时已自带 /etc/logrotate.d/nginx)。源码安装可手动编写:

vim /etc/logrotate.d/nginx
/usr/local/nginx/logs/*.log {
    daily
    rotate 30
    missingok
    notifempty
    dateext
    compress
    delaycompress
    sharedscripts
    postrotate
        [ -f /usr/local/nginx/logs/nginx.pid ] && kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`
    endscript
}
参数 说明
daily 每天切割(可选 weekly / monthly / size 100M)
rotate 30 保留 30 份历史日志
missingok 日志文件不存在时不报错
notifempty 文件为空时不切割
dateext 切割后的文件名带日期后缀
compress / delaycompress 压缩历史日志,延迟一轮再压缩
sharedscripts 多个日志文件只执行一次 postrotate
postrotate ... endscript 切割后执行的命令,此处发送 USR1 重新打开日志

手动测试:

logrotate -vf /etc/logrotate.d/nginx

9. 反向代理

9.1 基础反向代理

架构说明:

client (3.3.3.x)
    |
    |  eth0: 3.3.3.5
    +-- Nginx 反向代理 --+
       eth1: 192.168.224.10
            |
            |
       Web 服务器: 192.168.224.11

在 Nginx 服务器上:

yum install -y nginx

编辑 /etc/nginx/nginx.conf,在 server 块中添加 location:

server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  3.3.3.5;
    root         /usr/share/nginx/html;
    index        index.php index.html;

    location / {
        proxy_pass http://192.168.224.11/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
    }

    error_page 404 /404.html;
    location = /40x.html {
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    }
}
systemctl restart nginx
lsof -i:80

客户端访问 http://3.3.3.5/ 即可看到后端 Web 服务器的内容。

验证缓存: 关闭后端 Web,客户端也无法访问,说明 Nginx 默认没有缓存功能。

proxy_pass 末尾斜线的区别:

写法 请求 /nba/1.html 转发到后端的路径
proxy_pass http://backend;(无 /) /nba/1.html(保留原始 URI)
proxy_pass http://backend/;(有 /) /1.html(去掉 location 匹配的前缀)

9.2 proxy_set_header 说明

以下两句是外网转内网双网段架构必需的配置:

proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
指令 说明
proxy_set_header Host $host 将客户端请求的原始 Host 头传递给后端,后端虚拟主机才能正确匹配
proxy_set_header X-Real-IP $remote_addr 传递客户端真实 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for 追加代理链中的客户端 IP(比 $remote_addr 更完整)
proxy_set_header X-Forwarded-Proto $scheme 告知后端原始请求协议,HTTPS 卸载时必需

为什么需要代理?

  • 安全 DMZ: 后端服务器隐藏在内网,不直接暴露给客户端
  • 缓存加速: 代理服务器可缓存静态内容,减轻后端压力
  • 统一入口: 便于做负载均衡、限流、灰度、SSL 卸载

9.3 超时与缓冲参数

http {
    proxy_connect_timeout 75;
    proxy_send_timeout 75;
    proxy_read_timeout 75;
    proxy_buffer_size 4k;
    proxy_buffers 4 32k;
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;
    proxy_max_temp_file_size 1024m;
    proxy_temp_path /usr/local/nginx/proxy_temp 1 2;
}

指令说明:

指令 说明
proxy_connect_timeout Nginx 与后端服务器连接超时时间
proxy_read_timeout 连接成功后,与后端两个成功响应之间的超时时间(后端处理慢导致 504 时需调大)
proxy_send_timeout 向后端服务器发送请求的超时时间
proxy_buffer_size 4k 保存后端响应头信息的缓冲区大小
proxy_buffers 4 32k 单个连接缓存后端响应的缓冲区数量和大小(网页平均在 32k 以下时这样设置)
proxy_busy_buffers_size 64k 高负荷下缓冲大小(通常为 proxy_buffers 的 2 倍)
proxy_max_temp_file_size 响应超出缓冲区时写入临时文件的最大值,设为 0 禁用
proxy_temp_file_write_size 每次写临时文件的大小,大于该值时将从 upstream 服务器传递请求而不缓冲到磁盘
proxy_temp_path 临时文件目录及目录层级
proxy_ignore_client_abort on 不允许代理端主动关闭连接
proxy_next_upstream 在何种情况下把请求转给下一台后端,如 error timeout http_502 http_503

生产反向代理常用的完整 http 段参数示例:

http {
    include       mime.types;
    default_type  application/octet-stream;
    include       /opt/nginx-1.8.0/conf/reverse-proxy.conf;

    sendfile        on;
    keepalive_timeout 65;
    gzip            on;

    # 缓冲区代理缓冲用户端请求的最大字节数,可理解为保存到本地再传给用户
    client_max_body_size 50m;
    client_body_buffer_size 256k;
    client_header_timeout 3m;
    client_body_timeout   3m;
    send_timeout          3m;

    # Nginx 与后端服务器连接超时时间(代理连接超时)
    proxy_connect_timeout 300s;
    # 连接成功后,后端服务器响应时间(代理接收超时)
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;

    # 保存用户头信息的缓冲区大小
    proxy_buffer_size 64k;
    # proxy_buffers 缓冲区,网页平均在 32k 以下时这样设置
    proxy_buffers 4 32k;
    # 高负荷下缓冲大小(proxy_buffers * 2)
    proxy_busy_buffers_size 64k;
    # 缓存文件夹大小,大于该值时将从 upstream 服务器传递请求而不缓冲到磁盘
    proxy_temp_file_write_size 64k;
    # 不允许代理端主动关闭连接
    proxy_ignore_client_abort on;

    server {
        listen       80;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
            root html;
        }
    }
}

9.4 网站数据切分(七层调度)

网站数据切分本质上是七层调度。例如将新闻和体育内容分发到不同后端服务器。

架构:

client
    |
Nginx (3.3.3.5)
    |
    +-- web1 (192.168.224.11)  -- /nba/
    +-- web2 (192.168.224.12)  -- /cba/

在 server 块中配置:

location /nba/ {
    proxy_pass http://192.168.224.11/;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
}

location /cba/ {
    proxy_pass http://192.168.224.12/;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
}
systemctl restart nginx

客户端验证:

  • http://3.3.3.5/nba/ → web1
  • http://3.3.3.5/cba/ → web2

9.5 动静分离

根据文件扩展名将静态资源和动态请求分发到不同后端:

# 静态资源 → web1
location ~ \.(html|htm|gif|jpeg|jpg|css|js|png|swf)$ {
    proxy_pass http://192.168.224.11;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
}

# 动态请求 → web2
location ~ \.(php|cgi|txt)$ {
    proxy_pass http://192.168.224.12;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
}

在两台 Web 服务器上分别创建 1.txt 和 1.html,访问 http://nginxIP/1.txt 和 http://nginxIP/1.html 观察内容差异。


10. 负载均衡(upstream)

10.1 upstream 基本配置

在 http 块中、server 块之前定义后端服务器组:

http {
    upstream backendweb {
        server 192.168.224.11 weight=1 max_fails=2 fail_timeout=1s;
        server 192.168.224.12 weight=1 max_fails=2 fail_timeout=1s;
    }

    server {
        listen 80;
        server_name 3.3.3.5;

        location ~ \.(txt|html)$ {
            proxy_pass http://backendweb;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }
}

backendweb 是自定义名称,在 proxy_pass 中通过 http://backendweb 引用。

10.2 负载均衡算法

算法 说明
round-robin 轮询(默认),平均分配请求
weight 权重,数值越大分配越多,用于后端服务器性能不均的情况
ip_hash 按客户端 IP 哈希分配,实现会话保持
least_conn 最少连接优先,把请求交给当前活动连接数最少的后端
hash $request_uri 按 URL 哈希分配,提高缓存命中率
fair 按后端响应时间分配(需要第三方模块 upstream_fair)
url_hash 按访问 URL 的哈希结果分配(第三方模块)

权重模式:

upstream backendweb {
    server 192.168.224.11 weight=3;
    server 192.168.224.12 weight=1;
}

最少连接: 适合请求处理时长差异较大的场景(如长连接、大文件下载)。

upstream backendweb {
    least_conn;
    server 192.168.224.11;
    server 192.168.224.12;
}

10.3 健康检查与 backup / down

参数 说明
weight=N 权重,默认 1
max_fails=N 允许失败的次数,超过则标记为不可用
fail_timeout=Ns 失败超时时间(秒),在此时间内失败 max_fails 次则认为不健康,并在该时间内不再分配请求
backup 备用服务器,主服务器全部宕机后才启用
down 标记为永久离线(常用于临时下线维护某台机器)
max_conns=N 该后端的最大并发连接数
upstream backendweb {
    server 192.168.224.11 weight=1 max_fails=2 fail_timeout=1s;
    server 192.168.224.12 weight=1 max_fails=2 fail_timeout=1s;
    server 192.168.224.13 down;          # 维护中,不参与调度
    server 192.168.224.14 backup;        # 备机,前面都挂了才启用
}

配合 proxy_next_upstream 让失败请求自动重试下一台:

location / {
    proxy_pass http://backendweb;
    proxy_next_upstream error timeout http_502 http_503 http_504;
}

Nginx 开源版的健康检查是被动的:只有真实请求失败才会标记后端不可用。若需要主动探测,可使用 Tengine 的 ngx_http_upstream_check_module 或商业版 Nginx Plus。

10.4 会话保持 ip_hash

同一 IP 客户端一旦被调度到某台后端,后续请求始终调度到同一台:

upstream backendweb {
    ip_hash;
    server 192.168.224.11 weight=1 max_fails=2 fail_timeout=1s;
    server 192.168.224.12 weight=1 max_fails=2 fail_timeout=1s;
}

注意: ip_hash 按客户端 IP 的网段进行哈希。如果测试客户端都在同一内网网段,可能都会被调度到同一台后端。公网不同网段的客户端 IP 则会均匀分配。

优点:配置方便;缺点:不能完全做到负载均衡。此外 ip_hash 不能与 weight、backup 同时使用。

其他会话保持方案:

  • 后端应用把 Session 存到 Redis / Memcached(推荐,彻底解耦)
  • 使用 sticky 第三方模块基于 Cookie 保持
  • 使用 hash $cookie_jsessionid 按 Cookie 哈希

10.5 url_hash 提高缓存命中率

使用 hash $request_uri 将同一 URL 的请求只调度给同一台后端缓存服务器:

upstream squid {
    hash $request_uri;
    server 192.168.224.12 weight=1 max_fails=2 fail_timeout=3s;
    server 192.168.224.13 weight=1 max_fails=2 fail_timeout=3s;
}

新请求仍然会轮询,但已请求过的 URL 再次访问时会调度到第一次缓存它的服务器,提高缓存命中率。加上 consistent 参数(hash $request_uri consistent;)可在增删后端时减少缓存失效范围。

负载均衡需要注意的四个方面:

  1. 算法:round-robin / weight / ip_hash / least_conn / hash 等
  2. 健康检查:max_fails + fail_timeout
  3. 会话保持:ip_hash 或 sticky 模块,或将 Session 外置
  4. 数据一致:rsync / drbd / 共享存储 / 分布式存储

11. 代理缓存(proxy_cache)

Nginx 自身默认不缓存后端响应,需要显式配置 proxy_cache_path + proxy_cache;若还需要按 URL 主动清除缓存,则要编译第三方模块 ngx_cache_purge。

11.1 编译缓存清除模块

# 安装依赖
yum install pcre-devel zlib-devel gcc gcc-c++ -y

# 下载源码
wget http://nginx.org/download/nginx-1.14.2.tar.gz
wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz

# 解压
tar xf nginx-1.14.2.tar.gz -C /usr/src/
tar xf ngx_cache_purge-2.3.tar.gz -C /usr/src/

# 编译(添加缓存模块)
cd /usr/src/nginx-1.14.2/
./configure --prefix=/usr/local/nginx \
    --user=nginx --group=nginx \
    --with-http_gzip_static_module \
    --with-http_stub_status_module \
    --add-module=../ngx_cache_purge-2.3/

make && make install

11.2 缓存配置详解

架构:

client
    |
Nginx 反向代理 + 缓存 (3.3.3.5)
    |
LNMP (3.3.3.3)
user  nginx nginx;
worker_processes  2;
error_log  logs/error.log  info;
pid        logs/nginx.pid;

events {
    worker_connections  65535;
    use epoll;
}

http {
    include       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" "$upstream_cache_status"';

    sendfile        on;
    tcp_nopush     on;
    keepalive_timeout  65;
    gzip on;

    # 临时文件目录
    proxy_temp_path   /usr/local/nginx/proxy_temp_dir 1 2;

    # 缓存路径配置
    proxy_cache_path  /usr/local/nginx/proxy_cache_dir/cache
                      levels=1:2
                      keys_zone=cache:100m
                      inactive=1d
                      max_size=10g;

    upstream web {
        server 3.3.3.3 weight=1 max_fails=2 fail_timeout=30s;
    }

    server {
        listen       80;
        server_name  3.3.3.5;
        access_log  logs/host.access.log  main;

        # 静态内容缓存
        location / {
            proxy_pass   http://web;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;

            proxy_cache cache;
            proxy_cache_key $host$uri$is_args$args;
            proxy_cache_valid 200 304 10m;

            add_header X-Cache '$upstream_cache_status from $host';
            expires 1d;
        }

        # PHP 等动态请求不缓存
        location ~ .*\.(php|cgi)$ {
            proxy_pass   http://web;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }
}

proxy_cache_path 参数说明:

参数 说明
/usr/local/nginx/proxy_cache_dir/cache 缓存文件存放目录
levels=1:2 缓存目录层级,第一级 1 个字符,第二级 2 个字符,如 cache/a/1b
keys_zone=cache:100m 共享内存区名称为 cache,分配 100MB 内存(存放缓存 key 与元数据)
inactive=1d 缓存文件 1 天未被访问则由 cache manager 进程删除
max_size=10g 缓存硬盘最大容量 10GB

server / location 段缓存指令说明:

指令 说明
proxy_cache cache 指定使用的共享内存区名称
proxy_cache_key $host$uri$is_args$args 设置缓存 key
proxy_cache_valid 200 304 10m 对 200/304 响应缓存 10 分钟
proxy_cache_min_uses N 请求 N 次后才开始缓存
proxy_cache_methods 允许缓存的请求方法,默认 GET HEAD
proxy_no_cache / proxy_cache_bypass 满足条件时不写缓存 / 不读缓存
add_header X-Cache '$upstream_cache_status' 响应头显示缓存状态
expires 1d 设置浏览器缓存过期时间为 1 天

$upstream_cache_status 可能值:

状态 说明
HIT 缓存命中
MISS 未命中,从后端获取
EXPIRED 缓存已过期,已回源刷新
BYPASS 命中 proxy_cache_bypass 规则,直接回源
UPDATING 缓存过期但正在被其他请求更新,返回旧内容
STALE 后端异常,返回过期的旧缓存

创建缓存目录并启动:

mkdir -p /usr/local/nginx/proxy_cache_dir/cache
/usr/local/nginx/sbin/nginx

# 如遇句柄数限制
ulimit -n 65535

11.3 缓存验证

# 第一次请求 — MISS
curl -I http://3.3.3.5/static/image/common/logo.png
HTTP/1.1 200 OK
Server: nginx/1.14.2
...
X-Cache: MISS from 3.3.3.5
# 第二次请求 — HIT
curl -I http://3.3.3.5/static/image/common/logo.png
HTTP/1.1 200 OK
Server: nginx/1.14.2
...
X-Cache: HIT from 3.3.3.5

查看缓存目录结构:

ls /usr/local/nginx/proxy_cache_dir/cache
0  1  2  3  5  6  7  9  a  b  d  e  f

11.4 缓存清除脚本

vim /tmp/purge_nginx_cache.sh
#!/bin/bash
cachedir=/usr/local/nginx/proxy_cache_dir/cache

grep -ra "$1" "$cachedir" | grep "$1" | awk -F':' '{print $1,$3}' | while read cacheurl url; do
    rm -rf "$cacheurl"
    echo "$url 被清除"
done
echo "缓存清除成功"

使用方法:

# 清除所有 .png 结尾的缓存
sh /tmp/purge_nginx_cache.sh .png$

# 手动搜索缓存文件(末尾要有 $)
grep -ra .png$ /usr/local/nginx/proxy_cache_dir/cache/

编译了 ngx_cache_purge 后,也可以配置一个专用 location 通过 URL 清除:

location ~ /purge(/.*) {
    allow 127.0.0.1;
    allow 192.168.0.0/24;
    deny all;
    proxy_cache_purge cache $host$1$is_args$args;
}

访问 http://3.3.3.5/purge/static/image/common/logo.png 即可清除对应缓存。


12. Squid 缓存加速架构(扩展实验)

12.1 架构说明

client (192.168.1.x)
    |
    |  公网 IP: 192.168.1.18 (test.squid.com)
Nginx 反向代理
    |  内网 IP: 3.3.3.5 (双网卡)
    |
    +-- Squid 缓存 (3.3.3.4)
    |       命中(hit) → 直接返回
    |       未命中(miss) → 从后端获取
    |
    +-- 动态 .php 直接转发
    |
LNMP 后端 (3.3.3.3)

实验前准备:

  1. 所有机器配置主机名并在 /etc/hosts 互相绑定
  2. 关闭 firewalld 和 selinux
  3. 关闭 NetworkManager,配置静态 IP
  4. 配置本地 yum、epel 源
  5. 时间同步

12.2 后端 LNMP 配置

在 LNMP 服务器(3.3.3.3)上安装相关 RPM 包:

yum install epel-release -y
yum install -y nginx
yum install mariadb mariadb-server php php-mysql php-gd libjpeg* \
    php-ldap php-odbc php-pear php-xml php-xmlrpc php-mbstring \
    php-bcmath php-mhash php-fpm php-pecl-zendopcache -y

systemctl restart mariadb.service
systemctl enable mariadb.service

配置 PHP-FPM(/etc/php-fpm.d/www.conf):

listen = /var/run/php-fpm/fastcgi.socket
php_flag[display_errors] = on
user = nginx
group = nginx
listen.owner = nginx
listen.group = nginx
chown nginx.nginx /var/run/php-fpm/
systemctl start php-fpm.service
systemctl enable php-fpm.service

配置 Nginx(/etc/nginx/nginx.conf):

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    use epoll;
    worker_connections 65535;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  192.168.224.11;
        root         /usr/share/nginx/html;
        index        index.php index.html;

        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }

        location ~ \.php$ {
            fastcgi_pass    unix:/var/run/php-fpm/fastcgi.socket;
            fastcgi_index   index.php;
            fastcgi_param   SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include         fastcgi_params;
        }
    }
}
systemctl start nginx.service
systemctl enable nginx.service

部署 Discuz 论坛:

mkdir /usr/share/nginx/html/discuz/
unzip Discuz_X3.3_SC_UTF8.zip -d /usr/share/nginx/html/discuz/
cd /usr/share/nginx/html/discuz/
mv upload/* .
rm upload/ -rf
chown nginx.nginx /usr/share/nginx/html/discuz -R

数据库授权:

MariaDB [(none)]> create database discuz;
MariaDB [(none)]> grant all on discuz.* to 'discuz'@'localhost' identified by '123';
MariaDB [(none)]> flush privileges;

浏览器访问 http://3.3.3.3/discuz/ 进行安装(数据库名/用户名/密码均为 discuz/123)。

12.3 前端 Nginx 配置

在 Nginx 反向代理服务器(3.3.3.5)上:

yum install -y epel-*
yum install -y nginx

编辑 /etc/nginx/nginx.conf:

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
    include /etc/nginx/conf.d/*.conf;

    upstream squid {
        server 3.3.3.4;
    }
    upstream web {
        server 3.3.3.3;
    }

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  3.3.3.5;
        root         /usr/share/nginx/html;

        include /etc/nginx/default.d/*.conf;

        # PHP 动态请求 -> 后端 LNMP
        location ~ .*\.php$ {
            proxy_pass   http://web;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }

        # 静态资源 -> Squid 缓存
        location ~ .*\.(html|htm|gif|jpeg|jpg|css|js|png|swf)$ {
            proxy_pass   http://squid;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }

        # 其他请求 -> Squid
        location / {
            proxy_pass   http://squid;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }

        error_page 404 /404.html;
        location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }
}
systemctl restart nginx
lsof -i:80

12.4 Squid 配置

在 Squid 服务器(3.3.3.4)上:

yum install squid openssl -y

编辑 /etc/squid/squid.conf(可清空后写入):

http_access allow all

acl purge_admin src 127.0.0.1
acl purge method PURGE
http_access allow purge_admin purge
http_access deny all purge

http_port 80 accel vhost vport

cache_dir ufs /var/spool/squid 256 16 256

cache_peer 3.3.3.3 parent 80 0 no-query originserver name=web
cache_peer_domain web test.squid.com
cache_peer_domain web 3.3.3.5

coredump_dir /var/spool/squid

refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
refresh_pattern .               0       20%     4320

关键指令说明:

指令 说明
http_access allow all 允许所有访问(生产环境需按需限制)
http_port 80 accel vhost vport 反向代理模式,监听 80 端口
cache_dir ufs ... 缓存目录格式、路径、大小(MB)、一级/二级目录数
cache_peer 指定后端父服务器,originserver 表示源站
cache_peer_domain 绑定域名和前端 Nginx IP
systemctl restart squid
lsof -i:80

查询已缓存内容:

/usr/bin/squidclient -p 80 mgr:objects | grep png

12.5 验证与缓存命中

客户端绑定 hosts(或搭建 DNS):

3.3.3.5  test.squid.com

访问 http://test.squid.com/ 或 http://3.3.3.5/ 可看到 Discuz 论坛。

验证缓存命中:

curl -I http://test.squid.com/static/image/common/logo.png
HTTP/1.1 200 OK
Server: nginx/1.12.2
...
Age: 29
X-Cache: HIT from server.com
X-Cache-Lookup: HIT from server.com:80
Via: 1.1 server.com (squid/3.5.20)

关闭 Squid 后访问,网站将没有图片等静态元素。重启 Squid 后恢复正常。

12.6 Squid 缓存清除

单条清除(必须在 Squid 本机执行):

squidclient -m PURGE -h 127.0.0.1 -p 80 \
    http://3.3.3.4/static/image/common/logo.png

批量清除脚本:

vim /tmp/purge_squid.sh
#!/bin/bash
squidcache_path="/var/spool/squid/"
squidclient_path="/usr/bin/squidclient"

grep -a -r "$1" "$squidcache_path"/* | strings | grep ^"http" | while read url; do
    $squidclient_path -h 127.0.0.1 -m PURGE -p 80 "$url" > /dev/null 2>&1
    echo "$url 被清除"
done

使用方法:

sh /tmp/purge_squid.sh .txt    # 清除所有 .txt 缓存
sh /tmp/purge_squid.sh .       # 清除所有缓存
sh /tmp/purge_squid.sh /aaa/   # 清除 URL 中含 /aaa/ 的缓存

12.7 多台 Squid 负载均衡

增加 Squid2(192.168.224.13),配置与 Squid1 相同。修改前端 Nginx:

upstream squid {
    hash $request_uri;
    server 192.168.224.12 weight=1 max_fails=2 fail_timeout=3s;
    server 192.168.224.13 weight=1 max_fails=2 fail_timeout=3s;
}

使用 hash $request_uri 使同一 URL 的请求始终调度到同一台 Squid,提高缓存命中率。


13. rewrite 地址重写

13.1 rewrite 指令语法

rewrite <正则表达式> <替换内容> [flag];

可以出现在 server、location、if 块中。Nginx 按配置顺序依次匹配 rewrite 规则,除非遇到 last / break / redirect / permanent 终止。

flag 说明:

flag 类型 说明
last 内部跳转 停止当前 location 中的 rewrite 处理,用新 URI 重新发起一轮 location 匹配
break 内部跳转 停止当前的 rewrite 处理,不再重新匹配 location,直接用新 URI 在当前 location 继续执行
redirect 外部跳转 返回 302 临时重定向,浏览器地址栏改变
permanent 外部跳转 返回 301 永久重定向,浏览器地址栏改变,对 SEO 友好

记忆要点:last 和 break 客户端看不到跳转(URL 不变);redirect 和 permanent 客户端能看到跳转。在 location 中滥用 last 容易造成循环,Nginx 有 10 次内部跳转上限,超过报 500。

13.2 if 条件判断

if ($request_method = POST) {
    return 405;
}

# 变量匹配正则(~ 区分大小写,~* 不区分,! 取反)
if ($http_user_agent ~* "MSIE|Trident") {
    rewrite ^(.*)$ /ie/$1 break;
}

# 文件/目录/可执行文件是否存在
if (!-e $request_filename) {
    rewrite ^/(.*)$ /index.php?s=$1 last;
}

# 防盗链
if ($http_referer !~* "^https?://([^/]+\.)?example\.com") {
    return 403;
}
判断符 说明
= / != 字符串相等 / 不相等
~ / ~* 正则匹配(区分 / 不区分大小写)
!~ / !~* 正则不匹配
-f / !-f 文件存在 / 不存在
-d / !-d 目录存在 / 不存在
-e / !-e 文件或目录存在 / 不存在
-x / !-x 文件可执行 / 不可执行

if 只能写在 server 或 location 中,且不支持 else。官方建议尽量用 try_files、多个 location 或 map 替代复杂的 if。

13.3 return 指令

return 比 rewrite 更高效,能直接返回状态码或跳转:

return 403;                                    # 直接返回状态码
return 404 "not found\n";                      # 返回状态码和文本
return 301 https://$server_name$request_uri;   # 301 跳转
return 200 '{"status":"ok"}';                  # 返回 JSON(配合 default_type application/json)

13.4 常见案例

1)不带 www 的域名跳转到带 www 的域名

server {
    listen 80;
    server_name example.com;
    return 301 http://www.example.com$request_uri;
}

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

等价的 rewrite 写法:

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

    if ($host != 'www.example.com') {
        rewrite ^/(.*)$ http://www.example.com/$1 permanent;
    }
}

2)旧域名整体跳转到新域名

server {
    listen 80;
    server_name old.example.com;
    rewrite ^/(.*)$ http://new.example.com/$1 permanent;
}

3)目录调整跳转

# /2024/xxx.html  ->  /archive/2024/xxx.html
rewrite ^/2024/(.*)$ /archive/2024/$1 last;

4)伪静态:把静态 URL 映射到动态脚本

# /article-123.html  ->  /article.php?id=123
location / {
    rewrite ^/article-([0-9]+)\.html$ /article.php?id=$1 last;
}

# ThinkPHP / Laravel 常用的伪静态(PATH_INFO 模式)
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

# 传统写法
location / {
    if (!-e $request_filename) {
        rewrite ^/(.*)$ /index.php?s=$1 last;
    }
}

# Discuz 伪静态示例
rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last;
rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;
rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;

5)根据设备类型跳转到手机站

if ($http_user_agent ~* "(android|iphone|ipad|windows phone)") {
    rewrite ^(.*)$ http://m.example.com$1 redirect;
}

6)禁止某些 User-Agent

if ($http_user_agent ~* "curl|wget|python-requests") {
    return 403;
}

调试 rewrite 规则时可临时开启日志:

error_log logs/error.log notice;
rewrite_log on;

14. 与 PHP 集成(fastcgi_pass)

Nginx 本身不能解析 PHP,需要把 .php 请求通过 FastCGI 协议交给 PHP-FPM 处理。

14.1 安装并配置 PHP-FPM

yum install -y php php-fpm php-mysql php-gd php-mbstring php-xml
systemctl start php-fpm
systemctl enable php-fpm

/etc/php-fpm.d/www.conf 关键配置(Unix Socket 方式):

listen = /var/run/php-fpm/fastcgi.socket
php_flag[display_errors] = on
user = nginx
group = nginx
listen.owner = nginx
listen.group = nginx

也可以使用 TCP 方式监听:

listen = 127.0.0.1:9000
chown nginx.nginx /var/run/php-fpm/
systemctl restart php-fpm

14.2 Nginx 侧配置

Unix Socket 方式(本机部署,性能更好):

location ~ \.php$ {
    fastcgi_pass    unix:/var/run/php-fpm/fastcgi.socket;
    fastcgi_index   index.php;
    fastcgi_param   SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include         fastcgi_params;
}

TCP 方式(PHP-FPM 可在其他主机):

location ~ \.php$ {
    root           /var/www/html;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
}
指令 说明
fastcgi_pass PHP-FPM 的地址(Unix socket 或 IP:端口)
fastcgi_index 请求以 / 结尾时补全的默认脚本名
fastcgi_param SCRIPT_FILENAME 告知 PHP-FPM 要执行的脚本绝对路径,配置错误会导致 "File not found"
include fastcgi_params 引入 Nginx 自带的 FastCGI 通用参数
fastcgi_connect_timeout / fastcgi_read_timeout / fastcgi_send_timeout 与 PHP-FPM 的超时时间,脚本执行慢导致 504 时需调大
fastcgi_buffer_size / fastcgi_buffers 接收 PHP-FPM 响应的缓冲区

14.3 验证

cat > /usr/share/nginx/html/info.php <<'EOF'
<?php phpinfo(); ?>
EOF

nginx -t && nginx -s reload
curl http://127.0.0.1/info.php

浏览器访问 http://server-ip/info.php 出现 PHP 信息页即集成成功(测试完毕请删除该文件)。


15. HTTPS 配置

15.1 确认编译时包含 SSL 模块

Nginx 默认编译参数不包含 SSL 支持。在 ./configure 阶段必须显式加上:

./configure --with-http_ssl_module
make && make install

可通过下面的命令查看已编译进 Nginx 的参数,确认其中含有 --with-http_ssl_module:

/opt/nginx-1.8.0/sbin/nginx -V
# 或
/usr/local/nginx/sbin/nginx -V

15.2 openssl 生成自签证书(测试环境)

mkdir -p /usr/local/nginx/cert
cd /usr/local/nginx/cert

# 1. 生成 2048 位 RSA 私钥(不加密,避免启动时要求输入密码)
openssl genrsa -out server.key 2048

# 若需要带密码的私钥
openssl genrsa -des3 -out server-pass.key 2048
# 去掉私钥密码
openssl rsa -in server-pass.key -out server.key

# 2. 生成证书签名请求 CSR(交互式填写信息)
openssl req -new -key server.key -out server.csr

交互填写内容示例:

Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:Beijing
Locality Name (eg, city) [Default City]:Beijing
Organization Name (eg, company) [Default Company Ltd]:Example Inc
Organizational Unit Name (eg, section) []:IT
Common Name (eg, your name or your server's hostname) []:www.example.com
Email Address []:admin@example.com
A challenge password []:            (直接回车)

Common Name 必须填写实际访问的域名,否则浏览器会提示证书与域名不匹配。

# 3. 用私钥自签证书,有效期 365 天
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

# 一条命令直接生成私钥 + 自签证书(非交互)
openssl req -newkey rsa:2048 -nodes -keyout server.key \
    -x509 -days 3650 -out server.crt \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=Example/CN=www.example.com"

# 4. 查看证书内容
openssl x509 -in server.crt -noout -text
openssl x509 -in server.crt -noout -dates      # 查看有效期

# 5. 校验私钥与证书是否匹配(两条命令输出的 MD5 应一致)
openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa  -noout -modulus -in server.key | openssl md5

# 6. 收紧私钥权限
chmod 600 server.key

15.3 商业证书的准备与放置

从证书服务商处下载证书(通常为 .pem / .crt 与 .key 两个文件)。在 Nginx 安装目录下创建 cert 目录,将证书文件放入其中:

mkdir -p /opt/nginx-1.8.0/conf/cert
# 将下载好的证书上传到该目录,例如:
#   1991970_test.jiuhexian.cn.pem
#   1991970_test.jiuhexian.cn.key

.pem 是证书文件(包含公钥证书链),.key 是私钥文件,二者必须成对使用,且私钥文件权限建议设置为 600。

如果服务商提供了中间证书(CA bundle),需要把站点证书在前、中间证书在后拼接成一个文件,否则部分客户端会报证书链不完整:

cat server.crt intermediate.crt > fullchain.pem

15.4 443 SSL server 块配置

server {
    listen       443 ssl;
    server_name  localhost;

    ssl_certificate      cert.pem;
    ssl_certificate_key  cert.key;

    ssl_session_cache    shared:SSL:1m;
    ssl_session_timeout  5m;

    ssl_ciphers          HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers  on;

    location / {
        root   html;
        index  index.html index.htm;
    }
}

商业证书 + 加密套件的完整示例:

# HTTPS server
server {
    listen 443 ssl;
    server_name www.tx97106.com;      # 证书绑定的域名
    ssl on;

    root  html;
    index index.html index.htm;

    ssl_certificate     /usr/local/nginx/cert/1947226_www.tx97106.com.pem;
    ssl_certificate_key /usr/local/nginx/cert/1947226_www.tx97106.com.key;

    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    location / {
        root  html;
        index index.html index.htm;
    }
}

SSL 关键配置项说明:

配置项 作用
listen 443 监听 HTTPS 默认端口(新版应写作 listen 443 ssl;)
ssl on 在该 server 块启用 SSL(Nginx 1.15 后已废弃,建议改用 listen 443 ssl;)
ssl_certificate 证书文件路径(.pem / .crt),可用相对 conf 目录的路径或绝对路径
ssl_certificate_key 私钥文件路径(.key)
ssl_session_cache SSL 会话缓存,shared:SSL:10m 表示所有 worker 共享 10MB
ssl_session_timeout SSL 会话缓存超时时间
ssl_protocols 启用的 TLS 协议版本,生产建议只保留 TLSv1.2 TLSv1.3
ssl_ciphers 加密套件列表
ssl_prefer_server_ciphers 优先使用服务端加密套件
ssl_stapling / ssl_stapling_verify 启用 OCSP Stapling,加快证书状态校验
proxy_pass 反向代理到后端的协议、地址与端口

现代推荐的安全基线:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:HIGH:!aNULL:!MD5:!RC4:!3DES;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;

15.5 Nginx 代理 HTTPS 后端

主配置 nginx.conf 通过 include 引入反向代理配置文件,代理配置单独放在 conf/reverse-proxy.conf:

http {
    include       mime.types;
    default_type  application/octet-stream;
    include       /opt/nginx-1.8.0/conf/reverse-proxy.conf;
    # ... 其余全局代理缓冲、超时参数见第 9.3 节
}

reverse-proxy.conf 内容:

server {
    listen 443;
    server_name test.kaojiwang.cn;        # 改成你的域名
    ssl on;

    index index.html index.htm;

    # 证书与私钥路径(相对 conf 目录,也可写绝对路径)
    ssl_certificate     cert/1991970_test.jiuhexian.cn.pem;
    ssl_certificate_key cert/1991970_test.jiuhexian.cn.key;

    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass https://120.77.145.44:8080;   # 后端 HTTPS 服务地址
    }
}

校验并重载配置:

/opt/nginx-1.8.0/sbin/nginx -t        # 检查语法
/opt/nginx-1.8.0/sbin/nginx -s reload # 平滑重载

浏览器访问 https://test.kaojiwang.cn 即可验证证书与代理是否生效。

若后端使用的是自签证书,proxy_pass https://... 可能报证书校验错误,必要时可在 location 中加入 proxy_ssl_verify off;(生产环境请慎用并配置正确的 CA)。

15.6 HTTP 强制跳转 HTTPS

再添加一个监听 80 端口的 server 块,将所有 HTTP 请求 301 重定向到 HTTPS。推荐使用 return 301:

server {
    listen 80;
    server_name www.tx97106.com;      # 使用证书绑定的域名替换 localhost
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name www.tx97106.com;
    # ssl_certificate / ssl_certificate_key 等配置同上
}

也可使用 rewrite 写法,效果等价:

server {
    listen 80;
    server_name www.tx97106.com;
    rewrite ^(.*)$ https://$host$1 permanent;
}

两种写法对比:

写法 示例 说明
return 301 return 301 https://$server_name$request_uri; Nginx 官方推荐,执行效率高,语义清晰
rewrite ... permanent rewrite ^(.*)$ https://$host$1 permanent; 通过正则重写,permanent 即 301 永久重定向

其中 $server_name 取自 server_name 指令,$host 取自请求头中的 Host,$request_uri 保留完整的请求路径与参数,确保跳转后路径不丢失。

校验并生效:

/usr/local/nginx/sbin/nginx -t
/usr/local/nginx/sbin/nginx -s reload

使用浏览器或 curl 验证跳转:

curl -I http://www.tx97106.com/

输出中可见 301 Moved Permanently 与 Location: https://... 即表示配置生效。

15.7 HSTS(HTTP 严格传输安全)

在 HTTPS 的 server 块中添加响应头,告知浏览器在指定时间内只能通过 HTTPS 访问本站,可防止 SSL 剥离攻击:

server {
    listen 443 ssl;
    server_name www.example.com;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}
参数 说明
max-age=31536000 有效期(秒),31536000 即 1 年
includeSubDomains 对所有子域名同样强制 HTTPS(确认所有子域都已支持 HTTPS 再加)
preload 申请加入浏览器 HSTS 预加载列表(提交后很难撤销,谨慎使用)
always 即使是错误响应也添加该头

验证:

curl -I https://www.example.com/ | grep -i strict

注意: HSTS 头只能加在 HTTPS 响应中;一旦下发,浏览器在 max-age 内会拒绝 HTTP 访问,上线前务必确认证书链完整、续期机制正常。


16. systemd 管理 Nginx

通过源码 make install 安装的 Nginx 不会自动生成 systemd 服务单元,因此无法直接使用 systemctl 管理。下面手动创建 nginx.service,实现开机自启与统一的启停控制。

16.1 确认安装路径与 PID 文件

假设 Nginx 安装在 /usr/local/nginx,主程序为 /usr/local/nginx/sbin/nginx,配置文件为 /usr/local/nginx/conf/nginx.conf。

为了让 systemd 能正确跟踪进程,需要让 Nginx 把 PID 写入固定文件。在 nginx.conf 的全局段加入:

pid /var/run/nginx.pid;

16.2 创建 systemd 单元文件

在 /usr/lib/systemd/system/ 下新建 nginx.service:

vim /usr/lib/systemd/system/nginx.service

写入以下内容:

[Unit]
Description=nginx - high performance web server
Documentation=http://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

[Install]
WantedBy=multi-user.target

16.3 修改 [Service] 段以匹配源码安装路径

默认单元文件中的路径通常是发行版打包 Nginx 的路径(/usr/sbin/nginx、/etc/nginx/nginx.conf),需要改为源码安装的实际路径:

ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

修改后的完整 [Service] 段为:

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

也可以加上启动前的语法检查与优雅退出(可选增强):

ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
ExecStop=/bin/kill -s QUIT $MAINPID
LimitNOFILE=65535

16.4 单元文件字段说明

段 字段 说明
[Unit] Description 服务描述
[Unit] Documentation 文档地址
[Unit] After 在指定 target 之后启动(网络、远程文件系统等)
[Unit] Wants 弱依赖,希望 network-online.target 就绪
[Service] Type=forking Nginx 启动后以守护进程方式 fork 到后台,systemd 据此识别主进程
[Service] PIDFile Nginx 主进程 PID 文件路径,须与 nginx.conf 中的 pid 指令一致
[Service] ExecStart 启动命令,指定 nginx 可执行文件与 -c 配置文件路径
[Service] ExecReload 重载命令,向主进程发送 HUP 信号实现平滑重载
[Service] ExecStop 停止命令,向主进程发送 TERM 信号优雅退出
[Install] WantedBy 设置为 multi-user.target,即在多用户命令行模式下开机自启

16.5 关闭原有进程并启用服务

如果之前已用二进制方式启动过 Nginx,需要先停止,避免端口冲突:

pkill -9 nginx
ps -aux | grep nginx

重新加载 systemd 配置,使其识别新建的单元文件:

systemctl daemon-reload

之后即可使用 systemctl 管理 Nginx:

systemctl start nginx       # 启动
systemctl enable nginx      # 设置开机自启
systemctl restart nginx     # 重启
systemctl reload nginx      # 平滑重载配置
systemctl status nginx      # 查看运行状态
systemctl stop nginx        # 停止

enable 执行后,systemd 会在 /etc/systemd/system/multi-user.target.wants/ 下建立 nginx.service 的软链接,从而实现开机自启。

16.6 验证

systemctl status nginx
ps -aux | grep nginx
ss -ntlp | grep nginx

若 status 显示 active (running),且 80/443 端口正常监听,则说明 systemd 已接管源码编译安装的 Nginx。

16.7 SysV 启动脚本(旧系统 / CentOS 6)

在没有 systemd 的环境中,可以编写 /etc/init.d/nginx 脚本:

vim /etc/init.d/nginx
#!/bin/bash
# chkconfig: - 30 21
# description: http service.
. /etc/init.d/functions

NGINX_SBIN="/usr/local/nginx/sbin/nginx"
NGINX_CONF="/usr/local/nginx/conf/nginx.conf"
NGINX_PID="/usr/local/nginx/logs/nginx.pid"
RETVAL=0
prog="Nginx"

start() {
    echo -n $"Starting $prog: "
    mkdir -p /dev/shm/nginx_temp
    daemon $NGINX_SBIN -c $NGINX_CONF
    RETVAL=$?
    echo
    return $RETVAL
}

stop() {
    echo -n $"Stopping $prog: "
    killproc -p $NGINX_PID $NGINX_SBIN -TERM
    rm -rf /dev/shm/nginx_temp
    RETVAL=$?
    echo
    return $RETVAL
}

reload() {
    echo -n $"Reloading $prog: "
    killproc -p $NGINX_PID $NGINX_SBIN -HUP
    RETVAL=$?
    echo
    return $RETVAL
}

restart() {
    stop
    start
}

configtest() {
    $NGINX_SBIN -c $NGINX_CONF -t
    return 0
}

case "$1" in
    start)   start ;;
    stop)    stop ;;
    reload)  reload ;;
    restart) restart ;;
    configtest) configtest ;;
    *) echo $"Usage: $0 {start|stop|reload|restart|configtest}"
       RETVAL=1 ;;
esac
exit $RETVAL
chmod 755 /etc/init.d/nginx

管理命令:

命令 说明
/etc/init.d/nginx start 启动
/etc/init.d/nginx stop 停止
/etc/init.d/nginx restart 重启
/etc/init.d/nginx reload 平滑重载
/etc/init.d/nginx configtest 测试配置
chkconfig --add nginx 加入系统服务
chkconfig nginx on 开机自启

17. 性能调优

17.1 Nginx 配置层面

优化项 配置建议
worker 进程数 worker_processes auto,与 CPU 核心数一致
CPU 亲和性 worker_cpu_affinity auto(或按核显式绑定 0001 0010 0100 1000)
单进程连接数 worker_connections 65535(根据内存调整)
文件描述符 worker_rlimit_nofile 65535,配合 ulimit -SHn 65535
事件模型 Linux 使用 use epoll,可搭配 multi_accept on
文件传输 sendfile on、tcp_nopush on、tcp_nodelay on
长连接 keepalive_timeout 65;upstream 中加 keepalive 32 复用到后端的连接
Gzip 压缩 gzip on,压缩级别 4-6
静态缓存 expires 设置浏览器缓存
代理缓存 proxy_cache 减轻后端压力
隐藏版本 server_tokens off
静态文件缓存元数据 open_file_cache max=65535 inactive=60s;

upstream 长连接示例(显著降低反向代理的 TIME_WAIT):

upstream backend {
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    keepalive 32;
}

location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

17.2 文件描述符与 limits

# 临时生效(当前 shell)
ulimit -n 65535
ulimit -SHn 65535

# 查看当前限制
ulimit -a

永久生效,编辑 /etc/security/limits.conf:

*    soft    nofile    65535
*    hard    nofile    65535
*    soft    nproc     65535
*    hard    nproc     65535

systemd 管理的服务不读取 limits.conf,需要在 unit 文件中设置:

[Service]
LimitNOFILE=65535
LimitNPROC=65535
systemctl daemon-reload
systemctl restart nginx

# 验证实际生效值
cat /proc/$(cat /var/run/nginx.pid)/limits | grep -i "open files"

17.3 内核参数优化

编辑 /etc/sysctl.conf(或 /etc/sysctl.d/99-nginx.conf):

# 系统级最大文件句柄数
fs.file-max = 655350

# 半连接队列长度(应对 SYN 洪泛与突发连接)
net.ipv4.tcp_max_syn_backlog = 65536
# 已完成连接队列长度,需与 listen backlog 配合
net.core.somaxconn = 65535
# 网卡接收数据包的队列
net.core.netdev_max_backlog = 32768

# socket 读写缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

# 允许 TIME_WAIT 套接字复用于新连接
net.ipv4.tcp_tw_reuse = 1
# 缩短 FIN_WAIT_2 与 TIME_WAIT 停留时间
net.ipv4.tcp_fin_timeout = 30
# TIME_WAIT 套接字最大数量,超出立即清理
net.ipv4.tcp_max_tw_buckets = 20000

# keepalive 探测
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3

# 可用的本地端口范围(反向代理需要大量对外连接)
net.ipv4.ip_local_port_range = 1024 65535

# SYN Cookie 防护
net.ipv4.tcp_syncookies = 1

生效并验证:

sysctl -p
sysctl net.core.somaxconn

net.ipv4.tcp_tw_recycle 在 NAT 环境下会导致连接异常,且已在较新内核中移除,不要开启。

17.4 压测与观察

# ab 压测(httpd-tools 包)
ab -n 10000 -c 500 http://127.0.0.1/index.html

# 观察连接状态分布
ss -s
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

# 观察 Nginx 状态
curl http://127.0.0.1/nginx_status

调优过程中重点关注:stub_status 中 accepts 与 handled 是否相等(不等说明连接被丢弃)、错误日志中是否出现 worker_connections are not enough 或 too many open files。


18. 故障排查

18.1 通用排查步骤

nginx -t                              # 1. 配置语法
systemctl status nginx                # 2. 服务状态
ss -ntlp | grep nginx                 # 3. 端口监听
tail -f /var/log/nginx/error.log      # 4. 错误日志(最关键)
tail -f /var/log/nginx/access.log     # 5. 访问日志与状态码
curl -I http://127.0.0.1/             # 6. 本机自测,排除网络与防火墙
getenforce; systemctl status firewalld # 7. SELinux 与防火墙

18.2 403 Forbidden 排查清单

可能原因 检查与解决
目录缺少默认首页且未开 autoindex 确认 index 指定的文件存在,或临时 autoindex on
文件/目录权限不足 目录需 755、文件需 644;namei -l /var/www/html/index.html 逐级检查
属主与 Nginx 运行用户不匹配 ps -ef \| grep nginx 看 worker 用户,chown -R nginx:nginx /var/www/html
nginx.conf 中 user 与实际不符 确认 main 段 user nginx;
deny 规则命中 检查 allow / deny 顺序与 IP 段,注意局部覆盖全局
SELinux 阻止 getenforce;setenforce 0 临时验证;正式方案 chcon -R -t httpd_sys_content_t /var/www/html 或 restorecon -Rv /var/www/html
网站根目录路径写错 nginx -T \| grep root 确认实际生效的 root

18.3 502 Bad Gateway 排查清单

502 表示 Nginx 连上了(或没连上)后端但没拿到合法响应,问题基本在后端或连接方式上。

可能原因 检查与解决
后端服务未启动 systemctl status php-fpm / tomcat / 应用进程;ss -ntlp \| grep 9000
fastcgi_pass / proxy_pass 地址或端口写错 与后端实际监听地址核对(socket 路径、IP、端口)
Unix socket 权限问题 socket 文件属主须与 Nginx worker 用户一致:listen.owner = nginx、listen.group = nginx,并 chown nginx.nginx /var/run/php-fpm/
PHP-FPM 进程数耗尽 调大 pm.max_children、pm.max_requests;查看 php-fpm 日志中的 server reached max_children
后端应用崩溃 / OOM 查看后端日志与 dmesg \| grep -i oom
后端返回响应头过大 调大 proxy_buffer_size、fastcgi_buffer_size 与 fastcgi_buffers
防火墙 / SELinux 阻断到后端的连接 setsebool -P httpd_can_network_connect 1
upstream 全部被标记不可用 检查 max_fails / fail_timeout 是否过于激进,查看 $upstream_addr、$upstream_status

18.4 504 Gateway Time-out 排查清单

504 表示后端在超时时间内没有返回响应,通常是后端处理太慢。

可能原因 检查与解决
后端脚本执行时间过长 优化 SQL / 代码;把耗时任务改为异步队列
代理超时设置过小 调大 proxy_connect_timeout、proxy_send_timeout、proxy_read_timeout(如 300s)
FastCGI 超时过小 调大 fastcgi_connect_timeout、fastcgi_send_timeout、fastcgi_read_timeout
PHP 自身超时 php.ini 中 max_execution_time;php-fpm 的 request_terminate_timeout
后端数据库慢 / 锁等待 检查慢查询日志、show processlist
后端 worker 全忙排队 增加后端实例或调整进程池大小

定位耗时的最快方式是在日志格式中加入 rt=$request_time urt=$upstream_response_time,对比 Nginx 总耗时与后端耗时。

18.5 其他常见错误

现象 原因与处理
bind() to 0.0.0.0:80 failed (98: Address already in use) 端口被占用:ss -ntlp \| grep :80,停掉旧进程(pkill -9 nginx)或改端口
too many open files 文件描述符不足:调大 worker_rlimit_nofile、ulimit -n、LimitNOFILE
worker_connections are not enough 调大 worker_connections,并确认不超过 worker_rlimit_nofile
413 Request Entity Too Large 调大 client_max_body_size
404 但文件确实存在 root / alias 路径拼接错误;用 nginx -T 查看生效配置,或在 error_log 中看拼出的完整路径
500 Internal Server Error 多为动态脚本报错,查看后端应用日志;rewrite 循环超过 10 次也会 500
PHP 页面被下载而不是执行 缺少 location ~ \.php$ 的 fastcgi 配置
File not found(PHP) SCRIPT_FILENAME 指向的路径不存在,检查 root 与 $document_root$fastcgi_script_name
HTTPS 提示证书不受信任 自签证书属正常现象;商业证书需检查证书链是否拼接完整、Common Name 是否匹配域名
修改配置无效 忘记 nginx -s reload;或修改的是未被 include 的文件;或被浏览器缓存

19. 完整生产配置示例

user  nginx nginx;
worker_processes  auto;
worker_cpu_affinity auto;
error_log  logs/error.log warn;
pid        logs/nginx.pid;
worker_rlimit_nofile 65535;

events {
    use epoll;
    worker_connections  65535;
}

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

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" '
                      '"$upstream_cache_status"';

    access_log  logs/access.log  main;

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    keepalive_timeout  65;
    types_hash_max_size 2048;
    server_tokens off;

    # Gzip 压缩
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/javascript application/json
               application/javascript application/xml;
    gzip_vary on;

    # 客户端请求设置
    client_max_body_size 20m;
    client_body_buffer_size 128k;

    # 代理设置
    proxy_connect_timeout 75;
    proxy_send_timeout 75;
    proxy_read_timeout 75;
    proxy_buffer_size 4k;
    proxy_buffers 4 32k;
    proxy_busy_buffers_size 64k;

    # 负载均衡
    upstream backend {
        server 10.0.0.11:8080 weight=1 max_fails=2 fail_timeout=30s;
        server 10.0.0.12:8080 weight=1 max_fails=2 fail_timeout=30s;
    }

    # HTTP 虚拟主机
    server {
        listen 80;
        server_name www.example.com;
        root /var/www/html;
        index index.html index.htm index.php;

        charset utf-8;
        access_log logs/www.access.log main;

        # 静态资源由 Nginx 直接处理
        location ~* \.(gif|jpg|jpeg|png|ico|css|js|svg|woff2?)$ {
            expires 30d;
            add_header Cache-Control "public, no-transform";
        }

        # 动态请求转发到后端
        location / {
            proxy_pass http://backend;
            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_next_upstream error timeout http_502 http_503;
        }

        # PHP 解析
        location ~ \.php$ {
            fastcgi_pass unix:/var/run/php-fpm/fastcgi.socket;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }

        # 状态监控
        location /nginx_status {
            stub_status on;
            access_log off;
            allow 127.0.0.1;
            allow 10.0.0.0/8;
            deny all;
        }

        # 禁止访问隐藏文件
        location ~ /\. {
            deny all;
        }

        # 禁止访问敏感目录
        location ~ ^/(WEB-INF)/ {
            deny all;
        }

        error_page 404 /404.html;
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
            root html;
        }
    }

    # HTTPS 虚拟主机
    server {
        listen 443 ssl;
        server_name www.example.com;

        ssl_certificate     /etc/nginx/ssl/www.example.com.pem;
        ssl_certificate_key /etc/nginx/ssl/www.example.com.key;
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_protocols       TLSv1.2 TLSv1.3;
        ssl_ciphers         HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;

        root /var/www/html;
        index index.html index.htm;

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

20. 练习

20.1 安装与基础配置

  1. 在 CentOS 7 上分别用 YUM 和源码两种方式安装 Nginx,写出源码方式所需的依赖包清单,并说明每个依赖对应哪个 Nginx 模块。
  2. 源码编译时要求同时启用 SSL、gzip 静态压缩和状态监控三个模块,写出完整的 ./configure 命令。
  3. 用 nginx -V 验证已编译的模块;用 nginx -t 校验配置;用 nginx -s reload 平滑生效。说明 nginx -s stop 与 nginx -s quit 的区别。
  4. 画出 Nginx 的 master / worker 进程模型,并说明 HUP、USR1、USR2 三个信号各自的用途。

20.2 虚拟主机与静态资源

  1. 在一台机器上同时配置三个虚拟主机:基于端口(80 / 8080)、基于域名(www.test.com / bbs.test.com)、基于 IP(主 IP 与 ens33:0 虚拟 IP),并用 curl 分别验证。
  2. 为 /download/ 目录开启目录浏览,要求显示人类可读的文件大小和服务器本地时间。
  3. 为图片、CSS、JS 设置 30 天浏览器缓存,用 curl -I 验证响应头中出现 Expires 与 Cache-Control。
  4. 自定义 404 与 50x 错误页面并验证。

20.3 访问控制与日志

  1. 只允许 192.168.10.0/24 访问 /nginx_status,其余全部拒绝,并叠加 basic auth 认证(用 htpasswd 创建 admin 与 test 两个用户)。
  2. 定义一个包含 $request_time、$upstream_addr、$upstream_response_time、$upstream_cache_status 的日志格式,并只对某个虚拟主机生效。
  3. 编写日志切割脚本,按 年/月/日 目录归档昨天的 access.log,并通过 crontab 每天 2:01 执行;再用 logrotate 实现同样的效果,说明两者都必须发送 USR1 信号的原因。

20.4 反向代理与负载均衡

  1. 搭建 Nginx(双网卡)+ 两台后端 Web 的反向代理环境,配置 proxy_set_header 使后端日志能记录到客户端真实 IP。
  2. 实现动静分离:.html/.jpg/.css/.js 请求转发到 web1,.php/.cgi 请求转发到 web2,并验证。
  3. 配置 upstream,要求:web1 权重 3、web2 权重 1、web3 作为 backup、web4 标记 down,失败 2 次后 30 秒内不再调度。停掉 web1 与 web2,验证 backup 是否接管。
  4. 分别用 ip_hash 和 least_conn 测试调度效果,说明 ip_hash 在同网段客户端测试时可能出现的现象及原因。
  5. 说明 proxy_pass http://backend; 与 proxy_pass http://backend/; 在 location /nba/ 下的转发路径差异。

20.5 缓存

  1. 配置 proxy_cache:缓存目录 levels=1:2、共享内存 200MB、1 天未访问淘汰、最大 5GB,对 200/304 缓存 10 分钟,并通过 X-Cache 响应头验证 MISS → HIT 的变化。
  2. 编写脚本清除所有 .png 结尾的缓存文件;再用 ngx_cache_purge 模块实现通过 URL 清除指定缓存。
  3. 使用 hash $request_uri 对两台缓存服务器做负载均衡,说明它为什么能提高缓存命中率。

20.6 rewrite

  1. 实现 example.com 到 www.example.com 的 301 跳转,分别写出 return 和 rewrite 两种写法。
  2. 实现伪静态:/article-123.html → /article.php?id=123,并说明 last 与 break 的区别。
  3. 配置防盗链:非本站 Referer 请求图片时返回 403。
  4. 检测移动端 User-Agent 并跳转到 m.example.com。

20.7 HTTPS

  1. 用 openssl 生成 2048 位私钥与有效期 365 天的自签证书(Common Name 为 www.test.com),配置 443 端口的 SSL server 并验证。
  2. 校验私钥与证书是否匹配(对比两条 openssl ... modulus | openssl md5 的输出)。
  3. 配置 HTTP 强制跳转 HTTPS,用 curl -I 验证返回 301 与 Location 头。
  4. 只保留 TLSv1.2 TLSv1.3,配置推荐加密套件,并添加 HSTS 响应头(1 年、包含子域),说明 preload 的风险。

20.8 systemd 与调优排错

  1. 为源码安装的 Nginx 编写 nginx.service,要求 PIDFile 与 nginx.conf 中的 pid 一致,实现 start/stop/reload/enable,并验证开机自启软链接的位置。
  2. 把 Nginx 的文件描述符上限提到 65535:分别通过 ulimit、limits.conf、worker_rlimit_nofile、systemd 的 LimitNOFILE 四处配置,并用 /proc/<pid>/limits 验证实际生效值。
  3. 写出至少 6 个与高并发相关的内核参数及其作用,并说明为什么不应开启 tcp_tw_recycle。
  4. 故障演练:分别人为制造 403(权限/SELinux)、502(停掉 php-fpm)、504(后端 sleep 超时)三种故障,记录 error.log 中的报错原文和你的排查步骤。
  5. 用 ab -n 10000 -c 500 压测,对比开启 / 关闭 gzip、开启 / 关闭 upstream keepalive 时的 QPS 差异。

本文档涵盖 Nginx 从安装、配置到生产实战的完整知识体系。修改配置后务必执行 nginx -t 验证语法,再通过 nginx -s reload 平滑生效。

最后修改:2026 年 09 月 20 日
如果觉得我的文章对你有用,请随意赞赏