Nginx 全景入门图解

目标:先建立“地图”,再深入每一块。默认以 Debian/Ubuntu 用 apt 安装为例,文中会标注 CentOS/官方源的差异。

1 Nginx 是什么、怎么运转

Nginx 是一个事件驱动的服务器软件,常见四种用途:静态文件服务、反向代理(把请求转给后端程序)、负载均衡、HTTPS 终结(负责证书与加密)。

进程模型:master 进程(root 运行,读配置、管理 worker)→ 若干 worker 进程(通常是 www-data/nginx 用户,真正处理连接)。
所以:配置文件由 root 读取;而 worker 要能读取你的网站文件,权限不对就会 403。

2 配置文件的层级结构(最重要)

配置由“指令”和“块”组成。指令以分号结尾;块用花括号包住,可以嵌套。下图是嵌套关系:

main(最外层,无花括号)user、worker_processes、error_log、pid
events { }worker_connections 等连接处理参数
http { }所有 HTTP 相关:include mime.types、log_format、gzip、upstream …
upstream 名字 { }后端服务器池(负载均衡用)
server { }一个“虚拟主机”:listen、server_name、ssl 证书
location 匹配规则 { }针对某类 URL 做什么:root、proxy_pass、return、try_files
stream { }可选,做 TCP/UDP 转发(如代理数据库端口)

继承规则

内层默认继承外层的指令;内层写了同名指令就覆盖外层。例如 http 里写 gzip on;,所有 server 都生效。

坑:add_header、proxy_set_header 这类“数组型”指令,只要内层写了一条,外层的全部失效,不是叠加。

include:把配置拆成多个文件

include 就是“把那个文件的内容原地粘贴到这里”。所以 conf.d/*.conf 里的内容只写 server{} 即可,因为它被粘贴在 http{} 里面。

3 一个请求进来后,Nginx 怎么决策

① 连接到达 端口 + IP,例如 :443
↓
② 选 server 块 先按 listen 的 IP:端口筛;再按请求头 Host 对比 server_name。都不匹配则走 default_server,没指定就是该端口第一个 server
↓
③ 选 location 块 按第 6 节的优先级规则,用 URI 路径匹配
↓
④ 执行处理方式 静态文件(root/alias/try_files)|反向代理(proxy_pass)|FastCGI(PHP)|直接 return/重定向
↓
⑤ 写日志并返回 access.log 记录请求,error.log 记录错误

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 里建一个“快捷方式”(软链接);删掉快捷方式就停用,真身不丢。点按钮体验:

sites-available/
mysite.conf
default
sites-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   # 停用(只删链接)
坑:ln -s 的源路径请写绝对路径。写相对路径时,链接是相对“链接所在目录”解析的,很容易变成坏链接。

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 Forbiddenworker 用户无权限:目录每一级都需要 x 权限,文件需要 r;或没有 index 文件;CentOS 还要检查 SELinux
404root 与 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.1

9 常用命令速查

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 建议学习路线

第1步 吃透本页:层级、请求流程、路径
↓
第2步 静态站:root、index、try_files、location 匹配
↓
第3步 反向代理:proxy_pass、请求头、WebSocket
↓
第4步 HTTPS:certbot、ssl 参数、HTTP 跳转 HTTPS
↓
第5步 负载均衡 upstream、缓存、限流、gzip
↓
第6步 日志格式、性能调优、安全加固

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 upstreamNginx 启动时就解析后端域名,后端容器未就绪会失败。用 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 配置语法两边完全一样,差的只是文件放在哪里、怎么重载、怎么看日志。