Rocky Linux 8 下 Nginx 安装与生产级配置全指南
1. 项目概述:为什么在 Rocky Linux 8 上装 Nginx 不是“照着命令敲一遍”就完事?
Nginx、Rocky Linux 8、dnf、systemctl、firewall-cmd——这五个词凑在一起,不是一道考题,而是一张真实生产环境的入场券。我第一次在客户现场部署时,就是被“sudo systemctl start nginx启动失败”卡了整整三小时。日志里只有一行Failed to start nginx.service: Unit not found.,翻遍所有教程都写着“安装完就能用”,没人告诉你 Rocky Linux 8 默认压根没启用 EPEL 仓库,而官方 baseos AppStream 里提供的 nginx 版本是 1.14.1,连stream模块都不带,更别说反向代理 WebSocket 或 gRPC 这些现代后端刚需功能了。这不是版本新旧的问题,是能力缺失——就像给你一把没开刃的刀,说明书还说“切菜如飞”。
很多人搜“nginx安装”“dnf安装nginx”,以为只是执行dnf install nginx就能收工。但实际操作中,你得先判断:这是要跑静态前端?做 API 网关?还是扛住每秒 5000+ 请求的高并发入口?不同目标,安装路径天差地别。比如用 dnf 装的包管理版,配置文件在/etc/nginx/,日志默认不记录真实客户端 IP(因为缺real_ip_header和set_real_ip_from配置);而自己编译的版本,路径全由你定,但得手动处理 OpenSSL、zlib、PCRE 的依赖链,稍有不慎就编译报错undefined reference to 'SSL_CTX_set_alpn_select_cb'。更隐蔽的是 firewall-cmd——Rocky Linux 8 默认用 firewalld,但firewall-cmd --permanent --add-service=http这条命令,只放行了 80 端口,如果你配了 8080 做健康检查,或者开了 443 做 HTTPS,它照样拦你,而且不会报错,只会让你的 curl 请求永远卡在Connection timed out。
还有个坑是 systemctl 的“假成功”。systemctl status nginx显示 active (running),但访问页面却是 502 Bad Gateway。查进程发现 nginx master 进程在,worker 却全挂了——因为你在/etc/nginx/conf.d/default.conf里写了proxy_pass http://127.0.0.1:3000;,而 Node.js 服务根本没起来。systemctl 只管 master 进程是否存活,不管 downstream 是否可用。这种“表面正常、实际瘫痪”的状态,在灰度发布时最致命。所以这篇不是教你怎么打命令,而是带你把 Rocky Linux 8 当成一台需要亲手调校的精密仪器:从仓库源怎么选、dnf 的 transaction check 怎么看、systemctl 的 unit 文件哪几行不能动、firewall-cmd 的 zone 机制怎么匹配网卡、到 nginx 启动时的 7 个关键检查点,全部拆开揉碎讲透。适合两类人:一是刚从 Ubuntu 转来、还在用apt思维想问题的运维新手;二是已经会装但总在上线后出诡异问题的中级工程师。你不需要背命令,只需要知道每个动作背后,系统到底在做什么。
2. 安装前的核心准备与环境诊断
2.1 确认 Rocky Linux 8 的真实版本与内核状态
Rocky Linux 8 有多个次要版本(8.6、8.8、8.10),它们的软件包基线差异极大。比如 8.6 的 dnf 默认使用 Python 3.6,而 8.10 已升级到 Python 3.9,某些自定义 repo 的元数据格式不兼容,会导致dnf makecache报错Failed to synchronize cache for repo 'epel'。所以第一步不是装 nginx,而是摸清底牌:
# 查看完整发行版信息(注意第二行的 Platform ID) $ cat /etc/os-release NAME="Rocky Linux" VERSION="8.10 (Green Obsidian)" ID="rocky" ID_LIKE="rhel centos fedora" PLATFORM_ID="platform:el8" PRETTY_NAME="Rocky Linux 8.10 (Green Obsidian)" ANSI_COLOR="0;32" CPE_NAME="cpe:/o:rocky:rocky:8::baseos" HOME_URL="https://rockylinux.org/" BUG_REPORT_URL="https://bugs.rockylinux.org/" ROCKY_SUPPORT_URL="https://rockylinux.org/support" ROCKY_VERSION_ID="8.10" ROCKY_PLATFORM_ID="platform:el8" # 查看内核版本(关键!影响 eBPF、io_uring 等高级特性支持) $ uname -r 4.18.0-553.16.1.el8_10.x86_64 # 检查 SELinux 状态(Rocky 8 默认 enforcing,但 nginx 相关端口可能被拦截) $ sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: enforcing Mode from config file: enforcing Policy MLS status: enabled Policy deny_unknown status: allowed Max kernel policy version: 33提示:如果
Current mode是enforcing,后续配置 nginx 监听非标准端口(如 8080)时,必须执行semanage port -a -t http_port_t -p tcp 8080,否则即使 firewall-cmd 放行了,SELinux 也会静默拒绝连接。这个细节 90% 的教程都跳过,导致你反复检查防火墙却找不到原因。
2.2 仓库源诊断:EPEL 是必须项,但不是唯一选择
Rocky Linux 8 的 AppStream 仓库里 nginx 版本固定为 1.14.1(截至 2024 年中),它缺少http_v2_module(HTTP/2)、http_geoip2_module(IP 归属分析)、http_perl_module(嵌入 Perl 脚本)等关键模块。而 EPEL(Extra Packages for Enterprise Linux)仓库提供的是 1.20.1,已支持 HTTP/2 和动态模块加载。但 EPEL 不是默认启用的,必须手动开启:
# 检查当前启用的仓库(重点关注 epel 和 epel-modular) $ dnf repolist --enabled | grep -E "(epel|appstream)" appstream Rocky Linux 8 - AppStream baseos Rocky Linux 8 - BaseOS epel Extra Packages for Enterprise Linux 8 - x86_64 epel-modular Extra Packages for Enterprise Linux 8 - Modular - x86_64 # 如果没有 epel,执行安装(注意:rockylinux-release 包必须先更新) $ sudo dnf update -y rockylinux-release $ sudo dnf install -y epel-release但这里有个隐藏陷阱:EPEL 8 的nginx包名是nginx,而 EPEL 9 的包名是nginx-all-modules。如果你误用了 EPEL 9 的 repo 配置,dnf install nginx会报错No match for argument: nginx。验证方法很简单:
# 查看 epel 仓库中 nginx 的真实包名和版本 $ dnf --disablerepo="*" --enablerepo="epel" list available nginx Last metadata expiration check: 0:02:15 ago on Thu 25 Apr 2024 03:14:22 PM CST. Available Packages nginx.x86_64 1:1.20.1-14.el8 epel看到1:1.20.1-14.el8就对了。如果显示nginx-all-modules或版本号带el9,说明仓库源配错了,必须删掉/etc/yum.repos.d/epel.repo里错误的[epel]段,重新安装epel-release。
2.3 系统资源预检:内存、磁盘、端口占用
Nginx 启动失败的第二大原因是资源冲突。Rocky Linux 8 默认安装的httpd(Apache)服务常与 nginx 争抢 80 端口,而dnf install nginx不会自动停掉 httpd。必须手动清理:
# 检查 80 端口谁在用(-t 表示 TCP,-n 表示数字端口,-p 表示显示进程) $ sudo ss -tlnp | grep ':80' LISTEN 0 128 *:80 *:* users:(("httpd",pid=1234,fd=4),("httpd",pid=1233,fd=4)) # 如果是 httpd,永久禁用(注意:不是 stop,是 disable) $ sudo systemctl disable httpd --now Removed /etc/systemd/system/multi-user.target.wants/httpd.service. # 检查磁盘空间(nginx 日志和缓存会持续增长) $ df -h /var/log Filesystem Size Used Avail Use% Mounted on /dev/mapper/rhel-root 50G 32G 19G 64% / # 检查可用内存(worker_processes 设为 auto 时,每个 worker 默认占 10MB+) $ free -h total used free shared buff/cache available Mem: 15G 2.1G 8.2G 128M 4.9G 12G Swap: 2G 0B 2G注意:
free -h中的available列才是真实可用内存。如果它低于 1G,建议在/etc/nginx/nginx.conf中将worker_processes显式设为1,避免 fork 大量 worker 导致 OOM Killer 杀掉进程。这个参数不写,默认是auto(即 CPU 核心数),在 16 核服务器上会起 16 个 worker,每个吃 10MB,光进程就占 160MB,还没算缓存。
2.4 用户与权限规划:别让 nginx 以 root 身份读你的私钥
Rocky Linux 8 的 nginx 包默认以nginx用户运行,但它的主配置/etc/nginx/nginx.conf里user指令被注释掉了:
# /etc/nginx/nginx.conf # user nginx; worker_processes auto; ...这意味着 nginx master 进程以 root 启动(必须的,为了绑定 80/443 端口),但 worker 进程会降权为nginx用户。问题来了:如果你把 SSL 私钥放在/root/myapp.key,worker 进程根本读不了,启动时会报错open() "/root/myapp.key" failed (13: Permission denied)。正确做法是:
- 将私钥移到
/etc/nginx/ssl/(该目录默认属主为root:nginx,权限750) - 执行
sudo chown root:nginx /etc/nginx/ssl/myapp.key - 执行
sudo chmod 640 /etc/nginx/ssl/myapp.key - 在 server 块中引用:
ssl_certificate_key /etc/nginx/ssl/myapp.key;
这个权限链必须闭环:nginx用户属于nginx组 →nginx组对/etc/nginx/ssl/有读权限 →nginx组对.key文件有读权限。漏掉任何一环,HTTPS 就起不来。我见过太多人把私钥放/home/deploy/ssl/,结果折腾半天才发现 SELinux 的httpd_can_network_connect布尔值没开,或者目录的user_home_t上下文不匹配。
3. 三种安装路径深度对比与实操步骤
3.1 方案一:dnf 安装(推荐给 95% 的生产场景)
dnf 安装不是最“酷”的方案,但它是 Rocky Linux 8 生态里最稳的。它把 nginx 编译、依赖、服务注册、日志轮转全部打包进 RPM,你拿到的是一个经过 Red Hat QA 测试的二进制。核心优势在于可审计、可回滚、与系统更新协同。比如dnf update时,nginx 会自动升级到 EPEL 提供的最新安全补丁版,无需手动下载源码编译。
完整实操步骤(含所有避坑点):
# 步骤 1:更新系统并启用 EPEL(前面已讲,此处强调顺序) $ sudo dnf update -y rockylinux-release # 必须先更新 release 包 $ sudo dnf install -y epel-release # 步骤 2:安装 nginx(注意:加 --enablerepo=epel 强制指定仓库) $ sudo dnf install -y --enablerepo=epel nginx # 步骤 3:验证安装(检查二进制路径、版本、模块) $ which nginx /usr/sbin/nginx $ nginx -v nginx version: nginx/1.20.1 $ nginx -V 2>&1 | grep -o "with-http_v2_module" with-http_v2_module # 确认 HTTP/2 支持 # 步骤 4:初始化配置(Rocky 8 的 nginx 默认不启动,需手动 enable) $ sudo systemctl enable nginx --now # --now = enable + start Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service. # 步骤 5:检查状态(重点看 Active line 和 Loaded line) $ sudo systemctl status nginx ● nginx.service - The nginx HTTP and reverse proxy server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled) Active: active (running) since Thu 2024-04-25 15:22:33 CST; 1min 23s ago Docs: man:nginx(8) Process: 1234 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS) Process: 1235 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS) Main PID: 1236 (nginx) Tasks: 5 (limit: 23590) Memory: 12.3M CGroup: /system.slice/nginx.service ├─1236 nginx: master process /usr/sbin/nginx └─1237 nginx: worker process关键观察点:
Loaded行显示enabled,证明开机自启已生效;Active行是active (running),且Main PID下有master和worker进程。如果只有 master 没有 worker,说明配置语法错误,ExecStartPre的nginx -t检查失败了,但 systemd 仍会标记为 running(这是个设计缺陷)。此时必须看journalctl -u nginx --since "1 hour ago"查具体错误。
dnf 安装后的目录结构解析:
| 路径 | 用途 | 所有权 | 关键说明 |
|---|---|---|---|
/usr/sbin/nginx | 主二进制文件 | root:root | 不可写,升级时被覆盖 |
/etc/nginx/ | 全局配置目录 | root:root | nginx.conf是入口,conf.d/存虚拟主机 |
/etc/nginx/nginx.conf | 主配置文件 | root:root | include /etc/nginx/conf.d/*.conf;是关键 |
/var/log/nginx/ | 日志目录 | nginx:nginx | access.log和error.log默认在此 |
/usr/share/nginx/html/ | 默认网页根目录 | root:root | index.html在此,权限 644 |
这个结构意味着:你修改配置必须用sudo,日志轮转由logrotate(配置在/etc/logrotate.d/nginx)自动处理,网页文件默认不可写——安全,但不够灵活。如果你需要让 CI/CD 自动部署前端到/usr/share/nginx/html/,必须提前sudo chown deploy:nginx /usr/share/nginx/html/并sudo chmod 775 /usr/share/nginx/html/,否则部署脚本会因权限拒绝失败。
3.2 方案二:源码编译安装(适合需要定制模块或最新版的场景)
当你需要nginx-rtmp-module(直播流)或nginx-http-shibboleth-module(单点登录集成),或者必须用上 1.25.3 的quic实验性支持时,dnf 就无能为力了。源码编译给了你完全控制权,但代价是维护成本陡增。
编译前的依赖准备(Rocky 8 特有):
Rocky Linux 8 的开发工具链叫Development Tools,但它不包含 PCRE(正则库)和 OpenSSL 开发头文件。必须单独装:
# 安装基础编译工具 $ sudo dnf groupinstall -y "Development Tools" # 安装 nginx 编译必需的开发库(注意:openssl-devel 不是 openssl) $ sudo dnf install -y pcre-devel openssl-devel zlib-devel # 验证头文件是否存在(编译时 configure 会检查) $ ls /usr/include/openssl/ssl.h /usr/include/pcre.h /usr/include/zlib.h /usr/include/openssl/ssl.h /usr/include/pcre.h /usr/include/zlib.h下载与编译全流程(以 nginx 1.25.3 为例):
# 创建工作目录并下载(官网地址:https://nginx.org/download/) $ mkdir ~/nginx-build && cd ~/nginx-build $ wget https://nginx.org/download/nginx-1.25.3.tar.gz $ tar -zxvf nginx-1.25.3.tar.gz $ cd nginx-1.25.3 # 执行 configure(关键参数解释见下表) $ ./configure \ --prefix=/opt/nginx-1.25.3 \ --sbin-path=/opt/nginx-1.25.3/sbin/nginx \ --conf-path=/opt/nginx-1.25.3/conf/nginx.conf \ --error-log-path=/opt/nginx-1.25.3/logs/error.log \ --http-log-path=/opt/nginx-1.25.3/logs/access.log \ --pid-path=/opt/nginx-1.25.3/run/nginx.pid \ --lock-path=/opt/nginx-1.25.3/run/nginx.lock \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_realip_module \ --with-http_stub_status_module \ --with-pcre \ --with-zlib \ --with-openssl=/usr/src/openssl-1.1.1w # 如果要用自定义 OpenSSL,需指定路径 # 编译(-j$(nproc) 用满所有 CPU 核心) $ make -j$(nproc) # 安装(不加 sudo 会报权限错) $ sudo make installconfigure 参数深度解读:
| 参数 | 作用 | Rocky 8 注意事项 |
|---|---|---|
--prefix | 指定安装根目录 | 强烈建议不用/usr/local,因为 Rocky 8 的dnf会忽略该目录,导致未来冲突。用/opt/nginx-x.x.x更清晰 |
--sbin-path | nginx 二进制路径 | 必须与--prefix一致,否则nginx -t会找不到配置文件 |
--conf-path | 主配置文件路径 | 如果不指定,configure 会默认用/usr/local/nginx/conf/nginx.conf,容易和 dnf 版本混淆 |
--with-http_ssl_module | 启用 HTTPS | Rocky 8 的openssl-devel必须已安装,否则 configure 报错OpenSSL library is not found |
--with-http_v2_module | 启用 HTTP/2 | 需要 OpenSSL 1.0.2+,Rocky 8.10 自带 1.1.1w,满足要求 |
--with-http_realip_module | 获取真实客户端 IP | 对接 CDN 或负载均衡必备,否则$remote_addr总是 127.0.0.1 |
编译完成后,你得到的是一个完全独立的 nginx 实例。它不与系统服务集成,需要自己写 systemd unit 文件:
# 创建 /etc/systemd/system/nginx-custom.service $ sudo tee /etc/systemd/system/nginx-custom.service << 'EOF' [Unit] Description=Custom nginx 1.25.3 After=network.target [Service] Type=forking PIDFile=/opt/nginx-1.25.3/run/nginx.pid ExecStartPre=/opt/nginx-1.25.3/sbin/nginx -t -c /opt/nginx-1.25.3/conf/nginx.conf ExecStart=/opt/nginx-1.25.3/sbin/nginx -c /opt/nginx-1.25.3/conf/nginx.conf ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s TERM $MAINPID Restart=on-failure [Install] WantedBy=multi-user.target EOF # 启用并启动 $ sudo systemctl daemon-reload $ sudo systemctl enable nginx-custom --now这个 unit 文件的关键是Type=forking(因为 nginx master 会 fork worker),以及ExecStartPre的配置检查——比 dnf 版本更严格,确保每次 reload 都先验语法。
3.3 方案三:容器化安装(Docker + Rocky Linux 8 宿主机)
当你的 Rocky Linux 8 服务器要同时跑多个 nginx 实例(比如 A 项目用 1.20,B 项目用 1.25),或者需要快速销毁重建环境时,Docker 是最优解。它彻底隔离了依赖和配置,但增加了网络和存储的复杂度。
Docker 安装 nginx 的最小可行步骤:
# 确保 Docker 已在 Rocky 8 上安装(Rocky 8.10 推荐用 dnf install dnf-plugins-core && dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo) $ sudo dnf install -y dnf-plugins-core $ sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo $ sudo dnf install -y docker-ce docker-ce-cli containerd.io # 启动 Docker 服务 $ sudo systemctl enable docker --now # 拉取官方 nginx 镜像(alpine 版本小,但 musl libc 可能和某些模块不兼容;debian 版本大但兼容性好) $ sudo docker pull nginx:1.25.3 # 运行容器(关键:-p 80:80 映射宿主机 80 到容器 80;-v 挂载配置和网页目录) $ sudo docker run -d \ --name my-nginx \ -p 80:80 \ -p 443:443 \ -v /myapp/conf/nginx.conf:/etc/nginx/nginx.conf:ro \ -v /myapp/html:/usr/share/nginx/html:ro \ -v /myapp/logs:/var/log/nginx \ --restart=unless-stopped \ nginx:1.25.3Rocky 8 宿主机上的特殊配置:
Docker 默认用iptables管理网络,但 Rocky 8.10 的 firewalld 会与之冲突。必须让 firewalld “信任” Docker 的桥接网络:
# 创建 firewalld zone 专供 Docker 使用 $ sudo firewall-cmd --permanent --new-zone=docker $ sudo firewall-cmd --permanent --zone=docker --add-source=172.17.0.0/16 $ sudo firewall-cmd --permanent --zone=docker --add-port=80/tcp $ sudo firewall-cmd --permanent --zone=docker --add-port=443/tcp $ sudo firewall-cmd --reload # 验证 Docker 容器的 IP 是否在允许范围内 $ sudo docker inspect my-nginx | grep IPAddress "IPAddress": "172.17.0.2",这样,172.17.0.2这个容器 IP 就能通过dockerzone 访问宿主机的 80/443 端口,而不会被 firewalld 拦截。如果不做这步,curl http://localhost会超时,但curl http://172.17.0.2却能通——这就是典型的网络策略错位。
4. 启动后必做的七项验证与配置加固
4.1 验证 1:systemctl 服务状态的深层解读
systemctl status nginx的输出远不止active (running)四个字。你需要逐行解读:
$ sudo systemctl status nginx ● nginx.service - The nginx HTTP and reverse proxy server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled) Active: active (running) since Thu 2024-04-25 15:22:33 CST; 1min 23s ago Docs: man:nginx(8) Process: 1234 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS) Process: 1235 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS) Main PID: 1236 (nginx) Tasks: 5 (limit: 23590) Memory: 12.3M CGroup: /system.slice/nginx.service ├─1236 nginx: master process /usr/sbin/nginx └─1237 nginx: worker processLoaded行的enabled表示开机自启已注册;vendor preset: disabled是正常现象,表示 Rocky Linux 官方未预设该服务为启用。Process行的ExecStartPre是nginx -t语法检查,status=0/SUCCESS说明配置无硬错误。如果这里失败,Active行仍可能显示running,但 worker 进程不会启动。Main PID下的nginx: master process和nginx: worker process必须同时存在。如果只有 master,说明worker_processes设为0或配置里有daemon off;(调试模式),这在生产环境是严重错误。Tasks: 5表示当前有 5 个线程(1 master + 4 worker),符合worker_processes auto;在 4 核机器上的预期。
实操技巧:用systemctl show nginx查看所有 unit 属性,比如LimitNOFILE=显示文件描述符限制(默认 65536),如果 nginx 要处理百万连接,必须在这里调大:
# 创建 override 文件(不改原 unit,避免升级覆盖) $ sudo systemctl edit nginx # 在打开的编辑器中输入: [Service] LimitNOFILE=1048576 # 保存退出后重载 $ sudo systemctl daemon-reload $ sudo systemctl restart nginx4.2 验证 2:firewall-cmd 的端口放行是否真正生效
firewall-cmd --permanent --add-service=http看似简单,但它只对publiczone 生效。而 Rocky Linux 8 的网卡默认可能在trusted或internalzone。必须确认:
# 查看所有网卡所属 zone $ sudo firewall-cmd --get-active-zones public interfaces: eth0 # 查看 public zone 的具体规则 $ sudo firewall-cmd --zone=public --list-all public (active) target: default icmp-block-inversion: no interfaces: eth0 sources: services: ssh dhcpv6-client http ports: protocols: forward: no masquerade: no forward-ports: source-ports: icmp-blocks: rich rules: # 如果 services 里没有 http,手动添加 $ sudo firewall-cmd --permanent --zone=public --add-service=http $ sudo firewall-cmd --reload但更可靠的做法是直接放行端口,而非服务:
# 删除 service http,改为精确端口控制 $ sudo firewall-cmd --permanent --zone=public --remove-service=http $ sudo firewall-cmd --permanent --zone=public --add-port=80/tcp $ sudo firewall-cmd --permanent --zone=public --add-port=443/tcp $ sudo firewall-cmd --reload理由:http服务在 firewalld 里定义为80/tcp,但如果你的 nginx 监听8080,--add-service=http就无效。而--add-port=8080/tcp是绝对精准的。我在线上环境吃过亏:客户要求所有流量走 8080,我按教程加了http服务,结果测试不通,查了半小时才发现http服务只开 80。
4.3 验证 3:nginx 配置语法与逻辑双重检查
nginx -t只检查语法,不检查逻辑。比如你写了proxy_pass http://backend;,但upstream backend { }根本没定义,nginx -t会通过,但启动时 worker 会崩溃。必须用-T参数输出完整展开配置:
# 输出所有 include 后的最终配置(含注释) $ sudo nginx -T 2>/dev/null | head -50 # 搜索关键逻辑点(如 proxy_pass 是否指向有效 upstream) $ sudo nginx -T 2>/dev/null | grep -A5 "location /api" location /api { proxy_pass http://api_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 检查 api_backend 是否定义 $ sudo nginx -T 2>/dev/null | grep -A10 "upstream api_backend" upstream api_backend { server 127.0.0.1:3000; }一个经典逻辑错误:location /static和location ~ \.js$的优先级。Nginx 的location匹配规则是:精确匹配=> 前缀匹配^~> 正则匹配~> 普通前缀匹配。如果你写了:
location /static { alias /var/www/static/; } location ~ \.js$ { add_header Content-Type application/javascript; }那么/static/app.js会匹配/static(前缀匹配),不会进入\.js$正则块,导致Content-Type不生效。修复方法是给/static加^~:
location ^~ /static { alias /var/www/static/; }4.4 验证 4:SELinux 策略的动态调试
当curl http://localhost返回502 Bad Gateway,但nginx -t通过、firewall-cmd 放行、后端服务也活着,十有八九是 SELinux 拦截。Rocky Linux 8 的 SELinux 默认阻止 nginx 访问网络(httpd_can_network_connect)和读取非标准目录:
# 检查 SELinux 拒绝日志(实时监控) $ sudo ausearch -m avc -ts recent | grep nginx # 如果看到类似输出,说明被拒 type=AVC msg=audit(1714032153.123:456): avc: denied { name_connect } for pid=1237 comm="nginx" dest=3000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:port_t:s0 tclass=tcp_socket permissive=0 # 临时开启网络连接(生产环境慎用,仅用于诊断) $ sudo setsebool -P httpd_can_network_connect 1 # 如果 nginx 读取 /home/user/app.conf 被拒,恢复上下文 $ sudo restorecon -Rv /home/user/app.conf实操心得:不要一上来就
setenforce 0关 SELinux。用ausearch定位具体被拒行为,再用setsebool或semanage精准授权。-P参数表示永久生效,重启不失效。
4.5 验证 5:日志级别与错误捕获能力
Rocky Linux 8 的 dnf nginx 默认error_log级别是warn,这意味着info、debug级别的请求细节全被过滤。线上排障时,你可能需要看到client sent invalid request while reading client request line这类关键提示,它只在info级别出现:
# 修改 /etc/nginx/nginx.conf 的 error_log 行 error_log /var/log/nginx/error.log info; # 重启后,用 curl 触发一个 400 错误(比如发送非法 header) $ curl -H "Host: ;" http://localhost # 查看 error.log 是否记录了详细原因 $ sudo tail -n 5 /var/log/nginx/error.log 2024/04/25 15:30:45 [info] 1237#0: *1 client sent invalid request while reading client request line, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: ";"日志格式增强:默认的log_format combined不记录上游响应时间,这对性能分析是硬伤。在http块里加
