nginx 编译指南:静态编译 + 动态编译

本文档覆盖两种编译方式,每一部分都从下载讲到安装

  • 第一部分:静态编译 ------ 产出单一静态二进制,不依赖目标机任何库,可打包发给别人直接使用。
  • 第二部分:动态编译 ------ 链接系统共享库,体积小、编起来快,但一般只在当前机器/同环境使用。

总览:静态 vs 动态

对比项 静态编译 动态编译
依赖 把 pcre/zlib/openssl/libc 全部编进二进制 运行时链接系统的 .so 共享库
产物体积 大(几 MB ~ 十几 MB) 小(二进制本身几百 KB,库在系统里)
跨机器可用性 可打包发给任意同架构 glibc 系 Linux 直接用 目标机必须有版本匹配的系统库,否则跑不了
编译复杂度 高(要拉依赖源码、强制静态链接、常踩 OpenSSL 坑) 低(用系统库或包管理器一行装好)
典型用途 一次性构建、跨发行版分发、Docker 基础镜像 在固定机器/CI 上部署,或就用发行版自带包

第一部分:静态编译(Statically Linked)

目标:把 nginx 编译成单一静态二进制 ,不依赖目标机的 pcre / zlib / openssl / libc

共享库,可直接拷贝到任意同架构 Linux(CentOS、Ubuntu 等)上运行。

1.1 下载并解压源码

静态编译需要 PCRE、zlib(以及可选的 OpenSSL)的源码包 ,nginx 在编译时把它们一起编进二进制。

在 nginx 源码目录的同级下载并解压:

bash 复制代码
cd ~
# nginx 本体
wget https://nginx.org/download/nginx-1.30.4.tar.gz
# 经典 PCRE 8.x(与 nginx --with-pcre 兼容性最好)
wget https://downloads.sourceforge.net/project/pcre/pcre/8.45/pcre-8.45.tar.gz
# zlib(注意:官网没有 1.3.1,当前稳定版是 1.3.2)
wget https://zlib.net/zlib-1.3.2.tar.gz
# OpenSSL(仅当需要使用 HTTPS 时;纯 HTTP 反代可不要)
wget https://www.openssl.org/source/openssl-3.2.1.tar.gz

tar -xzf nginx-1.30.4.tar.gz
tar -xzf pcre-8.45.tar.gz
tar -xzf zlib-1.3.2.tar.gz
tar -xzf openssl-3.2.1.tar.gz
cd nginx-1.30.4

目录结构(关键:依赖源码与 nginx 源码平级):

复制代码
~/build/
├── nginx-1.30.4/      # 进入此目录执行 ./configure
├── pcre-8.45/
├── zlib-1.3.2/
└── openssl-3.2.1/

1.2 清理旧配置(如已 configure 过)

如果之前跑过普通 ./configure(比如提示 "using system PCRE library"),必须先清掉,否则新配置不生效:

bash 复制代码
cd nginx-1.30.4
rm -rf objs Makefile

1.3 静态 configure

nginx 没有 --enable-static 开关,需要手动把依赖源码编进去并强制静态链接。按是否需要 HTTPS 选方案。

方案 A:纯 HTTP 反代(推荐,最稳)

当前用途是 HTTP 反向代理,proxy_pass http://127.0.0.1:8000不需要 SSL

不带 OpenSSL 可避免静态链接 OpenSSL 的各种麻烦:

bash 复制代码
cd nginx-1.30.4
./configure \
  --prefix=$(pwd)/../nginx_static_dist \
  --with-pcre=../pcre-8.45 \
  --with-zlib=../zlib-1.3.2 \
  --with-cc-opt="-static" \
  --with-ld-opt="-static -lpthread"

方案 B:带 HTTPS(SSL)

如需要 443 / SSL,加入 OpenSSL 源码与模块。OpenSSL 静态链接在 glibc 下偶尔挑剔,必要时补 -ldl

bash 复制代码
cd nginx-1.30.4
./configure \
  --prefix=$(pwd)/../nginx_static_dist \
  --with-pcre=../pcre-8.45 \
  --with-zlib=../zlib-1.3.2 \
  --with-openssl=../openssl-3.2.1 \
  --with-http_ssl_module \
  --with-http_v2_module \
  --with-cc-opt="-static" \
  --with-ld-opt="-static -lpthread -ldl"

若链接阶段报 OpenSSL 相关错误,最省事的办法是暂时不带 SSL (用方案 A)。

想彻底避开 glibc 的静态链接坑,见 1.7 节 musl 方案。

⚠️ 特殊说明:CentOS 7 上编 OpenSSL 3.x 极易失败(务必先读)

环境特征 :CentOS 7.x 自带 gcc 4.8.5 (2015 年,Red Hat 4.8.5-44),系统 Perl 也偏旧。

用它去编 OpenSSL 3.2.1(方案 B) 时,./configure 能过(它只检测能否找到 OpenSSL 源码,

不真正编译),但 make 阶段会踩坑,常见两类报错:

坑 1:缺 Perl 模块 IPC::Cmd(最先撞到)

复制代码
Can't locate IPC/Cmd.pm in @INC ...
BEGIN failed--compilation aborted at .../openssl-3.2.1/Configure line 23.
make[1]: *** [.../openssl-3.2.1/.openssl/include/openssl/ssl.h] Error 2
make: *** [build] Error 2

OpenSSL 3.x 的 Configure 脚本依赖 Perl 的 IPC::Cmd,CentOS 7 默认不带 → 直接中断。

坑 2:即使补上 Perl 模块,gcc 4.8.5 仍可能编不过 OpenSSL 3.2 的 C 代码(版本过老)。

判断依据 :若 make 日志里 PCRE、zlib 都已编译成功 (pcre 打印 configuration summary、

zlib 生成 libz.a),只在 OpenSSL 处 Error 2 中断,即是此坑。

✅ 推荐解决:方案 A(去掉 OpenSSL,只编 HTTP 反代)

纯 HTTP 反向代理(proxy_pass http://127.0.0.1:8000根本用不到 HTTPS ,OpenSSL 纯属多余。

重配一次即可绕开全部 OpenSSL 问题(PCRE / zlib 会自动重编,很快):

bash 复制代码
cd nginx-1.30.4
rm -rf objs Makefile
./configure --prefix=$(pwd)/../nginx_static_dist \
  --with-pcre=../pcre-8.45 \
  --with-zlib=../zlib-1.3.2 \
  --with-cc-opt="-static" \
  --with-ld-opt="-static -lpthread"
make -j$(nproc)
make install     # root 用户无需 sudo
仅当确实需要 HTTPS 时(不推荐在 CentOS 7 硬编)
  1. 先补 Perl 模块:yum install -y perl-IPC-Cmd(base 源没有可试 EPEL 或 cpan IPC::Cmd);
  2. 再升级编译器:scl enable devtoolset-9 bash 把 gcc 升到 9 后重新 make
  3. 或退回 OpenSSL 1.1.1(已 EOL,有安全隐患,不建议)。

结论 :在 CentOS 7 上做静态编译,优先用方案 A(无 SSL) ,最稳、一次过;

需要 HTTPS 再按上面升级 gcc + 补 Perl 模块,或改用 1.7 节的 Alpine/musl 路线。

1.4 编译并安装

bash 复制代码
make -j$(nproc)
make install     # root 用户直接运行;prefix 指向系统目录(如 /usr/local)时才需 sudo

安装位置(由 --prefix=$(pwd)/../nginx_static_dist 决定,即 nginx 源码同级

nginx_static_dist 目录,与源码分开、便于打包):

  • 二进制:../nginx_static_dist/sbin/nginx
  • 配置:../nginx_static_dist/conf/nginx.conf
  • pid / 日志:../nginx_static_dist/logs/

以上为相对 nginx-1.30.4 目录的路径;make install 后回到上一级 ls 就能看到新出现的

nginx_static_dist/。绝对路径示例:/root/nginx_static_build/nginx_static_dist/sbin/nginx
源码编译版不在 apt/dnf 管理内,nginx 不在 PATH,需用全路径

.../nginx_static_dist/sbin/nginx 运行,或自行 ln -s/usr/local/bin

1.5 验证是静态的

bash 复制代码
ldd ../nginx_static_dist/sbin/nginx
# 期望输出:'not a dynamic executable'

file ../nginx_static_dist/sbin/nginx
# 期望包含:'statically linked'

看到 statically linked 即成功,该二进制可拷到 CentOS / Ubuntu 直接运行。

1.6 ★ 打包分发给他人(静态编译专属)

静态编译的最大价值就在这里:对方机器不需要装任何依赖,拿到包解压即跑

步骤 1:在你的编译机上打包整个安装目录

因为 --prefix=$(pwd)/../nginx_static_dist 已经把成品装到了一个独立目录 (与源码分开、便于打包),

直接打包这个目录即可,干净利落:

bash 复制代码
# 回到安装目录的父目录(即源码同级)
cd ..
tar -czf nginx_static_dist.tar.gz nginx_static_dist

包名与目录名一致,便于识别。如需区分不同系统编的产物,可在打包时自行加后缀,例如

nginx_static_dist_centos-7.tar.gznginx_static_dist_ubuntu-22.04.tar.gz

步骤 2:对方解压并运行

bash 复制代码
# 解压到任意目录,例如 /opt(目录名同名落位即可直接运行;否则用 -p 指定运行前缀,见步骤 3)
tar -xzf nginx_static_dist.tar.gz -C /opt/
# 解压到与编译时同名目录时可直接运行;否则用 -p 指定运行前缀(见步骤 3)
/opt/nginx_static_dist/sbin/nginx

步骤 3:为什么建议用 -p

--prefix 路径(.../nginx_static_dist 的绝对路径)被写死进了二进制 。对方解压到别的位置时,

若直接裸跑 sbin/nginx,它会按编译机那个绝对路径去找 conf/logs 而失败。用 -p 覆盖前缀即可:

bash 复制代码
/opt/nginx_static_dist/sbin/nginx \
  -p /opt/nginx_static_dist \
  -c /opt/nginx_static_dist/conf/nginx.conf

-p 覆盖前缀(配置/日志/临时目录都按它找),-c 指定配置文件,这样二进制放哪都能跑。

必须确认的两个前提

  1. CPU 架构必须一致:x86_64 编的不能给 ARM(aarch64)机器跑。静态只解决"库依赖",不解决"架构"。
  2. glibc 静态的 DNS 限制 :静态链接 glibc 后,域名解析(NSS)是动态加载的、用不了,会导致
    proxy_pass / upstream 里用主机名 时在启动时报 host not found
    因此给对方的那份 nginx.conf 一律写 IP 字面量 (如 127.0.0.1),不要写主机名。

对方机器自检(可选但建议)

bash 复制代码
ldd /opt/nginx_static_dist/sbin/nginx    # 换成对方实际解压路径
# 显示 not a dynamic executable → 说明不依赖任何 .so,能跑

只要显示 not a dynamic executable,对方无需装 pcre/zlib/openssl/glibc 任何东西。

1.7 备选:用 musl 编译(最干净的可移植静态二进制)

(musl 产物本身已高度可移植,可直接打包分发,无需额外系统标识;如确需区分,自行在包名加后缀即可。)

glibc 静态总有 NSS / TLS 等历史包袱。社区做"一个二进制通吃所有 Linux"通常用

musl libc 编译 nginx(Alpine 系,或 musl-gcc 工具链),产出的静态二进制无 glibc

依赖、DNS 也正常。若需频繁跨发行版分发,这条路线更彻底。

1.7.1 方式 A:在 Ubuntu / Debian 上用 musl-gcc 直接编

bash 复制代码
sudo apt update
sudo apt install -y musl-tools gcc make   # musl-tools 提供 /usr/bin/musl-gcc

cd nginx-1.30.4
CC=/usr/bin/musl-gcc ./configure \
  --prefix=/usr/local/nginx \
  --with-pcre=../pcre-8.45 \
  --with-zlib=../zlib-1.3.2 \
  --with-cc-opt="-static" \
  --with-ld-opt="-static"
make -j$(nproc)
sudo make install

说明:

  • musl 自带 pthread,无需 -lpthread;通常也不需要 -ldl
  • musl 静态二进制 DNS 正常(用 musl 自带解析器,无 NSS 动态加载问题),比 glibc 静态更省心。
  • 若要带 HTTPS,加 --with-openssl=../openssl-3.2.1 --with-http_ssl_module --with-http_v2_module
    musl 下静态链 OpenSSL 比 glibc 顺利,但若链接报错,可先不带 SSL 跑通流程。

1.7.2 方式 B:在 Alpine 容器里编(最推荐,宿主零污染)

bash 复制代码
# 宿主机执行:把当前目录(含 nginx / pcre / zlib 源码)挂进容器
docker run --rm -it -v "$PWD":/work -w /work alpine:latest sh

# 容器内:
apk add --no-cache build-base wget
cd nginx-1.30.4
./configure \
  --prefix=/usr/local/nginx \
  --with-pcre=../pcre-8.45 \
  --with-zlib=../zlib-1.3.2 \
  --with-openssl=../openssl-3.2.1 \
  --with-http_ssl_module --with-http_v2_module \
  --with-cc-opt="-static" --with-ld-opt="-static"
make -j$(nproc)
make install
# 产物 /usr/local/nginx/sbin/nginx 即 musl 静态二进制,拷出容器可在任意 Linux 直接运行

注意:Alpine 默认 musl 环境。若链接阶段报缺 libssl.a / libcrypto.a,在容器内

apk add --no-cache openssl-libs-static。纯 HTTP 反代可去掉 --with-openssl 三段,最干净。


第二部分:动态编译(Dynamically Linked)

目标:编出的 nginx 链接系统的共享库 (libpcre / libssl / libc 等),

体积孝编译快,但通常只在当前机器 / 同环境使用,不适合随便发给别人。

动态编译有两种做法:

  • 做法一(推荐):直接用发行版包管理器装,得到的就是动态链接的 nginx,一行搞定。
  • 做法二:从源码动态链接编译(链接系统库,而非把依赖编进二进制)。

2.1 下载与准备(仅做法二需要)

做法一只用包管理器,无需下载源码。做法二需要从源码编,先装好构建依赖 (系统开发库),

再下载解压 nginx 源码(依赖用系统的,不必下载 pcre/zlib/openssl 源码):

bash 复制代码
# Ubuntu / Debian
sudo apt update
sudo apt install -y build-essential libpcre3-dev zlib1g-dev libssl-dev wget

# CentOS / RHEL / Fedora
sudo dnf install -y gcc make pcre-devel zlib-devel openssl-devel wget

# 下载并解压 nginx 源码
cd ~
wget https://nginx.org/download/nginx-1.30.4.tar.gz
tar -xzf nginx-1.30.4.tar.gz
cd nginx-1.30.4

2.2 做法一:包管理器安装(最推荐)

绝大多数情况下,动态编译 = 直接用系统包。无需下载、无需编译、自动注册服务、自动升级:

bash 复制代码
# Ubuntu / Debian(含 WSL)
sudo apt update && sudo apt install -y nginx

# CentOS / RHEL / Fedora
sudo dnf install -y nginx

安装位置(发行版约定,非源码前缀):

  • 二进制:/usr/sbin/nginx
  • 配置:/etc/nginx/
  • 服务:systemctl start nginx / systemctl enable nginx

装好即是一个动态链接的 nginx,可直接用。

2.3 做法二:源码动态编译

从源码编但不静态链接 :不传 --with-*-opt="-static",也不传 --with-pcre/zlib/openssl

源码路径(让 nginx 使用 2.1 装好的系统开发库):

bash 复制代码
cd nginx-1.30.4
./configure \
  --prefix=/usr/local/nginx \
  --with-http_ssl_module \
  --with-http_v2_module
make -j$(nproc)
sudo make install

要点:

  • 没写 --with-pcre=../pcre-8.45 这类源码路径 → nginx 改用系统的 PCRE / zlib / OpenSSL 开发库。
  • 没写 --with-cc-opt="-static" / --with-ld-opt="-static" → 最终二进制动态链接系统库。
  • ./configure 报缺 PCRE / SSL,说明 2.1 的开发库没装全,回去补 libpcre3-dev / libssl-dev

2.4 验证是动态的

bash 复制代码
ldd /usr/local/nginx/sbin/nginx
# 会列出一堆 .so,例如 libpcre.so、libssl.so、libc.so → 说明是动态链接

动态二进制依赖目标机存在版本兼容的这些 .so,这正是它不适合随意分发的原因。

2.5 动态编译的产物能打包给别人吗

一般不能像静态那样直接发。 动态二进制在对方机器上运行时,必须能找到版本匹配

libpcre / libssl / libc 等 .so;对方发行版/版本不同就跑不起来。

可行的分发方式:

  • 对方也用同发行版同版本 → 让对方同样 apt/dnf install nginx(或你发的是相同环境的包)。
  • 跨环境分发 → 改用 1.6 节的静态编译,或把 nginx 连同运行环境一起用 Docker 镜像 分发
    FROM 基础镜像里带好库,最稳)。

附录 A:常用命令速查

下面用 /usr/local/nginx 作示例路径;若你按第一部分把 --prefix 设到了其他目录,请把路径换成你的实际安装目录(或分发后用 -p 指定的目录)。

bash 复制代码
# 启动
sudo /usr/local/nginx/sbin/nginx
# 停止(快速)
sudo /usr/local/nginx/sbin/nginx -s stop
# 优雅停止
sudo /usr/local/nginx/sbin/nginx -s quit
# 改配置后热重载
sudo /usr/local/nginx/sbin/nginx -s reload
# 验证静态 / 动态
ldd /usr/local/nginx/sbin/nginx

附录 B:示例 nginx.conf(HTTP 反代,IP 字面量)

nginx 复制代码
worker_processes  1;

events {
    worker_connections  1024;
    # use poll;   # Windows 版才需要;Linux 原生用 epoll,无需此行
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;
    keepalive_timeout  65;

    server {
        listen 8001;
        server_name _;
        location / {
            proxy_pass http://127.0.0.1:8000;
            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 8081;
        server_name _;
        location / {
            proxy_pass http://127.0.0.1:8082;
            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;
        }
    }
}

注意:listen 侧可用 0.0.0.0(绑定所有网卡),但 proxy_pass / upstream

的目标必须是 127.0.0.1 这类具体可达地址,不能写 0.0.0.0。静态编译分发时

尤其要坚持写 IP 字面量,规避 glibc 静态的 DNS 限制。

相关推荐
元岳数字人小元1 小时前
数字人系统源码:决定一体机长期运维稳定性的核心因素
运维·开源·人机交互·交互·源代码管理
田里的水稻1 小时前
EP_局域网传输音视频的主流方案
运维·人工智能·机器人·音视频
运维大师2 小时前
【Linux运维极简教程】11-防火墙与安全加固
运维·安全
Icoolkj2 小时前
ICOOLKJ 前后端分离项目完整部署文档(MySQL8.4+Redis+JDK21+Nginx+HTTPS+自动同步演示库)
redis·nginx·https
Faith_xzc2 小时前
Apache Doris 元数据运维深度解析:从架构原理到故障实战
大数据·运维·doris
kdxiaojie2 小时前
Linux 驱动研究 —— V4L2 (3)
linux·运维·笔记·学习
facaixxx20243 小时前
雨云游戏服务器MCSM面板指南:低延迟BGP线路(一键开服)
运维·服务器·游戏
焦点链创研究所3 小时前
算力核心机器人将于 7 月 18 日发布
运维·服务器·机器人
Starry-sky(jing)4 小时前
如何判断 Windows 是否安装 Node.js:三种方法 + 常见漏判场景
运维·windows·node.js·环境检测