最近我搭建了一个个人网站,发现一个问题:网站可以通过 IP 地址直接访问。为了解决这个问题,我深入研究了 Nginx 的配置方法,以确保网站只能通过域名访问,同时防止恶意用户通过其他域名指向我的服务器 IP 来蹭流量。以下是详细的配置步骤和解决方案。
1. Nginx 配置域名及禁止直接通过 IP 访问
Nginx 的默认配置文件通常位于 /etc/nginx/sites-enabled
目录下(以 Ubuntu 系统为例)。需要注意的是,sites-enabled
目录中的文件通常是软链接,指向 sites-available
目录下的实际配置文件。这是 Nginx 的最佳实践,建议在 sites-available
中修改配置,完成后创建软链接到 sites-enabled
。不过,为了简化操作,我直接在 sites-enabled
目录下创建和修改配置文件。
以下是 Nginx 的默认配置文件示例:
# default
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
}
最简单的配置方式是直接修改这个 default
文件,添加自己的网站配置,例如:
# default
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name www.example.com;
root /path/to/www.example.com;
index index.html index.htm;
}
问题:这种方式不推荐。因为恶意用户可能通过其他域名指向你的服务器 IP,蹭你的流量,甚至养他们的域名。
推荐做法:复制默认配置文件,创建专属的网站配置文件。例如:
cp /etc/nginx/sites-enabled/default /etc/nginx/sites-enabled/example-website
然后修改新文件 example-website
:
# example-website
server {
listen 80;
listen [::]:80;
server_name www.example.com;
root /path/to/www.example.com;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
}
说明:
- 去掉
default_server
,避免该配置成为默认服务器。 server_name
设置为自己的域名。root
指向网站文件根目录。- 支持 PHP 文件处理,适用于动态网站(使用 PHP 8.1)。
禁止 IP 访问
为了禁止通过 IP 直接访问网站,可以修改默认配置文件 default
。以下是一个常见的配置方法,以及对提供的代码片段的分析和改进:
提供的代码片段
# default
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# 对直接 IP 访问返回 444 错误
if ($host ~* "^[0-9.]+$|^[0-9a-f:]+$") {
return 444;
}
}
代码分析:
- 监听端口:
listen 80 default_server
和listen [::]:80 default_server
分别监听 IPv4 和 IPv6 的 80 端口,并设置为默认服务器,处理所有未匹配到其他server
块的请求。 - server_name _:表示匹配任何未明确配置的域名或 IP 访问。
- if 条件:
if ($host ~* "^[0-9.]+$|^[0-9a-f:]+$")
使用正则表达式检查$host
变量,匹配 IPv4(如192.168.1.1
)和 IPv6(如2001:db8::1
)地址。 - return 444:返回 Nginx 特有的 444 状态码,直接关闭连接,不返回任何响应内容。这比返回 500 更高效,因为 500 会生成错误页面,而 444 完全拒绝连接。
- 问题:注释错误地提到“返回 500 错误”,与实际的
return 444
不一致,可能会误导读者。HTTP 500 表示“服务器内部错误”,不适合用于阻止 IP 访问,而 444 是更合适的选择。 - 改进建议:由于
server_name _
已确保只匹配未配置的请求(包括 IP 访问和未配置的域名),可以省略if
条件,直接使用return 444
,以提高性能:
改进后的配置
# default
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
说明:
return 444
能阻止所有未匹配的请求(包括 IP 访问和恶意域名指向),适用于所有未配置的情况。- 相比
return 500
(服务器内部错误),return 444
更适合,因为它直接关闭连接,不生成错误页面,减少服务器负载。 - 去掉
if
条件避免了 Nginx 中if
指令的潜在性能问题,尤其在高并发场景下。
2. 配置 HTTPS
对于 HTTPS 网站,需要创建一个默认的 SSL 配置文件来处理通过 IP 或未配置域名的 HTTPS 访问。例如,创建 default-ssl
文件:
# default-ssl
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate /path/to/cert/www.example.com.pem;
ssl_certificate_key /path/to/cert/www.example.com.key;
server_name _;
return 444;
}
注意:
- 必须提供有效的 SSL 证书和密钥文件,否则 HTTPS 访问会失败。
- 使用
return 444
替代return 500
,与 HTTP 配置保持一致,高效拒绝未授权访问。
正常的 HTTPS 网站配置如下,创建 example-website-ssl
文件:
# example-website-ssl
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /path/to/cert/www.example.com.pem;
ssl_certificate_key /path/to/cert/www.example.com.key;
ssl_session_timeout 30m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_prefer_server_ciphers on;
server_name www.example.com;
root /path/to/www.example.com;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
}
说明:
- 使用 TLSv1.2 和 TLSv1.3 协议以提高安全性。
- 配置合理的加密套件,禁用不安全的加密算法。
- 使用 PHP 8.1 支持动态网站。
3. 处理恶意域名指向
如果已经有恶意域名指向你的服务器 IP,可以通过重定向将这些流量导入自己的域名。修改 default
和 default-ssl
文件如下:
修改 default
文件(HTTP):
# default
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 301 https://www.example.com;
}
修改 default-ssl
文件(HTTPS):
# default-ssl
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate /path/to/cert/www.example.com.pem;
ssl_certificate_key /path/to/cert/www.example.com.key;
server_name _;
return 301 https://www.example.com;
}
说明:
return 301
表示永久重定向,将所有未匹配的 HTTP 或 HTTPS 请求重定向到你的域名。- 这不仅能防止流量被蹭,还能将流量导入你的合法域名。
HTTP 到 HTTPS 重定向
为了确保所有 HTTP 请求都重定向到 HTTPS,创建或修改 example-website
文件:
# example-website
server {
listen 80;
listen [::]:80;
server_name www.example.com;
return 301 https://$server_name$request_uri;
}
说明:
- 将 HTTP 请求永久重定向到 HTTPS,确保用户始终使用安全连接。
4. 语法错误分析
以下是原文中存在的语法错误或不推荐的配置问题,以及对新代码的检查:
-
重复的
return
语句:- 原文中
default
和default-ssl
配置同时包含return 500
和return 301
,会导致语法错误或后一个语句覆盖前一个。新代码使用单一return
语句,避免此问题。
- 原文中
-
过时的
ssl on
语句:- 原文使用了
ssl on
,在新版本 Nginx 中已弃用。新代码正确使用listen 443 ssl
。
- 原文使用了
-
PHP 版本过旧:
- 原文使用 PHP 7.0,已停止支持。新代码更新为 PHP 8.1。
-
SSL 协议配置不完整:
- 原文包含不安全的 TLSv1 和 TLSv1.1。新代码只使用
TLSv1.2 TLSv1.3
。
- 原文包含不安全的 TLSv1 和 TLSv1.1。新代码只使用
-
缺少
$request_uri
在重定向中:- 原文的 HTTP 到 HTTPS 重定向缺少
$request_uri
,可能导致路径丢失。新代码使用return 301 https://$server_name$request_uri
。
- 原文的 HTTP 到 HTTPS 重定向缺少
-
注释错误:
- 提供的代码中,注释提到“对直接 IP 访问返回 500 错误”,但实际使用
return 444
。这是一个描述错误,可能误导读者。改进后的配置修正了注释为“关闭连接”,并推荐直接使用return 444
替代if
条件,以提高性能。
- 提供的代码中,注释提到“对直接 IP 访问返回 500 错误”,但实际使用
-
使用
if
指令的性能问题:- 提供的代码使用
if ($host ~* "^[0-9.]+$|^[0-9a-f:]+$")
检查 IP 地址。虽然功能正确,但if
指令在 Nginx 中可能影响性能。改进后的配置直接使用return 444
,更高效。
- 提供的代码使用
-
未说明 Nginx 配置测试:
- 原文未提到修改配置后需运行
nginx -t
测试语法。新代码分析中已补充此建议。
- 原文未提到修改配置后需运行
5. 总结
通过以上配置,我们可以:
- 使用 Nginx 实现域名绑定,禁止通过 IP 直接访问网站(推荐使用
return 444
关闭连接)。 - 配置 HTTPS,确保安全通信。
- 通过重定向处理恶意域名指向,保护服务器流量。
- 优化配置,避免语法错误和不安全的设置。
每次修改 Nginx 配置文件后,记得运行以下命令检查语法并重启服务:
nginx -t
systemctl restart nginx
希望这些配置方法能帮助大家更好地管理自己的网站!