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"
1
wslsq 2020-02-27 22:11:47 +08:00
感谢分享
|
2
YUX 2020-02-28 01:25:21 +08:00 via iPhone
学习了
|
3
julyclyde 2020-02-28 14:34:47 +08:00
看你把一句话写成好几 K,本来我期待在最末尾有个广告链接
但是没有 失望啊差评! |
4
wsgzao OP @julyclyde #3 本来就是个人 Blog,纯粹的互联网分享,可能哪天写着写着就和大多数人一样停更了。因为是涉及技术类,换作几年前的自己可能和大多数网络上的分享一样直接写个几句话的结果就完事了,不思考不追根溯源,但是现在的知识体系都是相互交融的,既然需要建立系统化的金字塔就应该刻意多问问自己几个 Why,然后多看官网的理论解释,再实践验证结果一下就清晰了。像这篇文章最简单的一个区别就是网上有人写 default,有人写 default_server,有人加 ssl 参数又有人说不用,到底相信什么。做技术随着年龄的增长和家庭的琐事没有办法做到面面俱到总会有跟不上时代的错误认知,尽可能不给自己留坑也尽量不给别人留坑吧
|
5
neroransom 2020-02-28 15:06:35 +08:00 via Android
感谢博主
|