最轻量可靠的方式是在Nginx中用location = /robots.txt精确匹配并return返回内容;需设default_type text/plain、add_header确保MIME正确;换行用LF;屏蔽特定爬虫要按“具体到泛化”顺序逐条声明User-Agent;恶意爬虫需用if+正则UA硬拦截或limit_req行为限流。

直接在 Nginx 配置里返回 robots.txt 内容是最轻量、最可靠的方式,不用碰磁盘文件,也不依赖后端或 Docker 容器内改动。
location =/robots.txt 必须用精确匹配
写成 location /robots.txt 会匹配所有以 /robots.txt 开头的路径(比如 /robots.txt.bak 或 /robots.txt/xxx),导致意外覆盖或返回错误内容。必须加等号,强制精确匹配:
location = /robots.txt {
default_type text/plain;
add_header Content-Type "text/plain; charset=UTF-8";
return 200 "User-Agent: *\nDisallow: /";
}-
default_type text/plain比text/html更准确,避免浏览器误解析 -
add_header是冗余但保险的做法——有些客户端(如旧版 curl)不信任default_type推断的 MIME 类型 -
\n必须是 Unix 换行符(LF),Windows 的\r\n会导致部分爬虫解析失败
禁止特定搜索引擎要单独声明 User-Agent
只写 User-Agent: * 无法区分对待百度、Google 等友好爬虫。如果只想屏蔽某些 bot,而允许其他爬虫(比如用于监控的健康检查 bot),就得逐条声明:
location = /robots.txt {
default_type text/plain;
return 200 "User-Agent: Baiduspider\nDisallow: /\nUser-Agent: YisouSpider\nDisallow: /\nUser-Agent: Sogou web spider\nDisallow: /\nUser-Agent: *\nDisallow: /";
}- 顺序重要:
User-Agent块必须按“从具体到泛化”排列,否则*会提前截断匹配 - 每行末尾不能有多余空格,
Disallow:后面必须跟一个空格再跟路径 - 不支持注释,任何
#或//都会被当成内容返回,破坏协议格式
if ($http_user_agent) 匹配 UA 是补充手段,不是替代方案
robots.txt 是君子协议,只对守规矩的爬虫有效;真正恶意的爬虫会直接绕过它,继续请求页面。这时需要用 if + 正则匹配 UA 字符串来硬拦截:
if ($http_user_agent ~* "Scrapy|Python-urllib|Apache-HttpClient|HeadlessChrome") {
return 403;
}- 必须加
~*(大小写不敏感),否则Googlebot和googlebot就不匹配 - 放在
server块顶层,不要嵌套在location里——Nginx 的if在 location 内行为不可靠 - 注意性能:每条请求都会执行正则,UA 列表太长(>20 项)会影响吞吐,建议先用
map提前归类
limit_req 对爬虫频率限制比 UA 匹配更有效
很多现代爬虫(比如用 Puppeteer 或 Playwright 的)UA 和真人一模一样,光靠字符串匹配毫无意义。这时候真正的防线是行为控制:
limit_req_zone $binary_remote_addr zone=spider:10m rate=2r/s;
server {
location / {
limit_req zone=spider burst=5 nodelay;
}
}-
rate=2r/s不是“每秒最多 2 次”,而是“平均每秒 2 次”,burst=5允许突发 5 次,之后才限流 - 别用
$http_user_agent当 key——UA 可伪造,$binary_remote_addr更难绕过 - 这个配置对真实用户几乎无感,但会让高并发爬虫大量触发
503或429,显著抬高其成本
最常被忽略的一点:Nginx 的 return 指令在 location = /robots.txt 中生效,但如果你用了 try_files 或 proxy_pass,且没把这段配置放在它们之前,请求就会被代理走,根本不会走到 return 这一步。


















