1. 项目概述从“门户大开”到“精确定位”的安全加固最近在排查服务器日志时发现一个挺有意思的现象除了正常的域名访问总有一些请求是直接通过服务器的IP地址过来的。这些请求五花八门有的是扫描器在探测漏洞有的是一些自动化脚本在尝试访问默认页面甚至还有些请求试图访问一些不存在的路径。这让我意识到我们的Nginx服务虽然配置了域名但服务器本身就像一栋大楼正门域名有门卫但后门IP地址却敞开着。这不仅是资源浪费更是一个潜在的安全隐患。任何一个知道服务器IP的人都可以绕过我们精心配置的域名解析、SSL证书甚至是一些基于域名的访问控制规则。与此同时另一个老生常谈但至关重要的问题也浮出水面防盗链。我们辛辛苦苦生产的内容无论是图片、视频还是文档如果被其他网站直接引用不仅会消耗我们宝贵的服务器带宽和资源还可能导致内容被滥用影响自家网站的访问体验和SEO。想象一下你网站上一张高清大图被一个流量巨大的论坛直接贴了过去每次论坛用户查看流量都算在你的服务器头上这谁顶得住所以今天要聊的就是这两个看似独立实则都关乎服务器“边界安全”和“资源主权”的实战配置禁止通过IP直接访问强制所有流量必须通过指定域名进入以及配置有效的防盗链策略保护我们的静态资源不被盗用。这两个操作是每一位服务器管理员在基础安全加固时都应该完成的“标准动作”。无论你是运维工程师、后端开发者还是个人站长掌握它们都能让你的服务更健壮、更经济。2. 核心需求与方案设计解析在动手修改配置文件之前我们得先想清楚为什么要这么做以及怎么做才最合理、最稳妥。盲目复制粘贴配置代码是运维工作的大忌理解背后的逻辑才能应对各种复杂场景。2.1 为何要禁止IP访问只允许域名访问禁止IP访问的核心目的有三个安全、管理和成本。安全加固这是最主要的原因。直接通过IP访问会暴露服务器的真实身份让攻击者更容易进行指纹识别和漏洞扫描。许多自动化攻击工具的第一阶段就是扫描IP段。此外一些基于域名的安全策略如WAF的某些规则、应用层的访问控制会因此失效。禁止IP访问相当于隐藏了服务器的“后门”。统一入口与管理现代Web服务常常使用反向代理、负载均衡和CDN。一个服务器可能承载多个网站虚拟主机。通过IP访问无法区分具体是哪个网站Nginx会返回默认主机或者第一个匹配的主机这可能导致用户访问到错误的内容或者暴露出不该暴露的测试站点。强制域名访问确保了流量路由的精确性。避免不必要的麻烦与成本一些恶意的扫描、爬虫会直接攻击IP。禁止后可以减少无效流量降低服务器负载和带宽消耗。同时也能避免因为IP访问导致搜索引擎收录了错误地址影响SEO。方案设计思路实现这个目标通常有两种主流方法各有优劣。方法一默认服务器返回错误码。这是最常用、最推荐的方法。我们配置一个特殊的“默认服务器”监听80和443端口但不对应任何有效域名。所有通过IP发起的请求都会被这个默认服务器捕获然后直接返回一个错误如444、403或500或者重定向到我们的主域名。方法二基于域名的条件判断。在每个server块中通过判断$host变量即请求头中的Host字段是否为IP地址来决定是正常处理还是拒绝。这种方法更灵活可以针对不同站点做不同处理但配置稍显繁琐。考虑到通用性和简洁性本文将重点讲解方法一这也是社区和官方文档中公认的最佳实践。2.2 防盗链的原理与策略设计防盗链的本质是校验请求的来源。当浏览器从一个网页例如www.other-site.com中加载一张图片例如http://your-site.com/image.jpg时浏览器会在请求头中携带一个Referer字段注意拼写是Referer不是Referrer其值通常是图片所在网页的URL即http://www.other-site.com/page.html。Nginx的防盗链模块ngx_http_referer_module就是通过检查这个Referer字段来实现的。我们可以设定一个规则只允许来自特定域名即我们自己的网站的请求访问图片等静态资源其他来源的请求一律拒绝或重定向。方案设计思路确定保护范围通常我们需要保护的是消耗带宽较大的静态资源如图片.jpg,.png,.gif、视频.mp4,.flv、样式表.css、脚本.js以及文档.pdf,.zip等。我们会通过文件扩展名来匹配这些资源。定义合法来源哪些Referer是合法的通常包括我们自己的主域名www.yourdomain.com。可能还有我们的其他子域名cdn.yourdomain.com,img.yourdomain.com。空Referer。这里需要特别注意当用户直接在浏览器地址栏输入资源URL或者从本地文件、HTTPS页面跳转到HTTP页面安全降级时Referer头可能为空或被浏览器移除。我们需要根据实际情况决定是否允许空Referer访问。选择拒绝后的行为对于非法请求是直接返回403禁止访问还是返回一张替代图片如“请勿盗链”的提示图或者重定向到另一个URL这取决于你的需求。返回403最简单消耗资源最少返回替代图片用户体验稍好但多一次请求重定向则可能带来循环风险需谨慎使用。我们将采用“返回403”和“返回替代图片”两种方式作为示例。3. 实战配置禁止IP访问让我们进入实战环节。假设我们有一个域名www.example.com服务器IP是192.168.1.100。我们希望所有通过http://192.168.1.100或https://192.168.1.100的访问都被拒绝或重定向。重要提示在修改任何Nginx配置前请务必先备份原始配置文件通常是/etc/nginx/nginx.conf或/etc/nginx/sites-available/下的文件。使用nginx -t命令测试配置语法是否正确确认无误后再nginx -s reload重载配置。3.1 配置默认Server块返回错误这是最优雅和高效的方法。Nginx在处理请求时会首先寻找与请求的Host头完全匹配的server_name。如果没找到就会使用监听相同IP和端口的默认服务器即该端口下第一个定义的server块或者显式标记为default_server的块。我们的策略就是显式定义一个default_server它不响应任何有效域名专门用于“兜底”处理非法请求如IP访问。打开你的Nginx配置文件例如/etc/nginx/sites-available/default或你的自定义站点配置在文件的最顶部或最前面添加如下server块# 禁止IP访问的默认服务器配置 server { listen 80 default_server; listen [::]:80 default_server; server_name _; # 使用下划线匹配所有未定义的域名也可以写一个不存在的域名 # 方案A直接关闭连接最节省资源 # 444是Nginx定义的一个非标准状态码用于无条件关闭连接不发送任何响应头。 # return 444; # 方案B返回403禁止访问更符合HTTP语义便于日志分析 return 403; # 方案C重定向到主域名对用户最友好但会暴露主域名并产生一次重定向开销 # rewrite ^(.*)$ http://www.example.com$1 permanent; # 或者使用return 301 # return 301 http://www.example.com$request_uri; access_log off; # 可选关闭此默认server的访问日志减少日志噪音 error_log /var/log/nginx/default_server_error.log; # 可选将错误日志单独存放 }配置详解与选择建议listen 80 default_server;关键在这里的default_server参数。它明确声明这个server块是80端口的默认服务器。server_name _;下划线是一个无效的域名用于匹配所有未能被其他server块匹配的请求。你也可以写一个像invalid.example.com这样绝不存在的域名。方案Areturn 444最彻底Nginx直接断开TCP连接不发送任何HTTP响应。客户端会收到一个连接重置的错误。这种方式最节省服务器资源但不利于在日志中区分是恶意扫描还是普通错误。方案Breturn 403返回标准的“403 Forbidden”错误。这是最推荐的做法因为它符合HTTP协议在Nginx访问日志中会留下403状态码记录便于后期分析和监控。消耗的资源微乎其微。方案C重定向将IP访问永久重定向301到你的主域名。这对误操作的用户很友好但有两个缺点1. 暴露了你的主域名2. 每次IP访问都会产生一次额外的重定向请求增加开销。一般不建议在生产环境使用除非你有特殊的SEO或迁移需求。access_log off;由于这个server块处理的都是“无效”请求关闭其访问日志可以显著减少日志文件体积提升性能。但如果你需要审计这些非法访问可以保留日志。实操心得 我个人的生产环境一律使用方案Breturn 403。原因很简单日志清晰。当我在监控告警中看到某个IP短时间内产生大量403错误时我就能立刻知道它在尝试直接访问IP可以结合其他安全手段如Fail2ban将其加入黑名单。而444状态码在Nginx标准日志模块中记录为000不方便筛选。3.2 处理HTTPSSSL端口如果你的网站启用了HTTPS那么对443端口也需要进行同样的配置。注意SSL证书是基于域名颁发的通过IP访问HTTPS本身就会因为证书域名不匹配而导致浏览器警告。但为了更严格的控制我们依然可以配置一个默认的SSL服务器。server { listen 443 ssl default_server; listen [::]:443 ssl default_server; server_name _; # 即使这里配置一个假的或通配符证书浏览器也会因为域名不匹配而告警。 # 但我们可以更早地在Nginx层面拒绝避免走到SSL握手和证书验证环节。 # 这里需要随便配置一个SSL证书否则Nginx启动会报错。 ssl_certificate /path/to/any/dummy.crt; # 一个自签名的假证书即可 ssl_certificate_key /path/to/any/dummy.key; return 403; # 或者 return 444; access_log off; }注意配置SSL默认服务器需要提供一个证书和密钥文件即使它们是无效的。你可以用OpenSSL快速生成一个自签名的假证书用于这个目的。这确实有点麻烦但能确保Nginx在SSL监听端口上有一个完整的server块来处理非法请求。3.3 验证配置效果配置保存并重载Nginx后进行测试在浏览器中直接输入你的服务器IP地址如http://192.168.1.100。你应该看到“403 Forbidden”页面如果你选择方案B。使用curl命令测试会更清晰curl -I http://192.168.1.100你应该收到类似这样的响应头HTTP/1.1 403 Forbidden Server: nginx/1.18.0 Date: ... Content-Type: text/html Content-Length: 153 Connection: keep-alive同时确保通过域名访问http://www.example.com一切正常。4. 实战配置Nginx防盗链设置防盗链配置通常放在处理静态资源的location块中例如匹配图片、视频的路径。4.1 基础防盗链配置返回403假设我们的静态资源都存放在/var/www/html/static/目录下或者通过/images/,/videos/这样的URL路径访问。在你的主站点的server块内例如server_name www.example.com;的那个块添加如下配置location ~* \.(jpg|jpeg|png|gif|ico|css|js|mp4|flv|pdf|zip)$ { # 定义合法的Referer来源 valid_referers none blocked server_names *.example.com example.com ~\.google\. ~\.baidu\. ~\.bing\.; # 允许搜索引擎爬虫可选 # 如果Referer不合法则执行if块内的内容 if ($invalid_referer) { return 403; # 或者返回一张自定义的错误图片 # rewrite ^ /path/to/anti-leech.jpg last; } # 合法的请求继续正常处理访问原始资源 # 这里可以加上缓存头等优化配置 expires 30d; add_header Cache-Control public, immutable; }配置详解location ~* \.(jpg|jpeg|png|gif|ico|css|js|mp4|flv|pdf|zip)$ {这是一个不区分大小写~*的正则表达式匹配匹配所有以列出的扩展名结尾的请求。valid_referers指令后面跟着一系列参数定义合法的Referer。none允许Referer头为空的请求。这是一个关键决策点。如果允许意味着用户直接输入图片链接、从书签打开、或从HTTPS页面链接到HTTP资源浏览器出于安全会移除Referer都能访问。对于纯内容站建议不允许去掉none以最大限度防盗。对于用户体验优先的站如图床可能需要允许。blocked允许Referer头存在但值被防火墙或代理移除通常为空值或http://的请求。通常和none一起使用。server_names允许server_name中定义的域名。*.example.com example.com明确列出允许的域名支持通配符*。~\.google\. ~\.baidu\.使用正则表达式匹配这里示例是允许来自Google和百度搜索引擎的爬虫。这有助于你的图片等资源能被搜索引擎正常收录和展示。if ($invalid_referer) {内置变量$invalid_referer在Referer非法时为1合法时为0。我们在这个判断块内处理非法请求。return 403;最简单的处理方式直接拒绝。expires 30d;为合法请求设置浏览器缓存时间这是良好的性能实践。4.2 进阶配置返回自定义防盗链图片直接返回403对用户不太友好他们可能只看到一个破碎的图片图标。我们可以选择返回一张提示“请勿盗链”的图片。首先准备一张提示图片例如anti-leech.jpg上传到服务器某个路径比如/var/www/html/anti-leech.jpg。然后修改配置location ~* \.(jpg|jpeg|png|gif|ico)$ { valid_referers none blocked server_names *.example.com example.com; if ($invalid_referer) { # 重写请求的URI到我们的提示图片 rewrite ^ /anti-leech.jpg last; # 注意这里重写后会跳出当前location去匹配能处理 /anti-leech.jpg 的location。 # 你需要确保根目录下的 /anti-leech.jpg 是可以公开访问的。 } # 为原始图片设置缓存等 expires 30d; } # 确保防盗链提示图本身不被防盗链规则拦截 location /anti-leech.jpg { # 这个location专门用于服务提示图片不设置valid_referers expires 1h; # 可以设置一个较短的缓存时间 add_header Cache-Control public; }配置详解当检测到盗链时使用rewrite规则将请求内部重定向到/anti-leech.jpg。关键点必须为提示图片本身/anti-leech.jpg单独设置一个location块并且不要在这个块里设置valid_referers。否则当盗链网站加载这张提示图时又会因为Referer不合法而再次触发重定向陷入死循环最终可能返回502错误。location /anti-leech.jpg中的表示精确匹配优先级最高确保对这张图片的请求能准确命中这个规则。4.3 针对特定目录的防盗链如果你的静态资源有统一的路径前缀比如所有图片都在/uploads/目录下配置会更简洁location /uploads/ { valid_referers none blocked server_names *.example.com example.com; if ($invalid_referer) { return 403; } # 其他优化配置... expires 30d; }这种方式避免了繁琐的正则匹配性能稍好也更易于管理。5. 常见问题、排查技巧与高级策略配置完成后并非一劳永逸。在实际运行中你可能会遇到各种预期之外的情况。5.1 问题排查实录问题1配置了禁止IP访问但通过IP依然能访问到我的网站可能原因A你的主站点server块监听指令也包含了default_server参数。检查你的主站点配置listen 80 ...;后面是否无意中写了default_server。如果有删除它确保只有我们专门设置的那个“挡板”server块拥有default_server。可能原因B配置未生效。修改配置后是否执行了nginx -s reload或systemctl reload nginx是否先用nginx -t通过了语法测试可能原因C存在多个配置文件冲突。Nginx会读取conf.d/、sites-enabled/等目录下的所有配置文件。检查是否有其他文件定义了相同端口的default_server。排查命令# 查看Nginx如何解析你的配置确认默认服务器 nginx -T 2/dev/null | grep -A5 -B5 default_server # 或者查看所有监听80端口的server块 nginx -T 2/dev/null | grep -B10 listen 80问题2防盗链配置后我自己网站上的图片也显示不出来了可能原因Avalid_referers列表中漏掉了自己网站的关键域名或写法错误。例如你的网站用www.example.com访问但列表里只写了example.com没有www。确保列表完整。可能原因B网站页面是通过file://协议本地打开的或者是从HTTPS页面链接到HTTP资源导致Referer头为空或被阻止。如果你在valid_referers中没有包含none blocked就会失败。你需要根据业务决定是否添加它们。可能原因C使用了CDN。如果你的图片URL是CDN地址如cdn.example.com但请求到达源站时Referer头可能被CDN修改或丢失。你需要将CDN的域名如*.cdnprovider.com或特定的CDN源标识头需要CDN支持并配置加入到合法列表中。排查技巧打开浏览器的开发者工具F12切换到“网络(Network)”标签。刷新页面找到加载失败的图片请求。点击该请求查看“请求头(Request Headers)”部分找到Referer的值。核对Referer值是否完全匹配你valid_referers列表中的某一项。特别注意协议http/https、子域名www/非www、末尾斜杠等细节。问题3搜索引擎的图片搜索无法收录我的图片了原因搜索引擎爬虫抓取图片时Referer头通常是搜索站的域名如images.google.com。如果你的valid_referers没有包含这些就会被拦截。解决方案在valid_referers中添加常见搜索引擎的正则匹配如上面示例中的~\.google\. ~\.baidu\. ~\.bing\.。注意正则中的点.需要转义\.因为它代表字面量的点字符。5.2 高级策略与注意事项白名单与黑名单的权衡我们上面使用的是“白名单”模式只允许列表内的。这更安全但维护成本稍高。“黑名单”模式只拒绝列表内的可以使用if ($http_referer ~* (bad-site\.com|leech-site\.org)) { return 403; }实现但面对海量的盗链网站黑名单是防不胜防的。生产环境强烈推荐白名单模式。secure_link模块对于安全性要求极高的资源如付费视频、机密文档referer检查可以被伪造通过编程方式设置请求头。Nginx的ngx_http_secure_link_module模块可以提供更强的保护。它通过生成带有时效性和签名的URL来授权访问过期或签名无效则拒绝。这属于更高级的防盗链方案。日志分析定期检查Nginx错误日志error_log和访问日志access_log。关注403状态码的请求分析其$http_referer和$remote_addr可以帮助你发现新的盗链源或者扫描攻击IP进而完善你的安全策略。性能考量valid_referers指令和if判断对性能有轻微影响但对于现代服务器来说基本可忽略。如果担心可以将防盗链规则放在location块中而不是server块根级别确保只对必要的请求做判断。移动端与App的特殊情况一些移动端WebView或原生App在加载图片时可能不发送Referer头。如果你的内容需要被App调用可能需要与App开发团队协商让他们在请求中携带特定的标识如自定义Header并在Nginx中通过判断该Header来放行或者将App的域名/IP加入白名单。配置完这两项你的服务器就从“门户大开”变成了“精确定位”的堡垒。禁止IP访问堵住了最基础的扫描入口而防盗链则保护了你的数字资产。这些配置看似简单却是构建稳健、高效、安全Web服务不可或缺的基石。每次部署新服务时都应该像检查门窗是否锁好一样习惯性地检查这些配置是否就位。