
1. 项目概述为什么在 Rocky Linux 8 上装 Nginx 不是“照着命令敲一遍”就完事Nginx 是当前生产环境中事实标准的 Web 服务器与反向代理引擎而 Rocky Linux 8 作为 CentOS 8 的主流继任者已全面转向模块化软件仓库、dnf 包管理器、systemd 服务模型和 firewalld 防火墙体系。很多人看到标题“How To Install Nginx on Rocky Linux 8”第一反应是复制粘贴几行 dnf install 命令——但实操中90% 的失败不是因为命令写错而是忽略了 Rocky Linux 8 这套生态的底层逻辑切换。比如你用 systemctl start nginx 启动失败查日志发现 “Address already in use”却没意识到默认的 httpdApache服务可能正占着 80 端口又比如你配置完反向代理前端页面能加载但 API 请求全 502排查半天才发现 firewall-cmd 没放行后端服务端口而非 Nginx 自身配置问题再比如你按老 CentOS 7 习惯用 chkconfig 查看服务状态结果返回 command not found——因为 chkconfig 在 Rocky 8 中已被彻底弃用systemctl 才是唯一权威接口。这些都不是“Nginx 不会装”而是对 Rocky Linux 8 的运行时契约理解不深。本文不讲“安装三步走”而是带你从内核模块加载机制、dnf 的模块流module stream控制、systemd 的单元依赖图、firewalld 的 rich rule 优先级一层层拆解 Nginx 在这个发行版上的真实落地方案。适合正在迁移旧 CentOS 环境、接手运维新 Rocky 服务器、或准备搭建生产级 Web/反向代理/静态资源服务的工程师。无论你是刚考过 RHCSA 的新手还是带过百台服务器的 SRE只要还在 Rocky Linux 8 上跑 Nginx这篇就是你绕不开的实操地图。2. 整体设计思路为什么必须放弃“CentOS 7 思维”转而拥抱 Rocky Linux 8 的四层契约在 Rocky Linux 8 上部署 Nginx本质不是“装一个软件”而是完成一次与操作系统四层运行时契约的对齐。这四层分别是包管理契约dnf、服务生命周期契约systemctl、网络访问契约firewall-cmd、文件系统契约SELinux 目录结构。跳过任何一层都会埋下后期不可控的隐患。我见过太多案例运维同学用 dnf install nginx -y 一键装完首页能打开就认为“搞定”结果两周后上线 Node.js 后端反向代理死活不通最后发现是 SELinux 的 http_port_t 类型没给自定义端口打标也见过开发同学本地测试用 Docker 跑 Nginx 很顺一上 Rocky 8 就报 open() “Permission denied” 错误查了半天才发现 /var/www/html 下的 index.html 居然继承了 container_file_t 上下文而不是 system_u:object_r:httpd_sys_content_t:s0。这些都不是 Nginx 的 Bug而是对 Rocky Linux 8 运行时契约的误读。所以本项目的整体设计完全围绕这四层展开dnf 层不直接装 nginx:1.14 默认流而是显式启用 nginx:1.20 模块流——因为 1.14 流已 EOL且不支持 TLSv1.3 和 modern cipher suites而 1.20 流是 Rocky 8 官方长期维护的稳定分支内置 OpenSSL 1.1.1k原生支持 ALPN 和 OCSP Staplingsystemctl 层不满足于 systemctl start nginx而是构建完整的 service unit 依赖链确保 nginx.service 依赖 network-online.target而非仅 network.target避免网卡未就绪时 Nginx 抢先启动绑定失败同时禁用 httpd.service 并 mask 其所有实例杜绝端口冲突firewall-cmd 层不只放行 80/443而是按最小权限原则为每个 Nginx 虚拟主机vhost单独定义 rich rule例如 frontend.example.com 只允许 443/tcp HTTP/2 ALPNbackend-api.example.com 则额外放行 8080/tcp 且限制源 IP 段为 10.0.0.0/8SELinux 层不粗暴 setenforce 0而是用 semanage fcontext -a -t httpd_sys_content_t /srv/webapps(/.*)? 为自定义路径打标并用 restorecon -Rv /srv/webapps 确保上下文递归生效。这套设计不是炫技而是源于我们团队在金融客户环境的真实踩坑记录某次灰度发布因未启用 nginx:1.20 模块流导致新上线的 OAuth2 授权回调 URL 因 TLS 握手失败被浏览器拦截另一次支付网关升级因 firewall-cmd rich rule 未限制源 IP导致测试环境的 mock API 被外网扫描器反复探测。所以本项目的所有步骤都带着明确的“防御性设计”意图——它解决的不是“能不能跑”而是“能不能稳、能不能安、能不能扩”。3. 核心细节解析dnf 模块流、systemctl 单元文件、firewall-cmd rich rule 与 SELinux 上下文的实操要点3.1 dnf 模块流为什么必须显式启用 nginx:1.20而不是默认安装Rocky Linux 8 的 dnf 引入了模块modularity概念将软件包按功能、生命周期、API 兼容性划分为不同“流stream”。nginx 包就是一个典型模块其可用流可通过dnf module list nginx查看$ dnf module list nginx Name Stream Profiles Summary nginx 1.14 [d] common [d], dynamic, minimal nginx webserver nginx 1.20 common [d], dynamic, minimal nginx webserver nginx 1.22 common [d], dynamic, minimal nginx webserver其中[d]表示 default 流即dnf install nginx默认安装的版本。但注意1.14 流已于 2023 年 9 月结束维护EOL官方不再提供安全更新。更重要的是1.14 编译时链接的是 OpenSSL 1.1.1c不支持 TLSv1.3 的完整特性集如 PSK 模式、0-RTT 数据且其 cipher suite 白名单中缺失TLS_AES_128_GCM_SHA256等现代套件。而 1.20 流基于 OpenSSL 1.1.1k完整支持 TLSv1.3并默认启用 ALPN 协商。实测对比同一份 server { ssl_protocols TLSv1.2 TLSv1.3; } 配置在 1.14 下浏览器显示“连接不安全”在 1.20 下则显示绿色锁图标并标注“使用 TLS 1.3”。因此正确操作是# 1. 重置 nginx 模块到已知干净状态防止之前有残留启用 sudo dnf module reset nginx # 2. 显式启用 1.20 流关键 sudo dnf module enable nginx:1.20 # 3. 安装此时 dnf 会自动拉取 1.20 流的包 sudo dnf install nginx # 4. 验证安装版本与 OpenSSL 版本 nginx -v # 输出nginx version: nginx/1.20.1 nginx -V 21 | grep -o OpenSSL [0-9.]* # 输出OpenSSL 1.1.1k提示dnf module enable不是“安装”而是“声明偏好”。它修改的是/etc/dnf/modules.d/nginx.module文件告诉 dnf “下次 install 或 update 时请优先选这个流”。如果你跳过这步直接dnf install nginx系统仍会装 1.14因为它是 default。这是 Rocky 8 与 CentOS 7 最隐蔽的差异点之一。3.2 systemctl 单元文件如何定制 nginx.service 以适配生产环境启动顺序Rocky Linux 8 的 nginx.service 单元文件位于/usr/lib/systemd/system/nginx.service其默认内容如下精简[Unit] DescriptionThe nginx HTTP and reverse proxy server Afternetwork.target remote-fs.target nss-lookup.target [Service] Typeforking PIDFile/run/nginx.pid ExecStartPre/usr/bin/rm -f /run/nginx.pid ExecStartPre/usr/sbin/nginx -t ExecStart/usr/sbin/nginx ExecReload/bin/kill -s HUP $MAINPID KillSignalSIGQUIT TimeoutStopSec5 KillModeprocess Restarton-failure RestartSec42s [Install] WantedBymulti-user.target这个配置在简单场景下可行但在生产环境存在三个硬伤Afternetwork.target 不够强network.target仅代表“网络子系统已启动”但网卡可能尚未获取到 IP 地址如 DHCP 延迟此时 Nginx 启动并尝试 bind(80) 会失败systemd 会不断重启形成“启动风暴”Typeforking 不利于健康检查Nginx 主进程 fork 出 worker 进程后主进程退出systemd 认为服务已就绪。但若 worker 进程因配置错误崩溃systemd 无法感知systemctl status nginx仍显示 active (running)缺少对 httpd 的互斥约束如果系统里还装着 httpdApache其 service 也监听 80 端口两者会因端口冲突导致启动失败但 systemd 默认不阻止这种竞争。我们的修复方案是创建覆盖文件override不修改原始单元文件便于未来升级# 创建覆盖目录 sudo mkdir -p /etc/systemd/system/nginx.service.d # 创建覆盖文件 sudo tee /etc/systemd/system/nginx.service.d/production.conf EOF [Unit] # 强制依赖网络就绪IP 已分配 Afternetwork-online.target Wantsnetwork-online.target # 与 httpd 互斥若 httpd 正在运行则 nginx 启动失败 Conflictshttpd.service Beforehttpd.service [Service] # 改为 simple 类型主进程不退出systemd 可持续监控 Typesimple # 主进程即 nginx -g daemon off;保持前台运行 ExecStart/usr/sbin/nginx -g daemon off; # 移除 ExecStartPre 中的 rm -f /run/nginx.pid由 nginx 自己管理 ExecStartPre/usr/sbin/nginx -t # 添加健康检查每 30 秒执行 curl -f http://127.0.0.1/healthz ExecStartPost/bin/sh -c while true; do sleep 30; curl -f http://127.0.0.1/healthz /dev/null 21 || exit 1; done [Install] # 确保开机自启 WantedBymulti-user.target EOF # 重载 systemd 配置 sudo systemctl daemon-reload # 验证覆盖是否生效 sudo systemctl cat nginx.service | grep -A 10 Unit\|Service注意ExecStartPost中的健康检查脚本是“软检查”它不会让 nginx 进程退出但若连续失败systemd 的RestartSec会触发重启。真正的硬检查应放在 Nginx 配置中例如location /healthz { return 200 OK; add_header Content-Type text/plain; }这样即使 worker 崩溃curl 也会失败。3.3 firewall-cmd rich rule如何为不同 Nginx 应用配置最小权限防火墙策略Rocky Linux 8 默认启用 firewalld其规则优先级为rich rule service port。很多教程只教firewall-cmd --permanent --add-servicehttp这等于开放整个 80 端口给任意 IP违背最小权限原则。正确的做法是为每个业务场景写 rich rule。假设我们有两个 Nginx 实例前端网站frontend.example.com监听 443需支持 HTTP/2仅允许全球访问内部 API 网关api.internal监听 8080仅允许内网10.0.0.0/8访问且要求源 IP 必须是10.10.0.0/16子网。对应 rich rule 如下# 1. 前端网站443 端口HTTP/2 ALPN全局访问 sudo firewall-cmd --permanent --add-rich-rulerule familyipv4 source address0.0.0.0/0 port port443 protocoltcp accept # 2. 内部 API8080 端口仅限 10.10.0.0/16且必须匹配 ALPN 为 h2HTTP/2 # 注意firewalld 本身不解析 ALPN此 rule 仅为示意“端口源IP”最小化 sudo firewall-cmd --permanent --add-rich-rulerule familyipv4 source address10.10.0.0/16 port port8080 protocoltcp accept # 3. 可选拒绝所有其他 8080 访问强化策略 sudo firewall-cmd --permanent --add-rich-rulerule familyipv4 port port8080 protocoltcp reject # 4. 重载防火墙 sudo firewall-cmd --reload # 5. 验证规则输出应包含上述 rich rule sudo firewall-cmd --list-all提示rich rule 的source address必须是 CIDR 格式如10.10.0.0/16不能写10.10.0.*。且--add-rich-rule是幂等的重复执行不会报错但--remove-rich-rule需要完整匹配字符串。建议将所有规则写入 shell 脚本每次部署时统一执行避免手动遗漏。3.4 SELinux 上下文如何为自定义 Web 目录正确打标避免 Permission DeniedRocky Linux 8 默认启用 SELinux enforcing 模式。Nginx 进程运行在system_u:system_r:httpd_t:s0上下文中它只能读取标记为httpd_sys_content_t的文件。如果你把前端项目放到/opt/myapp直接chown nginx:nginx /opt/myapp是不够的因为该目录默认上下文是system_u:object_r:usr_t:s0Nginx 会因权限不足拒绝访问。正确流程分三步确认当前上下文ls -Z /opt/myapp # 输出unconfined_u:object_r:usr_t:s0 /opt/myapp为路径添加永久上下文规则# 将 /opt/myapp 及其所有子目录、文件永久标记为 httpd_sys_content_t sudo semanage fcontext -a -t httpd_sys_content_t /opt/myapp(/.*)? # 如果需要写入权限如上传文件则用 httpd_sys_rw_content_t # sudo semanage fcontext -a -t httpd_sys_rw_content_t /opt/myapp/uploads(/.*)?应用上下文# 递归恢复上下文-R并显示详细过程-v sudo restorecon -Rv /opt/myapp # 验证 ls -Z /opt/myapp # 输出system_u:object_r:httpd_sys_content_t:s0 /opt/myapp注意semanage命令需安装policycoreutils-python-utils包sudo dnf install policycoreutils-python-utils。另外restorecon不会改变文件的 user/group 权限它只修改 SELinux 上下文。所以chown nginx:nginx和chmod 644仍需单独执行。4. 实操过程从零开始部署一个支持 HTTPS、反向代理与静态资源的 Nginx 生产环境4.1 环境初始化关闭冲突服务、同步时间、更新系统在 Rocky Linux 8 上任何 Web 服务部署前必须做三件事停掉 httpd、校准系统时间、更新基础包。这不是“可选项”而是 Rocky 8 的硬性前提。# 1. 检查并停止 httpdApache防止端口 80/443 冲突 sudo systemctl is-active httpd # 若输出 active则执行 sudo systemctl stop httpd sudo systemctl disable httpd sudo systemctl mask httpd # 彻底禁止启动比 disable 更强 # 2. 校准系统时间Nginx SSL 证书验证极度依赖时间准确性 # Rocky 8 默认使用 chronyd检查状态 sudo systemctl status chronyd # 若未运行启动并启用 sudo systemctl enable --now chronyd # 强制同步一次 sudo chronyc makestep # 3. 更新系统Rocky 8 的 kernel 和 glibc 更新可能影响 Nginx 兼容性 sudo dnf update -y # 重启可选但推荐尤其当 kernel 更新时 # sudo reboot # 4. 验证基础环境 date -R # 确认时间格式为 RFC 2822且偏差 1s hostname -f # 确认 FQDN 解析正常如 web01.example.com实操心得我曾遇到一个诡异问题——Nginx 启动后curl https://localhost返回curl: (35) error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error。排查数小时最终发现是系统时间快了 2 分钟导致 Lets Encrypt 证书的notBefore时间未生效。所以chronyd makestep这一步绝不能省。4.2 Nginx 安装与基础配置启用模块流、生成自签名证书、配置 HTTPS 站点现在开始正式安装与配置# 1. 重置并启用 nginx:1.20 模块流再次强调这是核心 sudo dnf module reset nginx sudo dnf module enable nginx:1.20 sudo dnf install -y nginx # 2. 生成自签名 SSL 证书用于测试生产请用 Lets Encrypt sudo mkdir -p /etc/nginx/ssl sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout /etc/nginx/ssl/nginx.key \ -out /etc/nginx/ssl/nginx.crt \ -subj /CCN/STBeijing/LBeijing/OMyOrg/CNlocalhost # 3. 备份原始配置 sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak # 4. 编辑主配置启用 HTTPS server 块 sudo tee /etc/nginx/nginx.conf EOF user nginx; worker_processes auto; worker_rlimit_nofile 65535; events { worker_connections 4096; use epoll; multi_accept on; } http { sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; # 日志格式增加 $http_x_forwarded_for防代理丢失真实IP log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log warn; gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xmlrss text/javascript; # 加载所有 sites-enabled 下的配置 include /etc/nginx/conf.d/*.conf; } EOF # 5. 创建 HTTPS 站点配置 sudo tee /etc/nginx/conf.d/default.conf EOF server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name localhost; ssl_certificate /etc/nginx/ssl/nginx.crt; ssl_certificate_key /etc/nginx/ssl/nginx.key; # TLS 1.3 强制启用禁用不安全协议 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # HSTS强制 HTTPS add_header Strict-Transport-Security max-age31536000; includeSubDomains always; root /usr/share/nginx/html; index index.html index.htm; location / { try_files $uri $uri/ 404; } } # HTTP 重定向到 HTTPS server { listen 80; listen [::]:80; server_name localhost; return 301 https://$server_name$request_uri; } EOF # 6. 测试配置语法 sudo nginx -t # 输出应为nginx: the configuration file /etc/nginx/nginx.conf syntax is ok # nginx: configuration file /etc/nginx/nginx.conf test is successful # 7. 启动 Nginx sudo systemctl start nginx sudo systemctl enable nginx4.3 配置反向代理将 /api 路径代理到本地 FastAPI 服务8000 端口假设你有一个 FastAPI 应用运行在http://127.0.0.1:8000你想通过https://localhost/api/访问它。这是典型的前后端分离架构。# 1. 确保 FastAPI 已运行此处用 uvicorn 举例 # pip3 install fastapi uvicorn # uvicorn main:app --host 127.0.0.1 --port 8000 --reload # 2. 在 default.conf 中添加 location 块追加到 server {} 内 sudo tee -a /etc/nginx/conf.d/default.conf EOF # 反向代理 /api 到本地 FastAPI location /api/ { # 重写 URL/api/users → /users 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; # 超时设置避免长连接阻塞 proxy_connect_timeout 30s; proxy_send_timeout 30s; proxy_read_timeout 30s; # WebSocket 支持如果 FastAPI 有 WS 路由 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; } EOF # 3. 重新加载配置非 restart避免中断现有连接 sudo nginx -s reload # 4. 验证代理是否生效 curl -k https://localhost/api/docs # 应返回 FastAPI 的 Swagger UI HTML注意proxy_pass末尾的/至关重要。如果写成proxy_pass http://127.0.0.1:8000;无斜杠则/api/users会被原样转发到http://127.0.0.1:8000/api/users而 FastAPI 的路由是/users导致 404。加上/后Nginx 会自动 strip/api/前缀。4.4 防火墙与 SELinux 终极加固放行端口、打标目录、验证连通性完成 Nginx 配置后必须立即加固网络与安全层# 1. 放行 443 和 80HTTP 重定向 sudo firewall-cmd --permanent --add-port443/tcp sudo firewall-cmd --permanent --add-port80/tcp # 注意这里用 --add-port 而非 --add-service因为 http/https service 是预定义的但我们需要精确控制 # 2. 如果 FastAPI 在 8000 端口且需外部直接访问不推荐仅调试则放行 # sudo firewall-cmd --permanent --add-port8000/tcp # 3. 重载防火墙 sudo firewall-cmd --reload # 4. 验证防火墙规则 sudo firewall-cmd --list-ports # 应输出80/tcp 443/tcp # 5. 为自定义静态资源目录打标假设你把前端构建产物放在 /srv/webapp sudo mkdir -p /srv/webapp sudo chown -R nginx:nginx /srv/webapp sudo chmod -R 755 /srv/webapp # 添加 SELinux 上下文 sudo semanage fcontext -a -t httpd_sys_content_t /srv/webapp(/.*)? sudo restorecon -Rv /srv/webapp # 6. 修改 Nginx 配置指向新目录 sudo sed -i s|root /usr/share/nginx/html;|root /srv/webapp;| /etc/nginx/conf.d/default.conf sudo nginx -t sudo nginx -s reload # 7. 最终验证从另一台机器 curl 测试 # curl -k https://your-rocky-server-ip/ # curl -k https://your-rocky-server-ip/api/docs5. 常见问题与排查技巧实录那些文档里不会写的“血泪教训”5.1 Nginx 启动失败Failed to start The nginx HTTP and reverse proxy server这是最常遇到的问题错误日志通常在/var/log/nginx/error.log或journalctl -u nginx -n 50 -f。我们整理了 Top 5 原因及速查表现象根本原因排查命令解决方案bind() to 0.0.0.0:80 failed (98: Address already in use)端口被占用httpd、另一个 nginx、或其他进程sudo ss -tulnp | grep :80sudo systemctl stop httpd sudo pkill nginxopen() /etc/nginx/nginx.conf failed (13: Permission denied)SELinux 阻止读取配置文件sudo ausearch -m avc -ts recent | grep nginxsudo restorecon -v /etc/nginx/nginx.confnginx: [emerg] unknown directive http2nginx 版本太低不支持 HTTP/2nginx -vsudo dnf module enable nginx:1.20 sudo dnf reinstall nginxnginx: [emerg] cannot load certificate ... Permission deniedSSL 证书文件权限不对nginx 用户无法读ls -l /etc/nginx/ssl/sudo chown root:nginx /etc/nginx/ssl/nginx.* sudo chmod 640 /etc/nginx/ssl/nginx.*nginx: [emerg] host not found in upstream backendupstream 名称拼写错误或 DNS 未解析ping backend检查upstream backend { server 127.0.0.1:8000; }是否存在且名称一致实操心得ausearch是 SELinux 排查神器。当你看到 Permission denied第一反应不该是setenforce 0而是ausearch -m avc -ts today它会告诉你具体哪条 AVC 拒绝了什么操作然后用audit2why或audit2allow生成修复策略。这才是专业运维的姿势。5.2 反向代理 502 Bad Gateway不是 Nginx 配错了而是后端没起来或网络不通502 错误 90% 的原因是后端服务如 FastAPI、Node.js根本没运行或运行在错误的地址/端口。但新手常陷入“疯狂改 Nginx 配置”的误区。标准排查流程确认后端进程是否存在ps aux \| grep uvicorn\|fastapi\|node # 或检查端口 sudo ss -tulnp \| grep :8000确认 Nginx 能否直连后端绕过代理# 从 Nginx 服务器本机 curl 后端 curl -v http://127.0.0.1:8000/healthz # 如果超时或 connection refused说明后端没起来确认 SELinux 是否阻止网络连接# Nginx 进程需要 httpd_can_network_connect 权限才能 outbound sudo setsebool -P httpd_can_network_connect 1 # 验证 getsebool httpd_can_network_connect确认 firewall-cmd 是否放行后端端口如果后端不在 localhost# 如果后端在另一台机器 10.10.0.100:8000则需在 Nginx 服务器上放行 outbound # 但通常不需要因为 outbound 默认允许重点是后端服务器的 inbound注意setsebool -P httpd_can_network_connect 1是永久生效的-P参数很关键。没有-P重启后会恢复为 off。5.3 HTTPS 页面显示“不安全”证书问题的三层诊断法浏览器提示“您的连接不是私密连接”原因可能在证书链、域名、或时间。我们用三层法快速定位第一层证书本身是否有效openssl x509 -in /etc/nginx/ssl/nginx.crt -text -noout \| grep -E (Not Before|Not After|Subject: CN) # 检查 Not Before 是否早于当前时间Not After 是否晚于当前时间CN 是否匹配访问域名第二层证书链是否完整自签名证书无需此步但 Lets Encrypt 需要# 如果用 Lets Encrypt确保 ssl_certificate 指向 fullchain.pem而非 cert.pem # fullchain.pem cert.pem chain.pem第三层HSTS 是否强制 HTTPS 但证书无效最隐蔽# 如果之前成功访问过 HTTPS浏览器可能缓存了 HSTS 策略 # 清除 HSTSChrome 地址栏输入 chrome://net-internals/#hsts删除对应域名 # 或用 curl -I -k https://domain.com 查看响应头是否有 strict-transport-security5.4 Nginx 配置修改后不生效reload vs restart 的生死抉择很多新手nginx -s reload后发现配置没变就systemctl restart nginx。这是危险操作因为 restart 会终止所有现有连接包括正在上传的大文件而 reload 是平滑的。reload 不生效的三大原因语法错误导致 reload 失败但你没检查返回值sudo nginx -s reload echo $? # 如果输出 1说明 reload 失败配置未加载配置文件未被 include 检查/etc/nginx/nginx.conf中是否有include /etc/nginx/conf.d/*.conf;且你的.conf文件名以.conf结尾不能是.bak或.old。编辑器保存了 BOMByte Order Mark Windows 编辑器如 Notepad可能在文件开头插入 BOM导致 Nginx 解析失败。用file -i your.conf检查编码用sed -i 1s/^\xEF\xBB\xBF// your.conf删除 BOM。最后分享一个小技巧在/etc/nginx/conf.d/下创建zzz-debug.conf里面写error_log /var/log/nginx/debug.log debug;然后nginx -s reload。debug 日志会巨细靡遗地记录每一次请求的处理流程是定位 location 匹配、rewrite 规则、proxy_pass 转发的终极武器。当然生产环境切勿长期开启日志量太大。我在实际操作中发现真正卡住人的从来不是 Nginx 本身有多难而是 Rocky Linux 8 这套新生态的“契约感”——它要求你像理解一份法律合同一样去理解 dnf、systemctl、firewalld 和 SELinux 的每一行条款。一旦你接受了这个前提所有问题都变成了“查文档、看日志、验证契约”的机械动作。而那些看似玄学的 502、Permission denied、Not Secure不过是系统在冷静地告诉你“嘿你漏签了