Nginx官网安装这个事,说大不大,说小不小。很多人第一次接触Nginx,图省事直接找了个一键脚本或者系统自带的旧版本,结果后面想加个HTTP/2、想换目录结构、想自己定制模块的时候,发现处处受制。我自己的习惯是,要么用官方软件源,要么直接从官网拉源码编译,两条路都走通过,也都在上面栽过跟头。这篇就专门聊聊Nginx官网安装时那些容易卡壳的地方,从安装路径选择、依赖处理到编译参数配置和报错排查,把该避的坑一次说清楚。
适合看这篇的读者,主要是两类:一类是刚接触Nginx、想在自己机器上搭一个干净环境的开发者;另一类是已经在用系统自带Nginx、但苦于版本旧想切换到官网版本的同学。涉及的命令以主流Linux发行版为主,CentOS系的yum和Debian系的apt都会讲到。
1. 官网安装的两条路,选错等于白折腾
Nginx官网其实给出了不止一种安装方式,但最核心的是两条路:配置官方软件源用包管理器安装,以及下载源码包自己编译。
这两条路的体验差异非常大。我遇到过不少同事,一开始图省事选了任意一种,装到一半发现不对,又推倒重来。所以第一步就得想清楚自己到底需要什么。
1.1 官方软件源方式的真实体验
官方软件源的意思是,在系统的软件源列表里加入Nginx官方维护的仓库,然后通过yum或apt直接安装。这种方式最明显的优势是省心:依赖自动处理,升级方便,卸载干净,而且和系统包管理器的生态完全兼容。
具体做法很简单,以CentOS为例,在/etc/yum.repos.d/下新建一个nginx.repo文件,写入如下内容:
[nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=trueDebian系的写法不太一样,需要先导入签名密钥,再添加apt源:
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo gpg --dearmor -o /usr/share/keyrings/nginx-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/debian $(lsb_release -cs) nginx" | sudo tee /etc/apt/sources.list.d/nginx.list然后yum install nginx或apt update && apt install nginx就完事了。
但这条路径有个绕不开的问题:你能装的版本,是官方仓库里已经构建好的版本。虽然比系统自带的版本新很多,但它能用的编译参数已经被固定了。比如有些模块在官方预编译包里是作为动态模块额外提供的,你想把某个模块静态编译进Nginx主体里,那就做不到了。
1.2 源码编译适合什么样的场景
源码编译这条路径,适合的场景非常明确:
- 需要自定义安装路径,比如统一放到
/opt下 - 需要定制编译参数,比如把
http_v2_module、stream_module等直接编进去 - 需要用特定版本的Nginx,而不是官方源里的最新版本
- 需要编译第三方模块,比如一些开源的WAF模块或缓存模块
代价也很明确:依赖要自己装,升级要自己管,configure参数要自己研究,而且一旦系统里的PCRE或OpenSSL版本变了,编译好的Nginx可能会受影响。
所以我的建议是:不确定自己要不要定制的话,先用官方源安装;确认有定制需求了,再用源码编译。别一上来就编译,白白浪费时间。
2. 依赖问题是头号拦路虎
源码编译Nginx的时候,十个人里有七个会卡在依赖这一步。Nginx的编译依赖核心是三件套:PCRE、zlib、OpenSSL。听起来就是三个库,但它们的版本组合、系统包和源码包的混用方式,坑特别多。
2.1 PCRE与rewrite模块的强制依赖
PCRE库的作用是提供正则表达式支持,Nginx的rewrite模块、if指令、location匹配都依赖它。如果你在configure参数里没有显式指定PCRE,而系统里又恰好没有安装开发库版本,就会得到一个非常典型的报错:
checking for PCRE library ... not found ./configure: error: the HTTP rewrite module requires the PCRE library.这个报错已经写得很直白了:没有PCRE,rewrite模块编不了。解决办法有两种:一是用系统的包管理器安装pcre-devel(CentOS系)或libpcre3-dev(Debian系),然后在configure里指定--with-pcre;二是自己下载PCRE源码,通过--with-pcre=/path/to/pcre-source指定源码路径。
我强烈建议用系统包管理器安装,不要自己编译PCRE。原因很简单:PCRE是系统里很多其他软件也在用的基础库,系统包管理器装的和Nginx的配合最稳定,后续维护也省心。自己编译PCRE意味着你要自己跟踪PCRE的安全更新,这是给自己找事。
不过要注意一个细节:新版本的Nginx(1.21.0以后)已经默认使用PCRE2了。如果你装的是新版本Nginx,那么需要安装的是pcre2-devel。我遇到过有些同学看了老教程,装了PCRE1,configure还是报错,就是因为版本对不上。
2.2 zlib与gzip模块的编译策略
zlib库是gzip压缩模块的基础。没有它,gzip on;这个指令就直接失效,就算配置文件里写了,启动时也只会告警不会报错,但实际根本不生效。
编译时如果用--with-http_gzip_static_module这个静态模块,就必须有zlib的开发库。系统的包名是zlib-devel(CentOS系)或zlib1g-dev(Debian系)。
这里有个容易忽视的点:zlib的版本会直接影响压缩率。Nginx官方文档提到,zlib 1.2.9以后的版本对部分数据有更好的压缩效果。如果你系统里的zlib版本比较老,而且你对压缩率有要求,可以考虑在configure时用--with-zlib=/path/to/zlib-source指定一个较新的源码包路径,这样Nginx会使用你指定的源码进行编译,而不是链接系统动态库。
实际操作中,我一般直接用系统的zlib-devel,因为性能差距在绝大多数场景下感知不到。但如果你做的是高流量静态资源服务,压缩率的差异会被放大,值得关注一下。
2.3 OpenSSL与HTTP/2的支持边界
OpenSSL是三个依赖里最复杂的一个,因为它的版本直接决定你能不能用上HTTP/2和TLS 1.3。
Nginx的HTTP/2模块在1.9.5版本以后是默认不编译的,需要显式加--with-http_v2_module。HTTP/2本身需要OpenSSL 1.0.2以上版本支持ALPN协议,TLS 1.3则需要OpenSSL 1.1.1以上版本。
很多人在这一步踩坑:系统里的OpenSSL版本是1.0.2,虽然能开HTTP/2,但TLS 1.3用不了,检查配置才发现是底层的OpenSSL太旧。
处理方案有三种:
- 升级系统OpenSSL(风险较大,可能影响其他依赖它的软件)
- 下载新版OpenSSL源码到指定目录,用
--with-openssl=/path/to/openssl-source让Nginx编译时把OpenSSL静态编译进去 - 用
--with-openssl配合--with-openssl-opt传递OpenSSL的编译选项,做更精细的定制
我个人比较推荐第二种。Nginx可以通过--with-openssl把这个库直接编进Nginx二进制里,不影响系统其他软件,风险可控。这个方案的问题是要自己关注OpenSSL的安全更新,一旦有漏洞需要重新编译。
2.4 依赖安装顺序的坑
依赖的安装顺序看上去无关紧要,实际上很有讲究。正确顺序是先装好所有依赖,再跑configure,最后make。如果顺序反了,configure已经跑完才发现缺依赖,重新补装依赖后,configure结果可能已经过期,需要重新执行。
还有一个小细节:使用yum install安装依赖开发包的时候,注意看安装日志,确认没有出现"没有可用软件包"这类提示。我就遇到过因为yum源里没有pcre2-devel而安装失败的情况,当时还以为是命令输错了。
3. 编译安装核心步骤与参数选型
依赖搞定之后,真正的编译安装反而没那么难,难在configure参数怎么选。这步我单独拿出来说,因为网上抄来的configure命令五花八门,很多人根本不知道每个参数是什么意思,直接复制粘贴,编译出来的Nginx要么缺模块,要么路径不对。
3.1 configure参数应该怎么选
先给一个我在生产环境里常用的configure参数组合,然后逐个解释:
./configure \ --prefix=/usr/local/nginx \ --user=nginx \ --group=nginx \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_gzip_static_module \ --with-http_stub_status_module \ --with-http_realip_module \ --with-pcre \ --with-http_slice_module \ --with-threads \ --with-file-aio \ --with-stream \ --with-stream_ssl_module这里每个参数的作用是:
--prefix=/usr/local/nginx:指定Nginx安装目录。默认是/usr/local/nginx,如果你想放到别的地方,比如/opt/nginx,改这儿就行。--user=nginx --group=nginx:指定Nginx worker进程的运行用户。千万不能省,如果让worker进程用root跑,一旦Nginx被攻击,攻击者直接拿到root权限,后果很严重。--with-http_ssl_module:启用HTTPS支持,没有这个参数,配置ssl证书时会报"unknown directive ssl",这是最常见的问题之一。--with-http_v2_module:启用HTTP/2,这个前面说过了。--with-http_gzip_static_module:启用静态gzip模块,可以让你提前把.gz文件准备好,Nginx直接发送,不实时压缩,节省CPU。--with-http_stub_status_module:启用stub_status页面,可以在Nginx状态页看到连接数、请求数等监控数据。--with-http_realip_module:启用真实IP模块,如果Nginx前面还有一层负载均衡或CDN,这个模块可以获取到用户的真实IP。--with-pcre:启用PCRE库。即使系统里已经装了PCRE开发库,这个参数也建议显式加上。--with-http_slice_module:启用slice模块,处理大文件分段下载时很有用。--with-threads和--with-file-aio:启用线程和异步IO,在高并发静态文件场景下有效果。--with-stream和--with-stream_ssl_module:启用四层TCP/UDP代理,如果你的Nginx还要做端口转发、数据库代理,这两个必须加。
这些参数基本上覆盖了90%以上场景的需求。需要注意的是,Nginx版本不同,可用参数会有差异,跑configure之前最好先./configure --help看一眼,确认参数拼写正确。
3.2 make安装与目录结构
configure跑完且没有报错后,编译和安装就是两条命令的事:
make -j$(nproc) make install-j参数是并行编译,多少核就用多少并行任务,能明显加快编译速度。如果你是在一台只有1核1G内存的小机器上编译,-j小一点或者直接用make,否则内存很容易爆。
安装完成后,/usr/local/nginx下会有四个主要目录:
conf/:配置文件目录,nginx.conf在这里html/:默认的网页根目录logs/:日志目录sbin/:可执行文件目录
有个操作上的建议:刚安装完,先不要急着改配置,直接启动一次,用默认配置确认服务能跑起来,再做后续定制。这样如果后面修改配置出了问题,可以确定问题大概率出在配置上,而不是安装环节。
3.3 systemd服务脚本的编写要点
用make install编译安装的Nginx,默认不会注册成systemd服务。这意味着重启服务器后,Nginx不会自动启动,这是个隐患。
一个可用的systemd服务脚本长这样:
[Unit] Description=nginx - high performance web server Documentation=https://nginx.org/en/docs/ After=network-online.target remote-fs.target nss-lookup.target Wants=network-online.target [Service] Type=forking PIDFile=/usr/local/nginx/logs/nginx.pid ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ExecReload=/usr/local/nginx/sbin/nginx -s reload ExecStop=/usr/local/nginx/sbin/nginx -s quit PrivateTmp=true [Install] WantedBy=multi-user.target把这个文件保存到/etc/systemd/system/nginx.service,然后执行:
systemctl daemon-reload systemctl enable nginx systemctl start nginx有个容易踩的坑:PIDFile路径必须和nginx.conf里的pid指令一致。Nginx启动时会把主进程PID写到pid指令指定的文件里,systemd根据这个文件来判断服务状态。如果两边路径不一致,systemctl status nginx会显示服务没跑,但实际上Nginx是跑着的,排查起来非常迷惑。
4. 官网安装高频报错速查与排查套路
这块我本来想写成一张表,后来觉得光表还不够,因为很多报错背后的排查套路是相通的。就把典型的报错和排查思路放在一起,方便对照。
4.1 编译阶段的典型报错
编译阶段最常遇到的报错,基本都是依赖问题,报错信息通常长这样:
| 报错信息片段 | 可能原因 | 处理方法 |
|---|---|---|
checking for PCRE library ... not found | 缺PCRE开发库 | 安装pcre-devel或pcre2-devel |
the HTTP rewrite module requires the PCRE library | PCRE版本或配置不对 | configure时加--with-pcre |
checking for zlib library ... not found | 缺zlib开发库 | 安装zlib-devel |
checking for OpenSSL library ... not found | 缺OpenSSL开发库 | 安装openssl-devel |
make: *** No rule to make target | 依赖未完全安装 | 重新configure并确保依赖完整 |
cc1: fatal error: Killed signal terminated program cc1 | 编译时内存不足 | 调低-j参数或加swap |
有一个比较隐蔽的报错:编译过程中提示某个头文件找不到,但依赖检查的时候没有报错。这通常是因为你用的是系统的开发库,但开发库版本和运行时库版本不一致。排查方法很简单,用rpm -qa或dpkg -l查看已安装的devel包版本,再用ldconfig -p | grep 库名确认系统实际加载的版本,对不上就重装devel包。
4.2 启动阶段的报错
编译安装成功但启动失败,这个环节的报错类型也很集中。
最常见的是端口冲突。Nginx默认监听80端口,如果机器上已经有其他Web服务占了80端口,启动时就会报:
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)排查方式是用ss -tlnp | grep :80看是哪个进程占用了端口。如果是系统自带的Apache或httpd,需要先停掉;如果是你自己之前跑的临时Web服务,kill掉就行。
另一个高发问题是配置语法错误。Nginx启动时只加载配置,不检查所有指令的合法性,有些指令拼错了也会启动成功,要到运行时才报错。比较稳妥的做法是每次修改配置后,都跑一遍nginx -t,它会告诉你配置文件有没有语法问题:
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful如果出现unknown directive的提示,通常是对应的模块没编译进Nginx。比如配置里写了ssl_certificate指令,但configure时没加--with-http_ssl_module,启动阶段就会报unknown directive "ssl_certificate"。
4.3 服务运行后的隐性问题排查
有一些问题,Nginx能启动起来,但实际访问不了或效果不对,这类隐性问题排查起来比启动报错更费时间。
最典型的是Linux防火墙和网络策略。Nginx装好了,本机curl localhost有响应,但外部访问超时,八成是防火墙拦了端口。CentOS上需要确认firewalld的状态:
systemctl status firewalld firewall-cmd --list-ports如果有防火墙,开放端口:
firewall-cmd --zone=public --add-port=80/tcp --permanent firewall-cmd --reloadDebian系的话,可能是ufw:
ufw allow 80/tcp还有一个容易忽略的是SELinux。CentOS默认开启SELinux,即使防火墙放行了端口,SELinux也可能阻止Nginx访问某些文件或端口。表现出来就是访问页面403或504,但Nginx的错误日志里没有明显信息。
遇到这种情况,可以先用getenforce确认SELinux状态。如果是Enforcing,再查/var/log/audit/audit.log里有没有denied相关的记录。临时测试可以把SELinux设为Permissive模式:
setenforce 0如果确认是SELinux导致的,就需要根据实际场景设置对应的布尔值,或者给Nginx放行特定端口和目录。不建议直接永久关闭SELinux,毕竟它是有安全价值的。
5. 安装完成后的第一轮验证
安装完成不代表万事大吉,我给自己定的规矩是:新装好的Nginx必须过四关,才算验收通过。这四关分别是:配置语法检查、模块功能验证、启动与自启确认、访问验证。
5.1 nginx -t语法检查与nginx -V参数确认
第一件事就是用nginx -t检查配置文件,这个前面提过了,就不重复。
第二件事是用nginx -V确认编译参数。这个命令输出的是当前Nginx编译时的configure参数,但也容易被误解——nginx -V不会实时检查你系统里现有的依赖情况,只是显示编译时的快照信息。所以你用它来确认"有没有编译某个模块"是准的,但用它来判断"当前系统的OpenSSL版本"就不一定对。
实际操作中,我会用这一套组合确认模块:
nginx -V 2>&1 | tr ' ' '\n' | grep http_ssl nginx -V 2>&1 | tr ' ' '\n' | grep http_v2 nginx -V 2>&1 | tr ' ' '\n' | grep stream如果输出里出现了对应的模块名,说明编译进去了。
5.2 服务启动与自启确认
启动之前还有个细节:如果configure时指定了--user=nginx,那么系统里必须存在名为nginx的用户和用户组。如果没有,先用useradd -r nginx创建,否则Nginx启动时会报找不到用户。
创建完用户后,按照前面的systemd方案注册服务:
systemctl daemon-reload systemctl enable nginx systemctl start nginx systemctl status nginxsystemctl status输出里应该显示active (running),如果有Main PID的报错显示,多半就是PID文件路径不一致的问题,去检查nginx.conf里的pid指令。
自启确认这里我踩过一次坑:systemctl enable nginx执行成功,但重启服务器后发现Nginx没起来。排查一圈,发现是After=和Wants=配置的问题——systemd在依赖目标还没完全就绪时就启动了Nginx,Nginx启动时网络还没起来,绑定端口失败。后来改成After=network-online.target并在Wants=里加上network-online.target才解决。
5.3 访问验证与日志确认
最后一步,在浏览器里访问http://服务器IP,看到Nginx默认欢迎页,说明服务正常。
如果看不到欢迎页,按这个顺序排查:
- 本机
curl localhost有没有响应,没有就先查Nginx进程和配置 - 本机
curl 服务器内网IP有没有响应,没有就查防火墙 - 外部机器
curl 服务器公网IP有没有响应,没有就查云安全组
另外,装完新Nginx后,最好在logs/目录下确认一下access.log和error.log的正常生成。日志文件在出现第一个请求后才会被创建,如果你访问了页面但日志目录里是空的,说明Nginx可能根本没有收到那个请求,这才是问题所在。
最后再分享两个小技巧
编译安装过程中有几个细节,当时觉得无所谓,后来发现都非常实用,这里一并说说。
第一个,configure时加--with-cc-opt和--with-ld-opt参数。这两个参数用于传递C编译器和链接器的额外选项,比如你想开启某些优化、指定额外的头文件路径时很管用。在64位系统上编译,我会习惯加上这两个参数指向系统优化选项,编出来的二进制在极端高并发下表现更好。
第二个,Nginx编译完默认是带调试符号的,生产环境不需要这些额外的调试信息,会让二进制文件更大、运行效率略低。可以通过--with-cc-opt=-O2配合--without-debug来去掉调试符号。这里要说清楚,--without-debug这个参数不是所有版本都支持,用之前建议先确认一下。
我个人在实际操作中的一个体会是:Nginx官网安装的过程,本质上是一个"理解构建系统"的过程。如果你只是想要一个能用的Nginx,官方软件源十分钟就搞定;但如果你想理解Nginx的模块机制、依赖关系、编译参数,源码编译这条路绕不开。两种方式我都推荐你至少走一遍——先用官方源快速跑起来,再用源码编译深度定制一次,踩过一遍坑之后,再看网上那些花里胡哨的教程,一眼就能看出哪些是瞎写的,哪些是真有用的。