惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
WordPress大学
WordPress大学
U
Unit 42
I
InfoQ
A
About on SuperTechFans
宝玉的分享
宝玉的分享
J
Java Code Geeks
博客园 - 司徒正美
爱范儿
爱范儿
Engineering at Meta
Engineering at Meta
G
Google Developers Blog
人人都是产品经理
人人都是产品经理
小众软件
小众软件
Microsoft Security Blog
Microsoft Security Blog
L
LangChain Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
aimingoo的专栏
aimingoo的专栏
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
腾讯CDC
Recent Announcements
Recent Announcements

Dallas Lu

一些没有意义的事情 博客程序的一次大重构 OpenWRT 使用 udp2raw 对抗 WireGuard 阻断 如何证明你是原创作者 Wildcard-domajna agordo en Nginx: riskoj kaj kontraŭrimedoj WISeID S/MIME 证书 V2EX 刑满释放记 使用 Radicale 在 Ubuntu 24.04 中搭建 vCards CardDav 服务 邮件服务的域名成功从 SURBL 黑名单移除 网站多语言的设计细节 网站评论系统的目前进展和展望 在公网使用 iptables 转发端口时保留客户端 IP 邮件投递平台 Postal 的使用经验 自建 Postal 完美替代 SendGrid 互联网在崩塌吗,然后呢 在 SvelteKit 应用中使用 JSON-LD 网页的打印样式应该怎么写 “茴字的四种写法”之 IP 与域名 怎么伪造 Git 提交的时区 供大众交流的论坛和其它替代产品还是不好用
Nginx 泛域名配置的隐患与对策
达拉斯・卢 · 2025-05-23 · via Dallas Lu

一个网站的程序支持多站点模式,于是为了方便,在 Nginx 中绑定了域名 *.example.com,但奇怪的是,访问记录中总是有一些奇怪的二级域名,让人隐约觉得不妥。本文记述使用 Nginx 的来规避类似的风险。

Wildcard 域名绑定

原本的 Nginx 大约如下:

server {
    listen 80;
    server_name *.example.com;

    // blabla
}

这样配置简单方便。不过如果你的网站并不是多站点模式,或者网站程序未能恰当地处理,会导致任意二级域名却都能正常访问网站内容,是有点奇怪的。也许有人故意构造并发布了一个链接,不幸又被爬虫抓取,想想一下,从这个流量来源过来的访客们,纷纷皱着眉头看着你的奇怪的二级域名。这是一个未预期的行为,会影响 SEO,且有一定的合规风险,应当采取一些措施。

如果二级站点数量有限,可以手动列出全部二级域名:

server {
    listen 80;
-    server_name *.example.com;
+    server_name
+        www.example.com
+        cdn.example.com
+        api.example.com;
}

倘若你有更复杂的需要,仍需要保留 *.example.com 以简化配置,那么就可以加一个白名单的校验。

在 Nginx 中 $server_name 代表的是 server_name 配置中的第一个值,因此将网站的主域名放在第一位就非常方便后续处理。

如果你曾经使用过 blog.example.com,如今回归到 www.example.com, 那么更合适的办法应该是重定向。而其他未预期的域名都可以使用 return 444; 来直接断开连接。

server {
    listen 80;
-    server_name *.example.com;
+    server_name www.example.com example.com *.example.com;
+
+    if ($host ~* ^((blog|feed|log)\.example\.com|example.com)$) {
+        return 307 $scheme://$server_name$request_uri;
+    }
+
+    if ($host !~* ^(cdn|api)\.example\.com$) {
+        return 444;
+    }
}

默认主机

除了泛域名绑定之外,默认主机也会有类似的问题。如果在中国大陆,有未备案的域名指向到你的服务器,也有监管的风险。直接让默认主机断开所有连接:

server {
    listen 80 default_server;
    server_name _;

    return 444;
}

在某些情况下,你可能需要在阻断未指定域名的同时,支持使用 IP 地址直接访问:

-return 444;
+if ($host !~* "^((?:\d{1,3}\.){3}\d{1,3}|(?:[a-fA-F0-9:]+))$") {
+    return 444;
+}

SNI 域名泄露

在尝试 HTTPS 访问时,即便 Nginx 断开了连接,但客户端仍能够从返回的证书中获取 SNI 域名列表。很多人使用 CDN 全站加速来隐藏真实 IP,被扫描 IP 时,可能就会泄漏服务器与域名的关联。

可以在 server 块中配置,拒绝 ssl 握手:

+ssl_reject_handshake on;

如果你还是希望能够支持使用 IP 地址直接访问……那么,要使用一个 IP 专用的 SSL 证书。IP 证书购买渠道有限,自签一个也是可以的。

生成一个 IPv4 地址的证书:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout ip.key -out ip.crt \
  -subj "/CN=1.2.3.4" \
  -addext "subjectAltName=IP:1.2.3.4"

如果你还希望支持 IPv6……建立自签证书的配置 openssl-san.conf

[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
CN = (ip or domain or keep bank)

[v3_req]
subjectAltName = @alt_names

[alt_names]
IP.1 = 1.2.3.4
IP.2 = 2001:db8::1

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout ip-mixed.key -out ip-mixed.crt \
  -config openssl-san.conf

然后配置 Nginx:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    server_name _;

    ssl_certificate /path/to/ip.crt;
    ssl_certificate_key /path/to/ip.key;

    if ($host !~* "^((?:\d{1,3}\.){3}\d{1,3}|(?:[a-fA-F0-9:]+))$") {
        return 444;
    }
}

避免 CDN 域名被收录

    root /path/to/webroot/www.example.com;

    location = /robots.txt {
        default_type text/plain;

        if ($host = 'cdn.example.com') {
            #return 200 "User-agent: *\nAllow: /*.png$\nDisallow: /\n";
            alias /path/to/webroot/www.example.com/robots-cdn.txt;
        }
    }