Nginx 全景入门图解
目标:先建立“地图”,再深入每一块。默认以 Debian/Ubuntu 用 apt 安装为例,文中会标注 CentOS/官方源的差异。
1 Nginx 是什么、怎么运转
Nginx 是一个事件驱动的服务器软件,常见四种用途:静态文件服务、反向代理(把请求转给后端程序)、负载均衡、HTTPS 终结(负责证书与加密)。
master 进程(root 运行,读配置、管理 worker)→ 若干 worker 进程(通常是 www-data/nginx 用户,真正处理连接)。所以:配置文件由 root 读取;而 worker 要能读取你的网站文件,权限不对就会 403。
2 配置文件的层级结构(最重要)
配置由“指令”和“块”组成。指令以分号结尾;块用花括号包住,可以嵌套。下图是嵌套关系:
继承规则
内层默认继承外层的指令;内层写了同名指令就覆盖外层。例如 http 里写 gzip on;,所有 server 都生效。
add_header、proxy_set_header 这类“数组型”指令,只要内层写了一条,外层的全部失效,不是叠加。include:把配置拆成多个文件
include 就是“把那个文件的内容原地粘贴到这里”。所以 conf.d/*.conf 里的内容只写 server{} 即可,因为它被粘贴在 http{} 里面。
3 一个请求进来后,Nginx 怎么决策
server_name 匹配顺序:精确名 → 以 * 开头的通配 → 以 * 结尾的通配 → 正则 → default_server。
4 文件和目录地图
/etc/nginx/配置总目录nginx.conf主配置,入口。末尾通过 include 引入下面的目录conf.d/放 *.conf,会被自动加载。官方源与 CentOS 的主要放置位置sites-available/(Debian 系特有)“可用站点”仓库,仅存放,不生效sites-enabled/(Debian 系特有)“已启用站点”,里面是指向 available 的软链接,nginx.conf 只 include 这里snippets/(Debian 系)可复用的配置片段,如 ssl 参数,用 include 引用mime.types文件扩展名 → Content-Type 的对照表fastcgi_params / uwsgi_params / scgi_params对接 PHP-FPM、uWSGI 等时用的参数模板modules-available/ modules-enabled/动态模块的加载配置,原理同 sites/var/log/nginx/日志:access.log(访问)、error.log(排错首选)/var/www/html/Debian 系默认网站根目录(CentOS/官方源为 /usr/share/nginx/html/)/usr/sbin/nginx可执行文件/run/nginx.pidmaster 进程号文件/lib/systemd/system/nginx.servicesystemd 服务定义,systemctl 靠它管理/var/cache/nginx/ /var/lib/nginx/代理缓存、临时文件/etc/letsencrypt/live/域名/用 certbot 申请证书时的证书位置(fullchain.pem、privkey.pem)不确定路径时,用 nginx -T 打印“最终合并后的完整配置”,并用 nginx -V 查看编译参数(含默认路径)。
Linux 路径小贴士
/etc 放配置,/var/log 放日志,/var/www 放网站,/usr 放程序,/run 放运行时临时文件。以 / 开头是绝对路径,否则是相对当前目录。
5 sites-available / sites-enabled 与软链接
思路:配置文件真身只有一份,放在 available;想启用就在 enabled 里建一个“快捷方式”(软链接);删掉快捷方式就停用,真身不丢。点按钮体验:
变更后还需要 nginx -t 检查,再 systemctl reload nginx 才会生效。
sudo ln -s /etc/nginx/sites-available/mysite.conf /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/mysite.conf # 停用(只删链接)
6 location 匹配优先级(交互演示)
规则:①先看精确匹配 =,命中即结束;②在普通前缀里找“最长匹配”,若它带 ^~ 则直接结束;③否则按文件中出现的顺序测试正则(~ 区分大小写,~* 不区分),第一个命中者胜出;④正则都不中,就用第②步的最长前缀。
点一个请求路径:
7 带注释的最小可用示例
# /etc/nginx/sites-available/mysite.conf(会被 include 到 http{} 里)
server {
listen 80; # 监听端口
server_name example.com; # 匹配 Host 头
root /var/www/mysite; # 网站根目录
index index.html;
location / { # 静态站:找不到就 404
try_files $uri $uri/ =404;
}
location /api/ { # 反向代理到本机 3000 端口程序
proxy_pass http://127.0.0.1:3000;
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;
}
}
常见内置变量:$uri(请求路径)、$host、$remote_addr(客户端 IP)、$scheme(http/https)、$request_uri(含参数的原始地址)。
8 高频踩坑清单
| 现象/问题 | 常见原因与处理 |
|---|---|
| 修改后没效果 | 忘了 reload;或改的是 available 而没建软链接;或被别的 server 块抢先匹配(用 nginx -T 查) |
| 少写分号/花括号 | reload 前务必 nginx -t |
| 403 Forbidden | worker 用户无权限:目录每一级都需要 x 权限,文件需要 r;或没有 index 文件;CentOS 还要检查 SELinux |
| 404 | root 与 alias 混淆。root 会把 location 路径拼到后面;alias 是替换 |
| 502 Bad Gateway | 后端程序没启动、端口写错、PHP-FPM sock 路径不对。看 error.log |
| 504 Gateway Timeout | 后端响应太慢,调 proxy_read_timeout 或优化后端 |
| 413 上传失败 | client_max_body_size 默认只有 1m |
| proxy_pass 路径错位 | 末尾有无 / 行为不同:proxy_pass http://x; 保留原路径;proxy_pass http://x/; 会替换掉 location 前缀 |
| 后端拿不到真实 IP / Host | 没设置 proxy_set_header(见第 7 节) |
| WebSocket 断开 | 需加 proxy_http_version 1.1; 与 Upgrade / Connection 头 |
| if 行为诡异 | location 里的 if 有很多限制,优先用 try_files、return、map |
| 默认站点冲突 | Debian 默认的 sites-enabled/default 会抢 80 端口,不用就删链接 |
| 端口被占用 / 外网访问不了 | ss -tlnp 看端口;检查 ufw/firewalld 与云服务商安全组 |
| HTTPS 重定向死循环 | 前面还有 CDN/负载均衡时,要根据 X-Forwarded-Proto 判断 |
nginx -t ② tail -f /var/log/nginx/error.log ③ curl -I -H "Host: example.com" http://127.0.0.19 常用命令速查
sudo nginx -t # 检查配置语法 sudo nginx -T # 检查并打印完整合并配置 sudo systemctl reload nginx # 平滑重载(不断连接,首选) sudo systemctl restart nginx # 重启(会中断连接) sudo systemctl status nginx # 状态 sudo systemctl enable nginx # 开机自启 sudo nginx -V # 版本与编译参数 ps aux | grep nginx # 看 master/worker sudo ss -tlnp | grep nginx # 看监听端口 ls -l /etc/nginx/sites-enabled/ # 看软链接指向
10 建议学习路线
11 Ubuntu / Debian 新旧版本差异
差异主要来自两个层面:Nginx 自身版本(语法变化)和发行版打包方式(路径、服务管理)。先确认版本:lsb_release -a、nginx -v。遇到 unknown directive 或 deprecated 警告,第一反应就是查版本。
| 项目 | 差异与建议 |
|---|---|
| 仓库自带版本(大致) | Ubuntu 20.04/22.04 约 1.18,24.04 约 1.24;Debian 11 约 1.18,12 约 1.22。以 nginx -v 为准,发行版会打补丁 |
| HTTP/2 写法 | 1.25.1 起,listen 443 ssl http2; 被废弃,改为 listen 443 ssl; 加单独一行 http2 on;。反过来,旧版本不认识 http2 on; 会直接报错 |
| ssl on; | 1.15 起废弃,应写成 listen 443 ssl;。网上老教程常见此写法 |
| 默认网站目录 | 很老的系统(如 Ubuntu 14.04、Debian 8)是 /usr/share/nginx/html;之后是 /var/www/html |
| 服务管理 | 老系统用 SysV init,新系统用 systemd。service nginx reload 在两者上都能用,systemctl 仅限 systemd |
| PHP-FPM 套接字 | 路径带版本号,如 /run/php/php8.1-fpm.sock。升级 PHP 后路径变化,Nginx 就会 502 |
| include 通配规则 | sites-enabled/* 不要求后缀,会加载目录里所有文件(包括 .bak、编辑器临时文件);conf.d/*.conf 只加载 .conf 结尾的文件 |
| 运行用户 | Debian/Ubuntu 仓库版是 www-data;nginx.org 官方源版是 nginx。目录权限要按实际用户设置 |
| 官方源 vs 发行版源 | 官方源(nginx.org)没有 sites-available/enabled 和 snippets,只用 conf.d/,版本更新更快(stable/mainline)。两种来源不要混装 |
nginx -V 看版本与编译模块(如 http_v2_module、http_ssl_module);抄网上教程时留意发布时间,对照版本再用。12 Docker 中的 Nginx
核心理解:容器 = 一个只跑 Nginx 的小系统。路径和宿主机上的“实体机安装”不同,且没有 systemd,配置要靠“挂载”从宿主机送进去。
/etc/nginx/nginx.conf主配置。官方镜像里没有 sites-enabled,只 include conf.d/etc/nginx/conf.d/default.conf默认站点,自己的 server 配置通常放这个目录/usr/share/nginx/html/默认网站根目录/var/log/nginx/日志文件其实是指向 stdout/stderr 的链接,用 docker logs 查看/etc/nginx/templates/*.template模板,启动时用环境变量替换后生成到 conf.d(官方镜像特性)/docker-entrypoint.d/启动前自动执行的脚本前台运行:镜像用 nginx -g 'daemon off;' 启动,Nginx 是容器的主进程,它一退出容器就停止。
最小运行示例
docker run -d --name web -p 8080:80 \ -v $(pwd)/conf.d:/etc/nginx/conf.d:ro \ -v $(pwd)/html:/usr/share/nginx/html:ro \ nginx:stable
docker compose(前端 + 后端)
services:
web:
image: nginx:stable
ports: ["80:80", "443:443"]
volumes:
- ./conf.d:/etc/nginx/conf.d:ro
- ./html:/usr/share/nginx/html:ro
- ./certs:/etc/nginx/certs:ro
depends_on: [app]
app:
image: my-app:latest # 监听 3000 端口
conf.d/site.conf 里这样写:
proxy_pass http://app:3000; 用服务名,而不是 localhost
日常操作
docker exec web nginx -t # 检查配置 docker exec web nginx -s reload # 平滑重载 docker logs -f web # 看访问与错误日志 docker exec -it web sh # 进入容器排查 docker exec web nginx -T # 查看最终完整配置
Docker 特有的坑
| 现象 | 原因与处理 |
|---|---|
| proxy_pass 到 localhost 不通 | 容器里的 localhost 是容器自己。同一 compose 网络用服务名;要访问宿主机服务,Linux 上加 --add-host=host.docker.internal:host-gateway,或使用 network_mode: host |
| 启动报 host not found in upstream | Nginx 启动时就解析后端域名,后端容器未就绪会失败。用 depends_on、重启策略,或用变量配合 resolver 127.0.0.11; 延迟解析 |
| 改了配置没生效 | 忘记 reload;或单文件挂载时,用 vim 等编辑器保存会换 inode,容器里看到的还是旧文件。建议挂载整个目录 |
| 挂载目录后文件消失 | 挂载会覆盖容器内原目录。把空目录挂到 /etc/nginx 会让 mime.types 等全部消失;只挂 conf.d 最稳妥 |
| 403 / 权限问题 | 容器内用户是 nginx,宿主机文件要对其可读。SELinux 系统在挂载后加 :z |
| 找不到日志文件 | 日志去了标准输出,用 docker logs;需要落盘就挂载并改 access_log 路径 |
| 证书与续期 | 把证书目录以只读方式挂入,续期后执行 nginx -s reload |
| 容器重建后配置丢失 | 容器内改的内容不持久。所有配置都应放在宿主机,通过 volume 挂载(配置即代码) |
systemctl 和 /etc/nginx 里的 sites-enabled;Docker 靠 docker exec 和挂载的 conf.d。Nginx 配置语法两边完全一样,差的只是文件放在哪里、怎么重载、怎么看日志。