nginx 在决定请求由哪个 server 块执行时,主要关注的是 server 块中的 listen 和 server_name 两个字段,如果根据 listen 指令无法得到最佳匹配,将会开始解析 server_name 指令。nginx 会检查请求中的"Host"头,这个值包含了客户端实际试图请求的域名或者 ip 地址。nginx 会根据这个值去匹配 server_name 指令,匹配规则会在文章中详细描述。其中有一个需要大家注意的地方是如果没有匹配到任何规则的话,则会选择可用列表中的第一个 server,带来的问题就是未绑定域名或 IP 直接访问 80 和 443 端口会给后端逻辑服务增加压力并产生不合理的错误日志,合适的解决办法是通过在 nginx 的 server 块中添加 default_server 禁止未绑定域名或 IP 访问 80 和 443 端口过滤不合理的流量。
Nginx 禁止未绑定域名或 IP 访问 80 和 443 端口实践小结
2020 年 02 月 26 日 - 初稿
阅读原文 - https://wsgzao.github.io/post/nginx-default-server/
如果根据 listen 指令无法得到最佳匹配,将会开始解析 server_name 指令.nginx 会检查请求中的"Host"头,这个值包含了客户端实际试图请求的域名或者 ip 地址.nginx 会根据这个值去匹配 server_name 指令,匹配规则如下:
示例如下:
( 1 )准确的 server_name 匹配,例如:
server {
listen 80;
server_name www.domain.com;
...
}
( 2 )以*通配符开始的字符串:
server {
listen 80;
server_name *.domain.com;
...
}
( 3 )以*通配符结束的字符串:
server {
listen 80;
server_name www.*;
...
}
( 4 )匹配正则表达式:
server {
listen 80;
server_name ~^(?.+)\.domain\.com$;
...
}
( 5 )如果以上都没有匹配,则使用 default_server.如果没有指定 default_server,则会选择第一个可用的 server.我们可以指定对于没有匹配的 host 值时,返回错误到客户端.可以用来防止别人把垃圾流量转到你的网站。
server {
listen 80 default_server;
server_name _; return 444;
}
通过返回 444 这个 nginx 的非标准错误码让 nginx 断开与浏览器的连接
Miscellaneous names
If someone makes a request using an IP address instead of a server name, the “Host” request header field will contain the IP address and the request can be handled using the IP address as the server name:
server {
listen 80;
server_name example.org
www.example.org
""
192.168.1.1
;
...
}
In catch-all server examples the strange name “_” can be seen:
server {
listen 80 default_server;
server_name _;
return 444;
}
There is nothing special about this name, it is just one of a myriad of invalid domain names which never intersect with any real name. Other invalid names like “--” and “!@#” may equally be used.
Name-based virtual servers
nginx first decides which server should process the request. Let’s start with a simple configuration where all three virtual servers listen on port *:80:
server {
listen 80;
server_name example.org www.example.org;
...
}
server {
listen 80;
server_name example.net www.example.net;
...
}
server {
listen 80;
server_name example.com www.example.com;
...
}
In this configuration nginx tests only the request’s header field “Host” to determine which server the request should be routed to. If its value does not match any server name, or the request does not contain this header field at all, then nginx will route the request to the default server for this port. In the configuration above, the default server is the first one — which is nginx’s standard default behaviour. It can also be set explicitly which server should be default, with the default_server parameter in the listen directive:
server {
listen 80 default_server;
server_name example.net www.example.net;
...
}
The default_server parameter has been available since version 0.8.21. In earlier versions the default parameter should be used instead. Note that the default server is a property of the listen port and not of the server name. More about this later.
How to prevent processing requests with undefined server names
If requests without the “Host” header field should not be allowed, a server that just drops the requests can be defined:
server {
listen 80;
server_name "";
return 444;
}
Here, the server name is set to an empty string that will match requests without the “Host” header field, and a special nginx’s non-standard code 444 is returned that closes the connection.
Since version 0.8.48, this is the default setting for the server name, so the server_name "" can be omitted. In earlier versions, the machine’s hostname was used as a default server name.
Nginx uses 'Host' header for server_name matching. It does not use TLS SNI. This means that for an SSL server, nginx must be able to accept SSL connection, which boils down to having certificate/key. The cert/key can be any, e.g. self-signed.
server {
server_name _;
listen 80 default_server;
listen 443 ssl default_server;
## To also support IPv6, uncomment this block
# listen [::]:80 default_server;
# listen [::]:443 ssl default_server;
ssl_certificate <path to cert>;
ssl_certificate_key <path to key>;
return 444; # or whatever
}
详细的配置步骤
# 未设置 default_server,会根据列表第一个 server 服务,产生垃圾流量
xxx - - [27/Feb/2020:15:17:58 +0800] "GET / HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36"
xxx - - [27/Feb/2020:15:18:24 +0800] "GET /api HTTP/1.1" 301 162 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36"
# 手动生成本地 ssl 公私钥
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt
# 增加 default_server
cat << 'EOF' > /etc/nginx/sites-available/000_default
server {
listen 80 default_server;
listen 443 ssl default_server;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
server_name _;
return 444;
access_log /var/log/nginx/000_default.access.log;
error_log /var/log/nginx/000_default.error.log;
}
EOF
# 设置软链接
ln -s /etc/nginx/sites-available/000_default /etc/nginx/sites-enabled/000_default
# reload nginx
nginx -t
nginx -s reload
# 查看日志,检查其他域名是否正常
tailf /var/log/nginx/000_default.access.log
xxx - - [27/Feb/2020:12:15:52 +0800] "GET /api HTTP/1.1" 444 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36"
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.