最近在整理一台 CentOS 7.9 服务器,准备把前端静态资源和一个 API 服务都放到 Nginx 后面统一代理。按理说“Linux CentOS 安装 Nginx”是个相当常见的操作,网上教程也多到看不过来,可真到自己动手时才发现,同样一条安装命令在不同系统、不同软件源、不同网络环境下,结果可能完全不一样。有人用 yum 装完就万事大吉,有人必须编译才能带上某个模块,还有人需要在离线环境里手工指定 rpm 包处理依赖。这篇文章就是围绕 CentOS 环境安装 Nginx 这件事,把环境准备、安装方式选型、配置文件和常见故障整个链路梳理一遍,穿插一些我实际踩过的坑。适合刚接手 Linux 服务器、需要快速部署 Nginx 的新手,也适合已经装过几次但想系统化补齐细节的运维同学。
1. 安装 Nginx 前的三个准备工作
1.1 先确认系统的发行版和内核状态
很多人拿到服务器就开始跑安装命令,这是第一个坑。CentOS 7、CentOS Stream 8、CentOS Stream 9 的软件仓库和默认行为都有差异,至少要先搞清楚三件事:系统版本是哪个、内核是多少、当前用户有没有 root 权限。
建议先执行这三条命令确认底细:
bash复制cat /etc/redhat-release
uname -r
whoami
如果当前用户不是 root,后续需要用到 sudo。CentOS 7 上常见输出是 CentOS Linux release 7.9.2009 (Core),内核一般是 3.10.0-xxx.el7.x86_64。CentOS Stream 9 的内核则已经到了 5.14 以上。内核版本主要影响一些模块编译和系统调用层面的兼容性,比如新版 Nginx 对 HTTP/3 的支持依赖更高版本的系统库,老内核上就算硬编出来也未必稳定跑得起来。
安装前的系统更新不是必须的,但我一般会顺手执行一次 yum update -y,尤其新上手的服务器,把基础软件包和 openssl 库先更新到仓库内最新版本,能少踩很多 HTTPS 证书相关报错。更新时间看机器网络状况,通常几分钟到十几分钟不等。
1.2 判断该选哪种安装方式
Nginx 在 CentOS 上的安装方式大致有三类:yum/rpm 包管理安装、源码编译安装、容器化部署。这里暂时不讨论容器,因为很多人要管理的还是传统物理机或虚拟机。
选型主要看以下几个问题:
- 是否需要 Nginx 官方最新稳定版,还是系统仓库自带版本就够用;
- 是否需要
--with-stream、--with-http_v2_module等特殊模块; - 服务器能否访问外网软件源,还是完全离线;
- 后续是否需要频繁升级、回滚,以及团队是否统一维护包管理方式。
我的经验是:能用 yum 或官方 rpm 解决的问题,不要一上来就编译。包管理方式安装简单、卸载干净、有 systemd 管理脚本,后续排查问题省心得多。源码编译更适合这些情况:需要把 Nginx 装到自定义路径、需要官方默认包没编译的模块、需要精确控制编译参数或做多版本并存。离线环境则优先考虑提前下载 rpm 包,而不是离线编译,因为编译过程还要处理一大堆开发依赖,更痛苦。
1.3 网络与软件源检查
确认能不能访问外网,直接决定安装路线:
bash复制curl -I https://nginx.org
能正常返回 HTTP 响应头说明外网通畅。如果用的是内网 yum 源,可以先看下当前有哪些可用仓库:
bash复制yum repolist
CentOS 7 默认源里其实没有 Nginx 包,直接 yum install nginx 大概率会提示没有可用软件包。这时要么启用 EPEL 仓库,要么配置 Nginx 官方仓库,要么走源码编译。很多人第一次装 Nginx 失败,就卡在这个“找不到软件包”的问题上。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 用包管理器安装:最快也最稳妥的常规路线
2.1 先认识 CentOS 软件源里的 Nginx 差异
CentOS 7 的 EPEL 仓库提供 Nginx 1.20 左右版本,虽然不是最新,但胜在稳定,补丁跟随仓库维护,对大多数 Web 场景完全够用。CentOS Stream 9 的 AppStream 仓库自带 Nginx 版本会更接近 1.20 甚至 1.22,可以通过 dnf module list nginx 查看可用的模块流。
如果你需要更新版本,或者希望直接用 Nginx 官方编译好的二进制包,那就配置 nginx.org 官方仓库。官方仓库的好处是版本新、发布节奏跟随 Nginx 官方、二进制包针对主流 CentOS 版本做了适配。缺点是需要自己额外维护一套仓库源。
从运维维护角度看,EPEL 和官方仓库都属于“包管理安装”,安装后的目录结构基本一致:主配置在 /etc/nginx/nginx.conf,站点配置默认目录是 /etc/nginx/conf.d/,日志在 /var/log/nginx/。这一点很关键,因为源码编译安装的目录结构完全不同,很容易让人改错文件。
2.2 使用 EPEL 仓库安装
EPEL 是 Fedora 社区维护的 Extra Packages for Enterprise Linux 仓库,安装方式很简单:
bash复制yum install -y epel-release
yum install -y nginx
安装完成后验证版本:
bash复制nginx -v
然后启动并设置开机自启:
bash复制systemctl start nginx
systemctl enable nginx
systemctl status nginx
能正常看到 active (running) 就说明装好了。此时在浏览器访问服务器 IP,应该能看到 Nginx 默认欢迎页。如果访问不了,不要急着怀疑安装有问题,先看防火墙和 SELinux,这两个坑在后面专门讲。
EPEL 装的 Nginx 默认站点根目录在 /usr/share/nginx/html,Nginx 运行用户是 nginx,这些信息后续配置权限时会反复用上。
2.3 使用 Nginx 官方仓库安装
想用比较新的 Nginx 版本,就添加官方 yum 源。在 /etc/yum.repos.d/ 下新建一个 nginx.repo:
bash复制vim /etc/yum.repos.d/nginx.repo
写入以下内容:
ini复制[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=true
$releasever 和 $basearch 会自动替换成系统版本和 CPU 架构,比如 CentOS 7 会解析成 http://nginx.org/packages/centos/7/x86_64/。保存后执行:
bash复制yum clean all
yum makecache
yum install -y nginx
这样安装出来的 Nginx 版本通常比 EPEL 要新。这里要注意,官方仓库的二进制包和系统自带软件源偶尔会有依赖冲突,如果你之前启用过 EPEL,可以指定只从官方仓库安装:
bash复制yum install -y nginx --enablerepo=nginx-stable
官方仓库安装后的管理方式和 EPEL 一致,配置文件路径也都在 /etc/nginx/ 下。
2.4 离线环境下用 RPM 包安装
有些内网服务器连不上外网,又必须装 Nginx,这时最稳妥的方案是提前在一台可以联网的同版本 CentOS 机器上下载好 rpm 包,再拷贝进去安装。
先在有网络的机器上安装 yum-utils:
bash复制yum install -y yum-utils
然后用 yumdownloader 把 Nginx 及依赖全部下载到指定目录:
bash复制yumdownloader --resolve --destdir=/tmp/nginx_rpms nginx
把 /tmp/nginx_rpms 整个目录拷贝到离线服务器,执行本地安装:
bash复制yum localinstall -y /tmp/nginx_rpms/*.rpm
如果离线服务器本身没有配置任何可用 yum 源,yum localinstall 依然会尝试解析依赖,所以建议保留下载的完整 rpm 目录。也可以直接到 Nginx 官网对应版本的 rpm 下载目录手工下载单个包,但那样要自己处理依赖,没有 --resolve 省心。
离线环境的另一个选择是打包整个已经装好的 Nginx 目录直接拷贝,但涉及用户、日志目录、systemd 脚本等一堆细节,太容易出差错,建议只在应急时使用。
3. 源码编译安装:把功能模块一次装到位
3.1 为什么需要源码编译
包管理安装省事,但有个短板:官方 rpm 和 EPEL 包里的编译选项是固定的。比如你想用 --with-stream 来做 TCP 四层转发,或者想启用 HTTP/2,却发现当前安装的 Nginx 二进制不支持,就得自己重新编译。
源码编译本质上是在你机器上从 C 源码生成可执行文件,所有模块开关都在 ./configure 阶段确认。编译安装可以自定义安装前缀、运行用户、启用模块,缺点是需要自己处理依赖、自己配 systemd 管理脚本、升级维护也相对麻烦。
我自己的判断标准是:只要是生产环境需要特殊模块,我会直接用源码编译一套独立的 Nginx,安装前缀放到 /usr/local/nginx-<版本号> 这样独立的目录,方便后续多版本并存。如果只是常规 Web 服务,一律 yum 安装即可。
3.2 编译依赖和完整步骤
源码编译要装开发工具和 Nginx 依赖的库头文件。在 CentOS 上执行:
bash复制yum install -y gcc gcc-c++ make pcre-devel zlib-devel openssl-devel
各依赖的作用可以这样理解:gcc 用来编译 C 源码,make 负责执行编译流程,pcre-devel 是 Nginx 正则支持所需,zlib-devel 是 gzip 压缩支持所需,openssl-devel 是 HTTPS 支持所需。这几个包缺失时,./configure 阶段会直接报错,比如缺少 pcre 会提示 the HTTP rewrite module requires the PCRE library。
建议单独创建一个 Nginx 运行用户:
bash复制useradd -r -s /sbin/nologin nginx
然后下载源码包。以 1.26.2 版本为例:
bash复制wget https://nginx.org/download/nginx-1.26.2.tar.gz
tar -zxvf nginx-1.26.2.tar.gz
cd nginx-1.26.2
配置编译参数:
bash复制./configure \
--prefix=/usr/local/nginx-1.26.2 \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-http_gzip_static_module \
--with-stream \
--with-stream_ssl_module
看到配置过程正常结束后,开始编译和安装:
bash复制make -j$(nproc)
make install
-j$(nproc) 表示用 CPU 全部核心并行编译,能明显缩短时间。编译完成后可以给 nginx 命令做个软链,方便全局调用:
bash复制ln -s /usr/local/nginx-1.26.2/sbin/nginx /usr/sbin/nginx
之后用 nginx -V 就能看到本次编译的详细参数。
3.3 关键编译参数解读
刚开始接触源码编译的人,看到一长串 --with 参数容易懵。下面这几个是我最常用的,它们的实际作用如下:
| 编译参数 | 作用 |
|---|---|
--prefix |
指定安装根目录,推荐带版本号,方便以后升级切换 |
--user / --group |
指定 worker 进程运行身份,不用 root 跑服务更安全 |
--with-http_ssl_module |
启用 HTTPS 支持,没有它无法配置 listen 443 ssl |
--with-http_v2_module |
启用 HTTP/2 协议支持 |
--with-http_stub_status_module |
提供 /nginx_status 页面查看连接状态 |
--with-http_realip_module |
反代场景下获取客户端真实 IP |
--with-stream |
启用 TCP/UDP 四层转发,常用于数据库或 Redis 代理 |
--with-stream_ssl_module |
让四层转发支持 TLS 加密 |
有一个容易被忽略的细节:编译参数一旦定下来,后期新增模块必须重新执行整套编译安装流程。所以源码安装前最好把可能用到的模块都先想清楚,别等到要配 HTTPS 才发现当初没编 ssl_module,只能再折腾一遍。
如果想确认线上已运行的 Nginx 当初用了哪些参数,直接执行:
bash复制nginx -V
输出里 configure arguments: 后面就是全部编译参数。
4. 配置文件拆解与第一份可用配置
4.1 默认配置结构拆解
yum 和 rpm 方式安装的 Nginx,主配置文件在 /etc/nginx/nginx.conf;源码编译安装的默认配置在 --prefix 目录下的 conf/nginx.conf,比如 /usr/local/nginx-1.26.2/conf/nginx.conf。这是最容易弄混的地方,我见过有人编译安装后到处找 /etc/nginx/nginx.conf,找到之后改了又不起作用。
默认 nginx.conf 是分层的,核心结构这样理解就够了:
nginx复制user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
}
user nginx 表示 worker 进程以 nginx 用户身份运行,避免用 root 直接处理请求。worker_processes 建议改成 auto,让 Nginx 根据 CPU 核心数自动设置 worker 进程数量。events 块里的 worker_connections 决定了每个 worker 能同时保持的最大连接数,默认 1024 对一般小站点够用,高并发场景再调大。http 块是最主要的配置区域,server 相当于一个虚拟主机,location 则做 URI 路径匹配。
4.2 静态站点与反向代理配置示例
包管理安装的 Nginx 默认在 /etc/nginx/conf.d/ 目录下加载所有 .conf 文件,主配置里通常有这一行 include /etc/nginx/conf.d/*.conf;。因此不用去改主配置,直接在 conf.d 下新建站点配置即可。
一个同时承担静态站点和反向代理的典型配置如下:
nginx复制server {
listen 80;
server_name example.com;
root /data/www;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
access_log /var/log/nginx/example_access.log;
error_log /var/log/nginx/example_error.log;
}
这里把静态文件放在 /data/www,前端页面直接由 Nginx 处理,访问 /api/ 开头的请求则转发给本机 8080 端口的后端服务。
使用反向代理时最容易踩的一个坑是 proxy_pass 后面到底带不带斜杠。简单记两条规则:如果 location 是 /api/,而 proxy_pass http://127.0.0.1:8080; 结尾不带斜杠,请求 /api/user 会原样转发到 http://127.0.0.1:8080/api/user;如果写成 proxy_pass http://127.0.0.1:8080/;,请求 /api/user 会去掉 /api/ 前缀,转发到 http://127.0.0.1:8080/user。到底用哪种,取决于后端接口是否自带 /api 前缀,这一点务必先确认后端路由规则。
4.3 校验配置与平滑重载
改完配置别急着重启服务,先做语法校验:
bash复制nginx -t
正常会看到 syntax is ok 和 test is successful。如果提示第几行有问题,根据报错信息检查对应文件,大多数是漏了分号、少了大括号、或者文件路径不存在。
配置校验通过后,用 reload 方式让配置生效,而不是粗暴 restart:
bash复制systemctl reload nginx
reload 会平滑重启 worker 进程,已建立的连接不会中断,对在线业务更友好。如果 Nginx 是源码编译安装的,可以用:
bash复制/usr/local/nginx-1.26.2/sbin/nginx -s reload
reload 前建议把当前配置备份一份,养成好习惯:
bash复制cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date +%F)
修改配置出错时,能快速回退到上一个可用版本。
5. CentOS 下安装后最容易踩的 4 个坑
5.1 防火墙拦截外部访问
CentOS 7 以上默认启用 firewalld,即使 Nginx 已经正常监听 80 端口,外部浏览器也可能访问不了。遇到这种情况先检查监听状态:
bash复制ss -lntp | grep :80
如果 Nginx 正常监听,基本可以断定是防火墙拦截。放行 HTTP 服务:
bash复制firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
如果用的是自定义端口,比如 8080,可以这样放行:
bash复制firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --reload
排查防火墙时最好不要为了省事直接 systemctl stop firewalld,生产环境这样做风险很大,也不符合服务器安全基线要求。
5.2 SELinux 拦截反向代理连接
CentOS 默认开启了 SELinux,状态是 Enforcing。SELinux 对 Nginx 的限制非常细,一个常见情况是:后端服务明明在运行、端口也通,但 Nginx 反代就是 502,日志里出现 Permission denied。这往往不是文件权限问题,而是 SELinux 阻止了 Nginx 作为 httpd 服务去访问网络。
可以查看 SELinux 拦截记录:
bash复制ausearch -m avc -ts recent
如果确认是 Nginx 访问网络被拦,打开对应布尔值即可:
bash复制setsebool -P httpd_can_network_connect 1
-P 表示持久化,重启后依然生效。如果只是提供静态文件服务,但访问目录返回 403,可能是目录的 SELinux 上下文不对,可以临时设置目录类型:
bash复制chcon -R -t httpd_sys_content_t /data/www
不要一遇到 SELinux 就 setenforce 0 全局关闭,那是把保护机制整个废掉了,遇到问题应该先定位是哪条策略拦截,然后用最小授权方式解决。
5.3 源码安装后没有 systemd 启动脚本
用 yum 安装 Nginx,装完自带 systemd 服务文件,直接 systemctl start nginx 就行。但源码编译安装不会自动生成 systemd 脚本,如果你仍然执行 systemctl start nginx,会提示服务不存在或找不到命令。
给源码编译的 Nginx 补一个 systemd 服务文件很方便。在 /etc/systemd/system/nginx.service 里写入:
ini复制[Unit]
Description=nginx web server
After=network.target
[Service]
Type=forking
PIDFile=/usr/local/nginx-1.26.2/logs/nginx.pid
ExecStartPre=/usr/local/nginx-1.26.2/sbin/nginx -t
ExecStart=/usr/local/nginx-1.26.2/sbin/nginx
ExecReload=/usr/local/nginx-1.26.2/sbin/nginx -s reload
ExecStop=/usr/local/nginx-1.26.2/sbin/nginx -s quit
PrivateTmp=true
[Install]
WantedBy=multi-user.target
保存后执行:
bash复制systemctl daemon-reload
systemctl enable --now nginx
这里 Type=forking 很关键,因为 Nginx 启动时 master 进程会 fork 出子进程,主进程切换成后台守护模式,systemd 需要根据 PIDFile 来跟踪主进程状态。
5.4 日志目录权限不足导致启动失败
Nginx 进程以非 root 用户运行时,如果日志目录不存在或者属主不对,启动时会在日志文件上报错。yum 安装的 Nginx 默认日志目录是 /var/log/nginx,安装包通常会创建好并设置属主为 nginx。但如果你在配置里把 access_log 改到了自定义目录,比如 /data/logs/nginx/access.log,而这个目录不存在,Nginx 启动可能直接失败,或者在运行一段时间后频繁打印权限错误。
遇到这种情况先确认目录存在及属主:
bash复制ls -ld /data/logs/nginx
然后创建目录并调整属主:
bash复制mkdir -p /data/logs/nginx
chown -R nginx:nginx /data/logs/nginx
源码编译的 Nginx 默认会把自己的日志写到安装目录下的 logs/ 里,例如 /usr/local/nginx-1.26.2/logs/error.log,相对省心。如果手工修改了日志路径,就要记得同步处理目录权限。
6. 启动失败与访问异常的排查速查
6.1 常见错误一览
下面这些报错是我在过去安装和运维过程中最常遇到的,整理成一张速查表,遇到问题可以直接对照:
| 报错或现象 | 可能原因 | 处理思路 |
|---|---|---|
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) |
80 端口被其他进程占用 | 用 ss -lntp | grep :80 查看占用进程,停掉冲突服务或改 Nginx 监听端口 |
connect() failed (111: Connection refused) while connecting to upstream |
后端服务没启动或端口不对 | 检查后端进程、端口监听、proxy_pass 地址 |
13: Permission denied 或 403 Forbidden |
SELinux 拦截或目录权限不足 | 检查 getenforce,查看目录属主与 SELinux 上下文 |
nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges |
用非 root 用户启动 Nginx | 改为 root 启动 master,worker 会按配置降权运行;或忽略该警告 |
open() "/var/log/nginx/access.log" failed (13: Permission denied) |
日志目录不可写 | 调整日志目录属主或权限 |
| 配置了反代但一直 404 | proxy_pass 是否带斜杠和后端路由不匹配 |
检查 URI 前缀转发规则,参考上文斜杠说明 |
6.2 一个典型的 502 排查过程
举一个实际例子。某次配置完 Nginx 反向代理到本机 8080 端口的 Java 服务,外部访问一直 502。我先在服务器本机用 curl 验证后端:
bash复制curl -v http://127.0.0.1:8080/health
结果后端本身正常返回 JSON。再检查 Nginx 错误日志,看到 connect() failed (111: Connection refused) while connecting to upstream,说明 Nginx 连接 8080 失败。但 curl 本地能通,为什么 Nginx 不通?这时候想到了 SELinux,执行:
bash复制ausearch -m avc -ts recent
果然有一堆 Nginx 访问网络被拒绝的记录。执行 setsebool -P httpd_can_network_connect 1 后,再访问接口就恢复了。整个排查过程十分钟不到,核心经验是:Nginx 访问本地后端失败时,如果 curl 能通而 Nginx 不通,优先怀疑 SELinux,而不是反复检查后端服务。
还有一种类似场景是修改了防火墙之后仍然不通,这种时候先分清方向:外部访问不了先查防火墙和云安全组,Nginx 访问不了上游先查 SELinux 和上游监听地址。方向错了,排查时间会成倍增加。
最后分享一个小习惯。每次安装或升级 Nginx,我都会把当前的编译参数保存到一个文件里,比如 /etc/nginx/build_args.txt,写清楚版本号和 configure 参数。后面换机器、补模块、升级版本时,直接翻出这个文件按原参数重新编译,能少走很多弯路。如果只是初学 CentOS 安装 Nginx,先别急着复制一堆命令,按环境确认、安装方式选型、配置验证的顺序走一遍,出问题的概率会小很多。
