我接触 Linux 的第一台服务器,装的就是 Nginx。从那以后,差不多十年时间里,不管给客户搭 Web 服务、做反向代理,还是自己折腾静态博客,Nginx 都是最先装的那批组件。今天这篇不聊虚的,就把“linux 系统下载安装 nginx”这条主线理清楚:源码编译和包管理器两种方式到底怎么选、安装时那些 configure 参数改不改、装完之后的目录结构和第一个站点怎么配、以及我这些年踩过的坑和排查思路。不管你是刚接触 Linux 的运维新人,还是准备在云服务器上部署项目的开发者,这篇文章都能给你一份可以直接照着抄的作业。
1. 安装方案选型与准备工作
1.1 源码编译和系统包管理器,到底选哪个
很多人第一次装 Nginx 都会纠结一件事:到底是用yum install nginx或者apt install nginx直接装,还是去官网下载源码包自己编译。我说一下我的选择逻辑。
系统包管理器安装的优势是快、干净、依赖自动处理。CentOS 上用 yum,Ubuntu 上用 apt,命令敲完,服务脚本、默认配置、日志轮转全都给你安排好了。缺点是版本往往偏旧,比如你在 CentOS 7 的默认源里装的 Nginx,版本可能还停在 1.16 或者 1.18,而且部分模块默认没编译进去。CentOS 官方源甚至没有 nginx 包,你得先启用 EPEL 源,这一步对新手来说就是第一个坑。
源码编译安装的过程虽然长一点,但可控性最强。你可以自己指定安装目录、选择需要的模块、使用较新的版本,生产环境里定制化程度高的场景基本都会走这条路。它需要你手动装好 gcc、pcre、zlib、openssl 等依赖,装完之后也没有现成的 systemd 管理脚本,得自己写一个。
我的建议很简单:如果只是内网快速搭一个应用、为了跑通功能,直接用系统包管理器,省时间;如果是要上生产环境、需要对版本和模块有明确要求,或者公司规范里要求统一安装路径,那就源码编译。两种方式下文都会给完整流程。
1.2 环境和依赖准备,一个都不能少
不管是哪种安装方式,装依赖都是第一步。源码编译对依赖的要求最严格,缺失任何一个编译时都会报错。下面以 CentOS 系和 Ubuntu 系分别给出准备命令。
CentOS / RHEL 系:
yum install -y gcc gcc-c++ make pcre pcre-devel zlib zlib-devel openssl openssl-develUbuntu / Debian 系:
apt update apt install -y build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev这里重点说下每个依赖是干嘛的:gcc 是编译器,没有它没法把 C 源码变成可执行文件;pcre 是正则表达式库,Nginx 的 location 匹配、rewrite 规则都依赖它;zlib 负责 gzip 压缩,静态资源压缩传输靠它;openssl 是 HTTPS 的基础,ssl 模块、证书加载都离不开它。编译时如果缺了某个库,通常缺的是xxx-devel(CentOS)或xxx-dev(Ubuntu)这种带开发头的包,而不只是运行库本身,这个是新手最常忽略的。
另外,在进安装流程之前,建议先做两个检查。一是看系统里是不是已经装过 Nginx:
which nginx nginx -v如果已经存在,先想清楚是要覆盖升级还是卸载重来,不要直接开始新的编译,后面会出现同名文件覆盖、路径混乱的问题。二是检查 80 端口是否被占用:
ss -lntp | grep :80端口被 Apache 或者其他 Web 服务占用是启动失败最常见的坑,提前发现能省很多排查时间。
1.3 版本选择与国内镜像加速
Nginx 官网提供三个版本线:Mainline 主线版、Stable 稳定版、Legacy 历史版。生产环境我强烈建议选 Stable,功能迭代比主线版慢,但经过更长时间的测试,出了问题也好搜解决方案。主线版通常带有新特性,适合测试环境尝鲜,不建议直接上生产。
下载地址是https://nginx.org/download/,文件格式一般类似nginx-1.26.2.tar.gz。国内服务器直接访问官网有时候速度不太稳定,可以用国内的开源镜像站下载同名文件。下载完成后最好做一下文件校验,确保压缩包完整。
wget https://nginx.org/download/nginx-1.26.2.tar.gz tar -zxvf nginx-1.26.2.tar.gz cd nginx-1.26.2版本号不是越新越好,我见过有人为了追新版本装了个主线版,结果某个旧模块不兼容,折腾半天又回退了。选一个你所在团队其他人用过的稳定版本,往往比选最新版本更稳妥。
2. 源码编译安装 Nginx 全流程
2.1 configure 参数逐个说清楚
进入解压后的源码目录,第一步是执行./configure。很多第一次接触源码编译的人,看到满屏的参数就懵了。其实参数就两类:一类是决定功能模块的--with-xxx,一类是决定路径和身份的--prefix、--user、--group。
我下面给一个生产环境常用的配置,你直接抄就能用:
./configure \ --prefix=/usr/local/nginx \ --user=nginx \ --group=nginx \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_realip_module \ --with-http_stub_status_module \ --with-http_gzip_static_module \ --with-stream \ --with-stream_ssl_module \ --with-pcre逐个说一下这些参数为什么重要。
--prefix=/usr/local/nginx是安装根目录。默认也是这个路径,但显式写出来能让后续维护的人一眼知道 Nginx 装在哪里。路径一旦定下来,后面升级时要保持前缀不变,否则会遇到配置文件、日志、二进制文件分散在两个目录的问题,非常痛苦。
--user=nginx和--group=nginx是让 Nginx 的 worker 进程以普通用户身份运行。默认会用 nobody,但在编译前先创建一个专用的 nginx 用户,权限颗粒度更细,也更安全。创建命令是useradd -s /sbin/nologin -M nginx。
--with-http_ssl_module必须加。不加这个,后面你想配置 HTTPS、加载 SSL 证书的时候,配置能写但 Nginx 会报错说不认识listen 443 ssl,到时候只能重新编译加模块,非常麻烦。
--with-http_v2_module是 HTTP/2 支持。网站打开速度、并发能力都会受益,尤其是有大量小文件的 Web 场景。注意这个模块在新版本 Nginx 里是默认开启的,但老版本需要显式加上。
--with-http_realip_module在做反向代理时非常关键。没有它,后端应用拿到的一律是 Nginx 所在服务器的 IP,有了它才能把真实客户端 IP 传过去,日志分析和风控都依赖这个。
--with-http_stub_status_module提供一个/nginx_status页面,可以看到当前活跃连接数、请求总数等信息,排障的时候特别有用。
--with-stream是四层 TCP/UDP 代理模块。如果你计划用 Nginx 转发数据库端口、SSH 端口或者做 TCP 负载均衡,就必须编译这个模块。--with-stream_ssl_module则是在四层代理中支持 SSL 透传或者终止。
这里插一句经验:不要什么模块都往上加,加的模块越多,编译时间越长,二进制体积越大,暴露在外的攻击面也越大。按需开启才是正解。
2.2 make 编译与 make install,以及常见报错
configure 执行成功后,会生成 Makefile 文件。接下来就是编译和安装:
make -j 4 make install-j参数是并行编译,后面的数字通常是 CPU 核数的一半或者等于核数。比如四核机器用-j 4,八核机器用-j 8,能明显缩短编译时间。如果编译过程中报错,先别慌,看报错信息缺什么就补什么,然后重新执行 configure 和 make。
最常见的两个报错都是依赖缺失引起的:
the HTTP rewrite module requires the PCRE library:说明没装 pcre-devel,补上后重新 configure。the HTTP gzip module requires the zlib library:说明没装 zlib-devel,同样补包。
还有一种情况是 openssl 版本过低或者路径找不到,checking for OpenSSL library ... not found。这时候先确认openssl version能正常输出,再检查openssl-devel是否安装。CentOS 7 上尤其容易遇到这种问题。
安装完成后,Nginx 二进制默认在/usr/local/nginx/sbin/nginx。运行nginx -V(注意是大写 V)可以看到编译时的所有参数和版本号,这是后续排障和升级时最重要的信息之一。
验证启动:
/usr/local/nginx/sbin/nginx -t /usr/local/nginx/sbin/nginxnginx -t是测试配置文件的语法,输出syntax is ok和test is successful就说明没问题。启动后访问服务器 IP,看到 Welcome to nginx 页面就成功了。
2.3 写一个 systemd 管理脚本
源码编译安装的 Nginx 默认没有 systemd 服务文件,导致你没法用systemctl start nginx来管理,杀进程和开机自启都得手动搞。我建议装完立刻补上这个文件。
新建/etc/systemd/system/nginx.service:
[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 ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf 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保存后执行:
systemctl daemon-reload systemctl enable nginx systemctl start nginx systemctl status nginxType=forking对应 Nginx 启动时 master 进程 fork 出 worker 进程的行为。PIDFile指向的 pid 文件要真实存在,路径别写错,否则 systemd 会认为启动失败。这个脚本是我经过多台服务器验证过的,直接拿去用没问题。
3. 用系统包管理器安装的差异化流程
3.1 CentOS 与 Ubuntu 的包管理器安装
如果你决定用包管理器,流程比源码编译快得多,但不同发行版的细节差异值得单独说一下。
CentOS / RHEL 7/8/9 的官方源里默认没有 nginx,需要先安装 EPEL 源:
yum install -y epel-release yum install -y nginx装完之后,配置文件的分布是:主配置在/etc/nginx/nginx.conf,子配置默认加载/etc/nginx/conf.d/*.conf,网站的根目录在/usr/share/nginx/html,日志在/var/log/nginx/。启动方式是用 systemd。
Ubuntu / Debian 系的安装更直接,官方源里就有:
apt update apt install -y nginxUbuntu 安装完会自动启动服务并设置开机自启。它的配置结构多了一层/etc/nginx/sites-available和/etc/nginx/sites-enabled,允许你像管理多个站点一样管理虚拟主机。如果你不熟悉这种结构,新手最容易蒙圈的就是明明写好了配置却不生效,因为没在sites-enabled里建软链接。
验证包管理器安装结果:
nginx -v systemctl status nginx curl -I http://127.0.0.1看到 HTTP/1.1 200 OK 的返回头,基本就确认服务正常了。
3.2 两种方式的路径差异与选型建议
包管理器安装和源码编译安装的目录结构差异很大,我用一张表把它们的关键路径摆出来方便对照:
| 项目 | 包管理器安装(yum/apt) | 源码编译安装 |
|---|---|---|
| 二进制文件 | /usr/sbin/nginx | /usr/local/nginx/sbin/nginx |
| 主配置 | /etc/nginx/nginx.conf | /usr/local/nginx/conf/nginx.conf |
| 子配置目录 | /etc/nginx/conf.d/ | 需自行创建 conf.d |
| 默认站点目录 | /usr/share/nginx/html | /usr/local/nginx/html |
| 日志目录 | /var/log/nginx/ | /usr/local/nginx/logs/ |
| 进程管理 | systemctl 直接可用 | 需自建 service 文件 |
| 升级方式 | yum 或 apt 更新 | 重新编译并平滑升级 |
从这张表能看出来,包管理器把文件分散到系统各个标准目录,符合 FHS 规范,和 selinux、logrotate 这些系统组件联动方便。源码编译则把所有东西集中在/usr/local/nginx一个目录下,多实例部署、整体迁移、按团队规范管理都更方便。
我个人在生产环境里更倾向于源码编译安装。原因有三个:一是版本可控,不会因为一次yum update把 Nginx 意外升级掉;二是模块可控,编译了什么模块一目了然;三是路径统一,团队成员换了一拨又一拨,但nginx -V一看就能了解这台机器的 Nginx 是怎么来的。当然,如果只是临时搭个环境玩一下,包管理器完全够用,没必要为了一个测试服务折腾编译。
4. 安装后的核心配置与首个站点
4.1 Nginx 目录结构与关键文件解读
装完之后先别急着写配置,把目录结构摸清楚再动手,效率会高得多。源码编译安装的目录结构大概是这样的:
/usr/local/nginx/ ├── conf/ │ ├── nginx.conf │ ├── mime.types │ └── fastcgi_params ├── html/ │ ├── index.html │ └── 50x.html ├── logs/ │ ├── access.log │ ├── error.log │ └── nginx.pid └── sbin/ └── nginxconf/nginx.conf是主配置,几乎所有的行为都由它控制。mime.types定义了文件后缀和 Content-Type 的映射关系,默认不用动。html/是默认站点目录,刚装完可以通过访问服务器 IP 看到官方欢迎页。logs/下三个文件很重要:access.log 记录所有访问日志,error.log 记录错误信息,nginx.pid 存 master 进程的 PID,后面平滑升级和平滑退出都要用到这个 pid 文件。
包管理器安装的目录虽然分散,但结构逻辑一致,主配置里同样会有http、server、location这些层级块。理解一个,另一个自然就通了。
建议安装完成后做一次备份,把原始的nginx.conf复制一份存为nginx.conf.bak。别小看这个动作,在你把配置改得面目全非又不知道怎么恢复的时候,这个备份就是救命稻草。
4.2 配置第一台虚拟主机
安装并确认服务能跑之后,就可以配置第一台虚拟主机了。我在conf.d下创建一个站点配置文件,内容如下:
server { listen 80; server_name example.com www.example.com; root /var/www/example; index index.html index.htm; access_log /var/log/nginx/example.access.log main; error_log /var/log/nginx/example.error.log warn; location / { try_files $uri $uri/ =404; } }逐行解释一下。listen 80是监听端口,如果你的机器上已经有别的 Web 服务占用了 80,这里会冲突。server_name是域名匹配的关键,多个域名用空格分隔。root指定站点文件所在目录,实际访问时映射到/var/www/example下的对应文件。index是默认首页文件,按顺序匹配。location /匹配所有路径,try_files $uri $uri/ =404的意思是优先找真实文件,找不到就找目录,再找不到直接返回 404。
写完配置之后,一定要测试语法:
nginx -t nginx -s reloadnginx -t只做语法检查,不实际加载;reload是平滑重载,不会中断现有连接。每次改配置都要走这两步,我已经养成肌肉记忆了。如果直接改完就重启进程,万一配置有误会导致服务挂掉,在线用户全部断连。
4.3 worker 进程与基础性能参数设置
一个刚装好的 Nginx,默认配置是单 worker 进程跑在 nobody 用户下。对于真实业务来说,这个状态肯定不行。我在配置 HTTP 块里通常会调整这么几个参数。
user nginx; worker_processes auto; worker_rlimit_nofile 65535; events { worker_connections 1024; } http { sendfile on; tcp_nopush on; keepalive_timeout 65; gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; }user nginx必须和编译时--user=nginx对应,如果是包管理器安装,默认通常也是 nginx。worker_processes auto让 Nginx 自动根据 CPU 核心数创建 worker 进程。worker_rlimit_nofile是每个 worker 能打开的文件描述符上限,高并发下不够用会报too many open files错误。worker_connections 1024是单 worker 的最大连接数,一般够用。sendfile on开启高效文件传输,静态文件分发性能提升明显。gzip on开启压缩,配gzip_types指定哪些类型要压缩,能显著减小页面体积。
这些都是基础参数,真正到业务场景还得按需调。但把这些默认值调好,至少能保证 Nginx 在大多数场景下不会成为瓶颈。
5. 常见问题排查与运维技巧实录
5.1 启动失败,八成是端口被占用或权限问题
先排查端口。最经典的情况是端口被占用,报错信息类似bind() to 0.0.0.0:80 failed (98: Address already in use)。执行:
ss -lntp | grep :80 lsof -i:80找到占用进程,要么停掉旧服务,要么把 Nginx 的 listen 端口换掉。在云服务器上,还要确认安全组规则是否放行了对应端口,否则外部访问不了,但本机 curl 却正常,这种情况和安全组有关。
还有一个经常被忽略的问题:nginx 用户对站点目录没有读权限。比如你手动创建的网站目录归属 root,权限是 700,Nginx worker 进程跑在 nginx 用户下,就会报Permission denied。排查时可以看 error.log,里面会有具体的路径和权限提示。解决方法很简单,把站点目录的属主改成 nginx,或者加chmod -R 755。
5.2 nginx -t 通过但网站 404,多半是 root 路径写错了
这个坑我踩过不止一次。配置文件语法检查完全通过,reload 也成功,但访问网站就是 404。查日志发现在 Nginx 的日志里只记录了访问请求,没有后端报错。这时候九成是root或alias路径配置有问题。
一个是路径本身不存在,比如你写的是/var/www/example,但实际目录只建到/var/www/example/html,写成/var/www/example自然什么都找不到。另一个是 Nginx 默认用户对路径没有执行权限,能读文件但进入不了目录,也会表现为 404。排查方法很直接:
ls -ld /var/www/example sudo -u nginx ls /var/www/example后者模拟 nginx 用户去访问目录,如果输出权限不足,就调整目录权限和属主。
再补充一个点:如果你用了alias做目录映射,后面必须精确匹配。比如location /static/ { alias /data/static/; },访问/static/xxx时会映射到/data/static/xxx,如果 alias 后面少了结尾斜杠,路径拼接就会出错,出现一堆奇怪的 404。这类问题不是语法错,是语义错,所以nginx -t根本查不出来。
5.3 如何平滑升级 Nginx 版本而不中断服务
生产环境升级 Nginx,不能直接把进程杀掉再启动,最简单可靠的办法是利用 Nginx 的平滑升级机制。升级流程说白了就是让新版本二进制替换旧版本,通过信号量控制新旧进程切换,整个过程连接不断开。
大致步骤是这样的:
- 下载并编译新版本 Nginx,编译参数和旧版本保持一致,但注意 prefix 一定相同。
- 备份旧二进制:
mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old - 把编译好的新二进制复制到原位置。
- 对旧的 master 进程发
USR2信号,让它启动新的 master 进程:kill -USR2 $(cat /usr/local/nginx/logs/nginx.pid) - 此时新旧进程并行,旧 worker 还在处理请求。发
WINCH信号给旧 master,让它逐步关闭旧 worker。 - 确认新进程正常工作后,再发
QUIT信号让旧 master 退出:kill -QUIT $(cat /usr/local/nginx/logs/nginx.pid)
执行完流程之后,用ps -ef | grep nginx确认只剩新的 master 和 worker 进程。平滑升级的好处是用户无感知,不会出现在线掉线。我第一次做平滑升级的时候,内心还有点慌,但流程测过几次之后,再遇到版本迭代就很淡定了。
5.4 SSL 证书替换不生效,怎么排查
热词榜单里有“nginx 替换 ssl 证书不生效”,这个坑几乎每个人都会遇到。我遇到过四种典型情况:
第一种是证书链不完整。下载证书时如果只拿到一张证书文件,没有把中间证书也配上,Nginx 启动没问题,但浏览器会提示证书无效。解决办法是检查证书文件内容,把中间证书和域名证书按顺序拼接成一个证书链文件。Nginx 里ssl_certificate指向的文件应该包含完整链,不只是域名证书本身。
第二种是改错了 server 块。如果你的 Nginx 配置里多个 server 块都监听了 443,而且server_name匹配规则又比较模糊,比如存在default_server,那么新证书可能根本没用在你真正对外提供服务的那个 server 块上。这个一定要通过实际访问来验证,不要只看你觉得在改的那个文件。
第三种是 CDN 缓存。如果站点前面挂了 CDN,CDN 节点会缓存旧的证书链。这种情况下,源站 Nginx 已经换好了,但客户端访问时经过的 CDN 节点还在用旧证书。处理方式是先确认源站证书没问题,再在 CDN 控制台刷新证书缓存,然后就正常了。
第四种是浏览器缓存。替换证书后,浏览器端可能还保留着旧的证书信息。用无痕模式访问就能确认是不是这个原因。重点是一套有效的验证命令,避免全靠猜:
# 查看实际连接拿到的证书 openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null # 查看证书有效期开始和结束时间 openssl x509 -in /usr/local/nginx/conf/ssl/example.com.pem -noout -dates # 查看证书指纹,对比是否是最新版本 openssl x509 -in /usr/local/nginx/conf/ssl/example.com.pem -noout -fingerprint第一条命令输出的是浏览器实际建立 HTTPS 连接时收到的证书,如果这个证书还是旧的,说明问题出在 Nginx 或中间链路;如果这个证书已经是新的,问题就在客户端缓存。
记得有一次用户说证书不生效,我远程看了半天 Nginx 配置都没发现问题,最后发现他改完配置之后根本没有执行nginx -s reload,新证书一直没被加载。所以替换证书之后,第一步永远是nginx -t && nginx -s reload,然后才谈得上排查其他环节。
我个人在实际操作中的体会是,安装 Nginx 这件事,真正的难度不在make那一下,而在于你对这台服务器后面要怎么用的推演。很多时候,一个看似复杂的故障,最后查出来的原因只是路径写错、权限不足、改完配置没 reload 而已。所以我建议每次装完 Nginx,先不改任何业务配置,用默认页裸跑一遍,确认进程、端口、日志都正常,再做自定义配置。这样出了问题,你能清楚是哪一步引入的,排查路径会短得多。最后再分享一个小习惯:所有自定义配置都放到单独的conf.d子文件里,每个虚拟主机一个文件,命名按域名来。这样后续维护时,看到文件名就知道这个 Nginx 上跑了哪些站点,比全部堆在主配置文件里要清爽得多。