Nginx 把 default.save 备份加载后报 duplicate default server
sites-enabled/* 会把 default.save 等备份一起加载,导致同一地址、端口重复声明默认站点。核对 include 和文件内容,将多余副本移出加载范围,再测试与重载。
修改站点配置后运行 nginx -t,错误却指向一个看起来只是备份的文件:
nginx: [emerg] a duplicate default server for 0.0.0.0:80 in /etc/nginx/sites-enabled/default.save:20
nginx: configuration file /etc/nginx/nginx.conf test failed
这时要检查的不是文件扩展名是否“像配置”,而是主配置实际包含了哪些文件。如果存在下面的规则,default.save 也会进入配置解析:
include /etc/nginx/sites-enabled/*;
它和仍然启用的 default 都声明了同一个地址、端口的默认站点,就会冲突。本文针对这种备份被通配符加载的情况;路径不是所有安装方式的统一约定,容器、自编译版本和面板生成配置应以自身的 include 规则为准。
先沿报错路径确认重复内容
查看报错涉及的目录及两个文件。下面路径对应开头的示例,实际排查时换成错误日志中的路径:
sudo ls -l /etc/nginx/sites-enabled/
sudo sed -n '1,120p' /etc/nginx/sites-enabled/default
sudo sed -n '1,120p' /etc/nginx/sites-enabled/default.save
sudo diff -u /etc/nginx/sites-enabled/default /etc/nginx/sites-enabled/default.save
diff 返回 1 通常表示内容不同,要阅读差异,不能因此认定读取失败。文件里可能包含内部地址或认证配置,只在本机查看,不公开粘贴全文。
需要确认:这两个文件是否都被加载,以及它们是否都含有相同地址、端口的默认监听,例如:
listen 80 default_server;
default_server 属于 listen 的地址与端口组合,不属于域名。把两个文件的 server_name 改成不同域名,仍不能允许同一地址、端口出现两个默认站点。IPv4 的 0.0.0.0:80 与 IPv6 的 [::]:80 应分别按报错核对,不能把“整个 Nginx 只能有一个默认站点”当成规则。
如果两个文件并不相同,要先判断哪份包含需要保留的有效设置。default.save 可能只是编辑器留下的副本,也可能有人把它当成正式配置;仅凭名字不能决定删除。
找到真正生效的 include 范围
使用与运行实例相同的 Nginx 二进制和配置入口检查。对于使用默认入口的主机安装,可以执行:
sudo nginx -T
-T 在 Nginx 1.9.2 起提供,会测试并输出配置文件。配置已解析失败时,不保证得到完整展开结果,应结合错误位置直接读取主配置,再沿其中的 include 查下去。更旧版本需要用 -t 检查,并手动追踪 include。
如果启动时使用了 -c、-p 等参数,检查时也应使用相同配置路径和前缀。不要用宿主机的默认 Nginx 去判断另一个容器内的配置。-T 输出可能带有敏感设置,不要为了排查重复声明把它传到公共日志服务。
下面两条 include 匹配的文件范围不同:
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
第一条只匹配该目录中以 .conf 结尾的路径;第二条也会匹配 default.save、default.bak、default.disabled 这类名字。把文件在原目录里换一个后缀,未必能停用它。即使移到别处,也要确认没有另一条显式 include 继续指向它。
若根本没有多余备份,继续沿实际 include 查找其他 server 块或重复引用的文件。相同报错可以由两个正常站点都设置了 default_server 引起,不一定存在 default.save。
确认是多余副本后移出加载目录
先备份配置,并把要移出的文件保留在不会被 include 的位置。以下示例只适用于已经确认 default.save 是多余的普通文件,default 是要继续使用的配置;如果它是符号链接,先核对链接及目标,不要照搬文件处理步骤。
backup_dir=$(sudo mktemp -d /root/nginx-config-backup.XXXXXX)
sudo cp -a /etc/nginx "${backup_dir:?}/nginx"
printf '%s\n' "$backup_dir"
记录输出的备份路径,并确认复制成功后再执行下一步。如果实际配置散落在其他目录,或符号链接指向 /etc/nginx 以外的文件,另行备份那些目标文件;复制符号链接本身不等于保存了它指向的内容。备份目录也必须在实际 include 范围之外。
sudo mv -i /etc/nginx/sites-enabled/default.save "${backup_dir:?}/default.save"
sudo nginx -t
这里移动的是确认过的单个副本,没有批量清空站点目录。-i 在目标已存在时询问是否覆盖;遇到提示应先检查,避免覆盖另一份备份。新终端不会自动保留 backup_dir 变量,应使用已经记录的真实路径。
如果冲突来自两个都要保留的正式站点,应先决定哪个站点承担未匹配域名请求,再只移除另一个站点对应 listen 上的 default_server 参数,保留它的监听地址、端口和其他选项。不要把整个站点删除,也不要为了通过语法检查把所有默认标记全部删掉:没有显式标记时,配置中该地址、端口的第一个站点会成为默认站点,行为可能因此改变。
检查通过后再重载
只有 nginx -t 成功,才通过该实例原有的服务管理方式重载。例如,直接管理 Nginx master 进程、且使用默认配置入口的环境可以使用:
sudo nginx -s reload
使用自定义配置入口的实例,应沿用对应启动参数或服务管理命令。重载时 Nginx 会检查并尝试应用新配置;失败时继续使用旧配置。因此网站还能访问,并不能证明磁盘上的配置已经修好。
重载后检查错误日志,并验证正常域名、未匹配域名请求分别落在预期站点。多站点环境还应验证其他站点没有被误停用;监听 IPv4 和 IPv6 时分别检查相应入口。语法检查成功只说明配置能被接受,不能替代这些请求结果。
需要找回被移动的文件时,从记录的备份目录取回内容即可,但应先在不被加载的位置比较与合并。把同一份 default.save 原样放回通配符目录,会再次引入冲突。若发现选错了有效配置,恢复确认过的正确内容,同时确保同一地址、端口仍只有一个显式默认站点,再测试并重载。


