目录

Nginx禁止直接IP访问

nginx

...
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name <服务器公网IP>;
    return 444;
}
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    server_name <服务器公网IP>;
    ssl_reject_handshake on;
}
...

值得一提的是, 相比server_name后接服务器的IP地址, server_name _;更为常见, 虽然两种写法的作用效果相同, 但是Nginx官方文档中并不提倡使用server_name _;, 并指出:

原文
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.

Nginx v1.19.4先前版本不支持ssl_reject_handshake, 所以只能先指定证书, 再return 444

nginx

...
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name <服务器公网IP>;
    return 444;
}
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    server_name <服务器公网IP>;
    ssl_certificate /etc/nginx/ssl/default.crt;
    ssl_certificate_key /etc/nginx/ssl/default.key;
    return 444;
}
...

当服务器只托管一个网站时, 这可能是最简单的解决方案, 但并不优雅, 因为不方便后续扩展网站。

nginx

...
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name example.com;
    if ($host != example.com) {
        return 444;
    }
}
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    server_name example.com;
    if ($host != example.com) {
        return 444;
    }
}
...
警告
它将阻止访问其他子域, 比如www.example.com, blog.example.com

如果要支持更多子域, 可以使用正则表达式来执行此操作, 比如这适用于example.comwww.example.com:

nginx

    if ($host !~* ^(www\.)?example.com$) {
        return 444;
    }

显而易见, 如果有更多的子域, 就与要更复杂的正则表达式来匹配, 这很容易出错, 所以还是ssl_reject_handshake方案好。