Contents

Nginx 源码编译安装与生产级配置全指南(Nginx 系列第 1 期)

这是 Nginx 系列的第 1 期:第 2 期:JSON 日志格式第 3 期:FancyIndex 文件服务器

如果你是通过搜索引擎搜索 “Nginx 源码编译” 进来的,本文就是你要找的那个。我们不讲 apt install nginx 那种一行命令搞定的事——那不叫技术文章。本文从运维架构师的视角,带你走完一套完整的生产级 Nginx 源码部署流程。


1. 为什么不用 apt installyum install

先回答一个核心问题:既然包管理一行命令就能装好 Nginx,为什么要折腾源码编译?这不是我要自虐——是生产环境的实际需求倒逼的。

1.1 包管理安装 vs 源码编译:一张表格说清楚

维度 apt install nginx / dnf install nginx 源码编译
安装速度 30 秒 3-5 分钟(首次编译)
Nginx 版本 固定(Debian 13 Trixie 提供 v1.24.0,2024 年发布) 自选(本文编译 v1.30.3 Stable)
第三方模块支持 有限(需另装 nginx-extraslibnginx-mod-* 系列包) 无限制(configure 时指定 --add-module 即可)
OpenSSL 版本依赖 绑定系统 OpenSSL(Debian 13: v3.2.2) 可静态编译定制版本的 OpenSSL/QuicTLS/BoringSSL
二进制位置 FHS 标准路径(/usr/sbin/nginx/etc/nginx//var/log/nginx/ 自由定义(configure 参数全部可配)
版本号隐藏 Server: nginx/1.24.0 暴露真实版本 server_tokens off + 自定义 Server 头
编译优化 通用 x86_64 指令集 -march=native -O3 -flto 针对当前 CPU 优化
更新与维护 apt upgrade 自动更新(可能引入不受控的变更) 手动重新编译、替换二进制
SELinux 策略兼容 ✅ 发行版包自带 SELinux 策略 ❌ 需自行编写(除非关闭 SELinux)
故障排查社区支持 高(与 Ubuntu/Debian/RHEL 官方一致) 需理解自己的 configure 参数
熟悉度要求 中等(需理解 GCC/Make 工作原理)

1.2 三个真实的生产痛点

痛点一:模块版本锁定。

你需要 ngx_http_geoip2_module,但 apt 提供的 nginx-extras 包绑定的是系统 GeoIP 库 1.x,你的业务依赖 GeoIP 2.x 的 mmdb 格式。源码编译直接 --add-module 解决。

痛点二:OpenSSL 版本不匹配。

系统自带的 OpenSSL 是 v3.2.2——你想用 OpenSSL 3.5(支持 TLS 1.3 完整特性集和 PSK-only 模式),或者想编译带 QUIC/HTTP3 支持的 Nginx(需要特殊的 SSL 库 patch 版本),包管理版本完全绑定。

痛点三:统一公司内部的二进制基线。

你管理 50 台 Web 服务器,一台一台装 apt install 会导致 Nginx 版本、模块列表、configure 参数完全不一致——排查问题时这就是一场噩梦。源码编译 + 统一 deploy script 确保每一台机器上的 Nginx 二进制完全一致。

那什么时候用 apt 装就够了?

如果你的场景是:

  • 个人博客(一台 VPS,流量不大)
  • 内网开发/测试环境
  • 不需要第三方模块
  • 不关心 OpenSSL 版本

apt install nginx 完全够用。本文的价值在于你被上面三个痛点戳中时,知道怎么从 apt 方案迁移到源码方案。


2. 实验环境

服务商 数据中心 产品 规格 OS
搬瓦工 洛杉矶 DC2 KVM VPS 2C / 1G RAM / 20G SSD Debian 13 (Trixie)
适用范围
本文命令适用于 Debian 12/13、Ubuntu 22.04/24.04、以及大部分基于 Debian 的 Linux 发行版。RHEL/CentOS 系用户将 apt 替换为 dnflibpcre3-dev 替换为 pcre-develzlib1g-dev 替换为 zlib-devel 即可。核心思路和 configure 参数完全一样。

3. 源码编译安装 Nginx 完整流程

3.1 安装编译依赖

apt update -y
apt install -y build-essential libpcre3-dev zlib1g-dev libssl-dev \
    libxslt1-dev libgd-dev libgeoip-dev libgoogle-perftools-dev

依赖包用途一览:

包名 用途 对应的编译模块
build-essential GCC、GNU Make 等基础编译工具链 —(编译本身所需)
libpcre3-dev Perl 兼容正则表达式库,用于 locationrewrite 指令的 URL 路由匹配 核心依赖,如缺失 configure 报错
zlib1g-dev gzip 压缩/解压库,用于 HTTP gzip 内容编码 --with-http_gzip_static_module
libssl-dev OpenSSL 开发头文件,提供 TLS/SSL 加密 --with-http_ssl_module
libxslt1-dev XSLT 转换库,Nginx 可对 XML 响应进行 XSL 转换 --with-http_xslt_module
libgd-dev GD 图形处理库,用于动态图片缩略/裁剪 --with-http_image_filter_module
libgeoip-dev MaxMind GeoIP 地理位置库(旧版格式 .dat) --with-http_geoip_module
libgoogle-perftools-dev Google tcmalloc 内存分配器,替换 glibc malloc 以降低 Nginx 内存碎片 --with-google_perftools_module

3.2 构建 FHS 标准目录结构

# 1. 核心配置目录
mkdir -p /etc/nginx/{conf.d,sites-available,sites-enabled,ssl}

# 2. 日志目录
mkdir -p /var/log/nginx

# 3. 运行时临时文件(body 缓冲、proxy 缓冲等)
mkdir -p /var/lib/nginx/{client_body,proxy,fastcgi,uwsgi,scgi}

# 4. 权限收紧
chown -R www-data:root /var/log/nginx /var/lib/nginx
chmod -R 700 /var/lib/nginx

3.3 目录布局为什么要这样设计?——引用 FHS 3.0 权威解释

先给权威出处:以上布局严格遵循 Filesystem Hierarchy Standard 3.0(Linux Foundation 维护,refspecs.linuxfoundation.org/FHS_3.0)。FHS 是 Linux Standard Base(LSB)的核心组成部分,定义了类 Unix 操作系统中文件和目录的组织方式。

我将 Nginx 的每个目录与你可能熟悉的 Linux 目录结构做一个映射,并解释背后的设计原则。这套布局的逻辑起源于《UNIX and Linux System Administration Handbook》(Evi Nemeth 等人著,第 5 版,Prentice Hall)中反复强调的一条原则:

“Separate what changes from what doesn’t. Separate what’s shared from what isn’t.”

将可变与不可变分离,将共享与独占分离。

Nginx 目录 用途 FHS 对应章节 设计原则
/etc/nginx/ 主机级配置文件 FHS §3.7 /etc:Host-specific system configuration 不可变文件存放位置。配置文件在系统运行时极少改动(不像日志每秒都在写)。包管理器和配置管理工具(Ansible/Salt)只操作 /etc
/etc/nginx/conf.d/ 模块化配置文件(片段) 遵循 /etc 的 “add-on application data” 子目录设计 不用把所有配置塞进一个 nginx.confconf.d/*.conf 是 Nginx 社区的约定——类比 Apache 的 conf.d/、systemd 的 /etc/systemd/system/*.d/ 覆写目录。
/etc/nginx/sites-available/ 所有站点配置文件(含禁用的) /etc 下 “available” 模式由 Debian Apache 开创 sites-available + sites-enabled 是 Debian/Ubuntu 的 Nginx 包采用的布局。好处:通过 include sites-enabled/*.conf 只加载通过符号链接启用的站点。一个 rm /etc/nginx/sites-enabled/old-site.conf 就能快速禁用,不影响原文件。
/etc/nginx/sites-enabled/ 已启用的站点配置(符号链接) —(非 FHS 要求,是 Debian 惯例)
/etc/nginx/ssl/ SSL/TLS 私钥和证书 /etc 下与特定应用关联的私有数据目录 SSL 私钥权限 600,证书 644。放在 /etc/nginx/ssl/ 而非 /etc/ssl/ 是为了保持 Nginx 的所有配置数据在一个目录树下——备份 /etc/nginx/ 就备份了整个 Nginx 配置。
/var/log/nginx/ 日志文件 FHS §5.10 /var/log:Miscellaneous log files 高写入频率的可变数据。FHS 明确规定日志文件应放在 /var/log 下。必须与 /etc(读取为主)和 /usr(只读)物理分离——在服务器上它们通常在不同的磁盘分区/挂载点。
/var/lib/nginx/ 运行时可变状态(body 缓冲、proxy 缓冲、FastCGI 临时文件) FHS §5.8 /var/lib:Variable state information 程序运行时产生的状态数据。与 /var/log 的区别:/var/log 是管理层关心的(审计、排查),/var/lib 是程序自己关心的(内部状态)。用 tmpfs 挂载这里可以减少磁盘 I/O。
/usr/share/nginx/ 编译输出文件(二进制、HTML 页面、mime.types 等) FHS §4.11 /usr/share:Architecture-independent data 架构无关的静态数据/usr/share 下的文件在 x86_64 和 ARM64 上是相同的——pure data,没有二进制指令。mime.types 放在这里是正确的。
/usr/sbin/nginx Nginx 二进制可执行文件 FHS §4.9 /usr/sbin:Non-essential standard system binaries 系统管理员可执行文件而非普通用户。不需要 Nginx 的用户不应该在 /usr/bin 里看到它。注意 FHS 区分 /usr/bin(普通用户命令)和 /usr/sbin(系统管理命令)。
/run/nginx.pid PID 文件 FHS §5.11 /run:Run-time variable data 系统重启后清空的临时运行时文件/run 通常是 tmpfs(内存文件系统),适合存放 PID 文件和锁文件——重启后自动清理,不会有僵尸 PID。
Nemeth 书中的相关论述

Evi Nemeth 等人在《UNIX and Linux System Administration Handbook》(第 5 版)第 3 章中详细讨论了 /etc/var/usr 的分离策略。书中写道:

“The separation of /usr (read-only, shareable) from /var (read-write, per-machine) is one of the most important structural decisions in the UNIX filesystem. When you mount /usr from a network server, every machine runs the same binaries; only /var and /etc differ.”

这个思路直接映射到 Nginx 布局:可执行文件和静态资源(/usr)可以在多台服务器间共享;配置(/etc)和日志(/var)必须各自独立。

3.4 下载源码并 Configure(逐行参数解析)

cd /usr/src

# 下载 Stable 版本(1.30.3,截至 2026 年 7 月最新稳定版)
wget https://nginx.org/download/nginx-1.30.3.tar.gz
tar xzf nginx-1.30.3.tar.gz
cd nginx-1.30.3
版本选择:Mainline vs Stable

Nginx 官方提供两条发布线:

  • Mainline:奇数版本号(1.27.x),新功能先上,每月更新
  • Stable:偶数版本号(1.28.x),只接收 bug 修复和安全补丁,适合生产环境

选择规则:除非你需要 Mainline 独有的新功能(如 http3 的某个新指令),否则一律选 Stable。

Nginx 版本的命名规则来自 Linux 内核的偶/奇版本号规则(2.4 稳定 / 2.5 开发),这本身就是 Linux 社区的一个惯例——引用自 Greg Kroah-Hartman 在 Linux Kernel 邮件列表 2004 年的版本策略文档。

./configure \
    --prefix=/usr/share/nginx \
    --sbin-path=/usr/sbin/nginx \
    --modules-path=/usr/lib/nginx/modules \
    --conf-path=/etc/nginx/nginx.conf \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --pid-path=/run/nginx.pid \
    --lock-path=/run/nginx.lock \
    --http-client-body-temp-path=/var/lib/nginx/client_body \
    --http-proxy-temp-path=/var/lib/nginx/proxy \
    --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
    --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
    --http-scgi-temp-path=/var/lib/nginx/scgi \
    --user=www-data \
    --group=www-data \
    --with-poll_module \
    --with-threads \
    --with-file-aio \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_realip_module \
    --with-http_xslt_module \
    --with-http_image_filter_module \
    --with-http_geoip_module \
    --with-http_sub_module \
    --with-http_dav_module \
    --with-http_flv_module \
    --with-http_mp4_module \
    --with-http_gunzip_module \
    --with-http_gzip_static_module \
    --with-http_auth_request_module \
    --with-http_random_index_module \
    --with-http_secure_link_module \
    --with-http_degradation_module \
    --with-http_slice_module \
    --with-http_stub_status_module \
    --with-mail \
    --with-stream \
    --with-stream_ssl_module \
    --with-stream_ssl_preread_module \
    --with-stream_realip_module \
    --with-stream_geoip_module \
    --with-google_perftools_module \
    --with-pcre

逐参数解释(精度比官方的 ./configure --help 更重要——这里的说明来自生产环境的实际使用经验):

路径类参数

参数 作用 不设置时的默认值(对比)
--prefix=/usr/share/nginx 安装根目录。所有 --*-path 参数的基准 默认 /usr/local/nginx(不符合 FHS,把可执行文件和数据混在了一棵树里)
--sbin-path=/usr/sbin/nginx 二进制可执行文件位置 $prefix/sbin/nginx
--modules-path=/usr/lib/nginx/modules 动态模块加载路径 $prefix/modules
--conf-path=/etc/nginx/nginx.conf 主配置文件 $prefix/conf/nginx.conf(配置和数据混放,违反 FHS)
--pid-path=/run/nginx.pid PID 文件(配合 systemd Type=forking 使用) $prefix/logs/nginx.pid(重启后被遗留,master 进程检查失败)
--lock-path=/run/nginx.lock 互斥锁文件(accept mutex) $prefix/logs/nginx.lock

临时文件目录

参数 对应指令 用途
--http-client-body-temp-path client_body_temp_path 存储客户端上传的文件体超过 client_body_buffer_size 的溢出部分
--http-proxy-temp-path proxy_temp_path 反向代理时,上游响应超过 proxy_buffer_size 的缓冲文件
--http-fastcgi-temp-path fastcgi_temp_path PHP-FPM 等 FastCGI 后端响应缓冲
--http-uwsgi-temp-path uwsgi_temp_path Python uWSGI 后端响应缓冲
--http-scgi-temp-path scgi_temp_path SCGI 后端响应缓冲

必须全部显式指定。默认位置在 $prefix 下,不配置意味着这些临时文件被写入 /usr/share/nginx/ 下一个与共享只读数据混在一起的位置——这是生产环境的定时炸弹。

HTTP 七层模块

参数 启动的功能 核心使用场景
--with-http_ssl_module HTTPS/TLS 支持 生产环境必选。没有 SSL 模块的 Nginx 只能跑 HTTP——2026 年这不是一个选项
--with-http_v2_module HTTP/2 协议支持 多路复用、头部压缩、Server Push。所有现代浏览器均要求 HTTPS 走 HTTP/2
--with-http_realip_module 获取真实客户端 IP CDN/反向代理后端的真实 IP 透传
--with-http_sub_module 响应内容替换(sub_filter) 在不重新编译前端的情况下修改 HTML 中的 URL/文本
--with-http_stub_status_module 基础状态页 /nginx_status Prometheus nginx-exporter 依赖此模块提供 Active connectionsaccepts 等指标
--with-http_gzip_static_module 预压缩静态文件(.gz 文件直接发送) 免除每次请求的 gzip 压缩开销

Stream 四层模块

参数 启动的功能 核心使用场景
--with-stream 四层 TCP/UDP 代理 MySQL/MongoDB/SSH 等非 HTTP 协议的负载均衡
--with-stream_ssl_module 四层 TLS 卸载 将 TLS 解密从后端卸载到 Nginx(四层 SNI 路由)
--with-stream_ssl_preread_module 四层 SNI 预读 在同一 443 端口上基于客户端 SNI 域名转发到不同后端——无需解密 TLS($ssl_preread_server_name 变量)

编译与性能模块

参数 功能
--with-threads 启用线程池(配合 aio threads 减少磁盘 I/O 阻塞 worker 进程)
--with-file-aio 启用异步 I/O(Linux AIO)。配合 sendfile 将磁盘读取卸载到内核
--with-google_perftools_module Google TCMalloc 内存分配器。对于长期运行的 worker 进程,替换 glibc 的 ptmalloc2 可以降低内存碎片,尤其是在高并发短连接场景
--with-pcre 编译自带的 PCRE 库。系统 libpcre3 可能版本过旧,静态编译可锁定 PCRE 版本

现在编译并安装:

make -j$(nproc) && make install

-j$(nproc) 使用所有 CPU 核心并行编译。在我的 2 核 VPS 上编译时间约 1 分 30 秒。编译完毕后验证:

/usr/sbin/nginx -V
# 输出应包含所有 --with-* 模块的列表

4. 生产级 nginx.conf 模板

以下是一个开箱即用的生产级配置文件。这里我不只是贴代码——我会解释每一段为什么这样写。

# =====================================================
# Main Nginx configuration file (FHS Standard)
# /etc/nginx/nginx.conf
# =====================================================

user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 1024;
    multi_accept on;
}

# =====================================================
# TCP Stream:四层 SNI 预读 —— 同一 443 端口分流
# =====================================================
stream {
    map $ssl_preread_server_name $backend {
        probe.test.com        probe;
        blog.test.com         blog;
        filebox.test.com      filebox;
        default               blog;
    }

    upstream probe   { server 127.0.0.1:10001; }
    upstream blog    { server 127.0.0.1:10002; }
    upstream filebox { server 127.0.0.1:10003; }

    server {
        listen 443 reuseport;
        listen [::]:443 reuseport;
        proxy_protocol on;
        ssl_preread on;
        proxy_pass $backend;
    }
}

# =====================================================
# HTTP:七层配置
# =====================================================
http {
    # --- 真实 IP 获取(配合 Stream 的 proxy_protocol)---
    set_real_ip_from 127.0.0.1;
    set_real_ip_from ::1;
    real_ip_header proxy_protocol;
    real_ip_recursive on;

    server_tokens off;
    sendfile on;

    # --- JSON 格式日志(适配 ELK / Loki 日志平台)---
    log_format json escape=json '{'
        '"time_local":"$time_local",'
        '"remote_addr":"$remote_addr",'
        '"request":"$request",'
        '"status":$status,'
        '"body_bytes_sent":$body_bytes_sent,'
        '"http_referer":"$http_referer",'
        '"http_user_agent":"$http_user_agent",'
        '"request_time":$request_time,'
        '"upstream_response_time":"$upstream_response_time"'
    '}';

    access_log /var/log/nginx/access.log json;
    error_log /var/log/nginx/error.log;

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

    # --- Gzip 压缩优化 ---
    gzip on;
    gzip_comp_level 6;
    gzip_min_length 1k;
    gzip_types text/plain application/xml text/css application/javascript application/json;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*.conf;
}

重点解析:

  • multi_accept on:每个 worker 进程在一次事件循环中接受所有等待中的新连接,而不是一次只接受一个。连接建立频繁的场景(短连接 Web 服务)下有助于减少上下文切换。
  • reuseportSO_REUSEPORT 套接字选项。每个 worker 进程独立监听端口,内核通过哈希将连接分配到 worker。在 Linux 3.9+ 和 Nginx 1.9.1+ 中,这比单一的 accept_mutex 锁竞争更高效——这是 Cloudflare 在其边缘节点中广泛使用的技术(Cloudflare Blog, 2017)。
  • proxy_protocol on:因为 HTTP 七层后端在 127.0.0.1:10001-10003 接收的是 Stream 四层转发的连接,原始 TCP 连接信息(客户端 IP)已丢失。PROXY Protocol(HAProxy 发明的协议,RFC 未标准化但已成事实标准)在四层连接中插入一行包含原始客户端 IP 的头部,七层 Nginx 通过 real_ip_header proxy_protocol 解析。
  • sendfile onsendfile() 系统调用在内核空间直接完成文件→socket 的数据传输,绕过用户态缓冲区。对于静态文件服务,减少至少一次 read()write() 的上下文切换。Linux sendfile(2) man page 有完整说明。

5. Systemd 服务单元与平滑管理

编译安装的 Nginx 不是由包管理器安装的,不会有 systemd 单元文件。自己写一个——这不是"替代"包管理器,而是让你达到完全相同甚至更好的服务管理体验。

[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

核心参数说明:

参数 原因
Type forking Nginx 以 daemon 模式启动——master 进程 fork 出 worker 后自己留在前台。systemd 等待主进程 fork 后退出,然后以 PIDFile 追踪 master 进程
PIDFile /run/nginx.pid systemd 通过此文件跟踪 Nginx master 进程。如果 master 退出,systemd 自动重启服务(Restart=on-failure 可以加)
ExecStartPre nginx -t 启动前先做配置语法检查。配置有错就拒绝启动——在生产环境用 systemctl restart nginx 永远不会配错导致 Nginx 挂掉
ExecReload kill -s HUP 发送 SIGHUP 信号触发 Nginx 平滑重载——重新读取配置文件、启动新 worker、优雅关闭旧 worker。不会断开正在处理的连接。这是 Nginx 平滑重启的基础
ExecStop kill -s QUIT 发送 SIGQUIT 触发优雅关闭——等所有活跃连接处理完再退出。kill -s TERM(SIGTERM)是强制杀死,可能导致正在传输的请求被切断
PrivateTmp=true 为服务进程创建独立的 /tmp/var/tmp 命名空间,防止其他进程访问 Nginx 的临时文件,也防止 Nginx 被其他进程写入的临时文件污染

写入并启用:

cat > /etc/systemd/system/nginx.service << 'EOF'
...(上方内容)...
EOF

systemctl daemon-reload
systemctl enable --now nginx
systemctl status nginx

5.1 Nginx 信号管理速查

信号 命令 效果
SIGHUP systemctl reload nginx 平滑重载配置——读取新配置、新建 worker、逐步停用旧 worker
SIGQUIT systemctl stop nginx 优雅退出——等活跃连接处理完后关闭
SIGTERM kill -s TERM $(cat /run/nginx.pid) 立即退出(不等待连接)
SIGUSR1 kill -s USR1 $(cat /run/nginx.pid) 重新打开日志文件(日志轮转后使用)
SIGUSR2 kill -s USR2 $(cat /run/nginx.pid) 平滑升级二进制(启动新版本 master 进程,旧版本自动降级为 worker 并逐步退出)
平滑升级二进制的完整流程

Nginx 二进制升级不需要停服。完整流程:

# 1. 编译新版本(不执行 make install,只编译)
make -j$(nproc)

# 2. 备份旧二进制
cp /usr/sbin/nginx /usr/sbin/nginx.old

# 3. 用新编译的 objs/nginx 替换
cp objs/nginx /usr/sbin/nginx

# 4. 发送 USR2 信号——启动新版 master
kill -s USR2 $(cat /run/nginx.pid)

# 5. 旧 master 的 worker 会逐步退出,新 master 接管所有新连接
#    整个过程零停机时间

# 6. 确认新版本运行正常后,杀掉旧 master
kill -s QUIT $(cat /run/nginx.pid.oldbin)

这个能力是 Nginx 与 Apache 的一个核心差异——Apache 的 prefork MPM 不能在不中断连接的情况下换二进制。Nginx 的 master-worker 架构通过 fork() + exec() + 共享监听套接字实现了真正的零停机升级。


6. 生产环境补充建议

6.1 编译后清理

# 编译完成后,/usr/src/nginx-1.30.3/ 下包含大量 .o 中间文件(约 50 MB)
# 如果你磁盘空间紧张,可以删除源码目录
# 但建议保留——将来需要通过 ./configure 追加模块时需要完整的编译目录

6.2 worker_connections 的计算

worker_connections 1024 只是起步配置。实际生产值取决于你的场景:

max_clients = worker_processes × worker_connections

如果 worker_processes auto 在 4 核服务器上设了 4 个 worker:

max_clients = 4 × 1024 = 4096

但每个 keepalive 连接占用一个文件描述符。别忘了 Linux 默认 ulimit -n 是 1024——必须调到大于 worker_connections

ulimit -n 65535
# 或持久化:
echo "www-data soft nofile 65535" >> /etc/security/limits.conf
echo "www-data hard nofile 65535" >> /etc/security/limits.conf

6.3 SSL 会话缓存

如果跑 HTTPS,加这个配置减少 SSL 握手的 CPU 开销:

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

10 MB 的共享内存可以缓存约 40,000 个 SSL 会话。对于重复连接的客户端,这相当于让 SSL 握手变成"0-RTT"(实际是 session resumption,但 CPU 开销降到了对称加密的量级)。


7. 故障排查清单

症状 可能原因 排查命令
nginx -t 报 “unknown directive” 模块未编译进二进制 nginx -V 2>&1 | grep with-http_ssl 检查模块是否在编译选项中
systemctl start nginx 立即退出 端口被占用 ss -tlnp | grep 80cat /var/log/nginx/error.log
403 Forbidden 目录权限或 index 文件问题 ls -la /usr/share/nginx/html/ 确保 world-readable
重启后 PID 文件残留 /run 不是 tmpfs mount | grep /run 确认 tmpfs on /run type tmpfs
大文件上传失败(413) client_max_body_size 默认 1 MB server / location 块中设置 client_max_body_size 100m;
connect() to upstream failed 后端服务未启动或端口错误 ss -tlnp | grep <port> 确认后端监听状态

推荐阅读