server_name只匹配host头中的域名(如example.com),不匹配ipv6地址;ipv6支持由listen [::]:80等指令决定,三者关系为:listen定协议与端口,server_name依host头选server块,域名需dns解析生效。

server_name 指令本身不用于匹配 IPv6 地址,它只匹配 HTTP 请求头中的 Host 字段(即域名),比如 example.com 或 www.example.net。IPv6 地址(如 2001:db8::1)不能、也不应直接写在 server_name 后面作为主机名来使用。
真正决定 Nginx 是否能接收并处理 IPv6 请求的,是 listen 指令;而 server_name 的作用,是在多个虚拟主机共用同一 IP+端口时,靠 Host 头区分该交给哪个 server 块处理。
✅ 正确理解三者关系
-
listen [::]:80;→ 让 Nginx 监听所有 IPv6 地址的 80 端口(必须带方括号) -
server_name example.com;→ 当请求带Host: example.com时,才选中这个 server 块 - 客户端用 IPv6 访问
http://[2001:db8::1]/时,不会发送Host: [2001:db8::1](不符合 Host 语法),实际仍需通过域名访问
⚠️ 补充:RFC 3986 规定,IPv6 地址在 URL 中必须用方括号包裹(如
http://[2001:db8::1]/),但此时浏览器发出的Host头是[2001:db8::1]—— 这属于IP 直连模式,不是标准域名访问,Nginx 默认不会匹配server_name [2001:db8::1],且这种写法也不被推荐用于生产环境。
✅ 实际可行的配置方式
1. 域名 + IPv6 双栈监听(推荐)
server {
listen 80; # IPv4
listen [::]:80; # IPv6
server_name example.com;
location / {
return 200 "Served via IPv4 or IPv6\n";
}
}
只要 DNS 同时提供 A(IPv4)和 AAAA(IPv6)记录,用户无论走哪种协议,都能正确命中该 server 块。
2. 强制某 server 块仅响应 IPv6 流量(按监听地址区分)
# 仅响应 IPv6 请求(不响应 IPv4)
server {
listen [2001:db8::1]:80;
server_name example.com;
...
}
# 仅响应 IPv4 请求
server {
listen 192.0.2.1:80;
server_name example.com;
...
}
此时 server_name 仍写域名,但因 listen 绑定了特定 IPv6 地址,Nginx 会自然把发往该 IPv6 地址的 Host: example.com 请求路由到这里。
3. 匹配 IPv6 客户端真实 IP(用于日志或限流)
如果想在 log_format 或 geo/map 中识别 IPv6 客户端地址,要用 $remote_addr,而不是 server_name:
log_format ipv6log '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 注意:若前端有反向代理(如 CDN),需用 $http_x_forwarded_for 并配合 real_ip 模块
❌ 常见误区
-
server_name [2001:db8::1];→ 语法错误,Nginx 启动失败 -
server_name "2001:db8::1";→ 不报错但无效,Host头不会是纯 IPv6 字符串(除非手动构造非法请求) - 试图用
server_name控制协议类型 → 应该用listen,不是server_name
? 验证是否生效
- 确保本机有全局 IPv6 地址(
ipconfig或ip -6 addr show) -
nginx -t检查配置语法 -
curl -g http://[2001:db8::1]/(需服务绑定该地址)或curl -6 https://example.com/ - 查看 access log 中
$remote_addr是否为 IPv6 地址
不复杂但容易忽略。











