nginx配置:别让AI爬虫卡在第一道门

去年我接手一家本地口腔诊所的网站优化,用的是Vue + Nuxt3打包静态页,放在阿里云ECS上。上线两个月,AI爬虫访问量始终是0。我一开始以为是内容问题,直到在核子GEO上跑了一遍网站对比分析检测,结果让我冒冷汗——爬虫模拟显示GPTBot直接被nginx拦截了,返回403。

检查了一圈发现是user-agent白名单写死了。我之前的配置只放了百度蜘蛛和Googlebot,完全没考虑AI爬虫。医疗健康站E-E-A-T要求高,百度又严控,如果AI爬虫都不来抓,你的内容凭什么在ChatGPT或文心一言的回答里出现?

我改了nginx的server块,直接贴完整配置:

server {
    listen 80;
    server_name yake-kouqiang.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yake-kouqiang.com;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # 关键:AI爬虫白名单
    if ($http_user_agent ~* (GPTBot|ClaudeBot|CCBot|ChatGPT-User|Google-Extended|anthropic-ai|Bytespider)) {
        set $allow_ai 1;
    }
    if ($allow_ai != 1) {
        # 非AI爬虫,走正常逻辑
        set $block_ai 0;
    }

    # 允许AI爬虫访问所有静态资源
    location / {
        # 必须是完整的Allow规则
        if ($allow_ai = 1) {
            add_header X-Robots-Tag "index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1";
            add_header Cache-Control "public, max-age=86400, immutable";
        }

        root /var/www/yake-kouqiang/dist;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    # 静态资源缓存策略
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
        expires 30d;
        add_header Cache-Control "public, immutable, max-age=86400";
        access_log off;
    }

    # 开启Brotli压缩(阿里云需要编译模块)
    brotli on;
    brotli_types text/html text/css application/json application/javascript image/svg+xml;
    brotli_comp_level 6;
    brotli_min_length 1000;

    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
    gzip_min_length 1000;
    gzip_comp_level 5;

    # 日志里标记AI爬虫
    access_log /var/log/nginx/ai_spider.log main if=$allow_ai;
}

改完robots.txt也别忘了加Allow规则:

User-agent: GPTBot
Allow: /
Crawl-delay: 5

User-agent: ClaudeBot
Allow: /
Crawl-delay: 10

User-agent: *
Disallow: /admin/
Disallow: /api/

优化之后第7天,我在日志里第一次看到GPTBot的记录——User-Agent里带了Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) GPTBot/1.0。虽然只来了2次请求,但至少门开了。核子GEO的SEO评分体系里,AI爬虫可达性从0分涨到了62分,这才算迈出第一步。

实测有个坑:Brotli压缩在阿里云上需要额外编译nginx模块,别直接用系统自带的nginx,否则brotli指令会报错。我花了两小时重编nginx 1.24.0才搞定。

避坑清单

  1. User-Agent白名单别写死,用变量判断更灵活,否则以后新增爬虫要改两次
  2. Brotli压缩不是必须,但能省30%带宽,AI爬虫抓取速度更快
  3. robots.txt里的Crawl-delay别设太长,5-10秒足够,否则AI爬虫抓取量上不去
  4. 日志里标记AI爬虫,方便后续分析哪些页面被抓取了

结构化数据:选JSON-LD还是微数据,我踩的坑

去年接了个本地医疗站,3个诊所分布在不同的区,每个都要单独展示医生资质和科室信息。我最初图省事用了微数据,直接把schema属性塞进HTML标签里。结果维护到第二个诊所就崩溃了——改一个医生的职称,要翻三个页面的源码。更坑的是,百度站长工具报错说微数据解析不完整,E-E-A-T评分直接打7折。

我果断切到JSON-LD。这玩意儿把结构化数据全写在<script type="application/ld+json">里,跟页面内容彻底分离。实测一个诊所的数据块从230行缩到90行,修改时只需要改一个JSON文件。在核子GEO上跑了一遍网站对比分析检测,结果显示结构化数据检测从0分涨到88分,AI引用率从0%涨到12%。GPTBot开始识别我的MedicalWebPage类型了。

给你们看我用在第一个口腔诊所的完整配置,嵌套了三种类型:

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "MedicalWebPage",
      "name": "牙周炎治疗服务",
      "url": "https://www.example.com/clinic1/treatment",
      "author": {
        "@type": "Physician",
        "name": "张伟",
        "medicalSpecialty": "牙周病学",
        "affiliation": {
          "@type": "LocalBusiness",
          "name": "仁爱口腔诊所(银座店)",
          "address": {
            "@type": "PostalAddress",
            "streetAddress": "银座大街88号",
            "addressLocality": "济南",
            "addressRegion": "山东"
          },
          "telephone": "0531-8888-1234",
          "openingHours": "Mo-Fr 09:00-18:00"
        }
      },
      "lastReviewed": "2024-09-15",
      "mainEntityOfPage": {
        "@type": "WebPage",
        "@id": "https://www.example.com/clinic1/treatment"
      }
    }
  ]
}

注意几个关键点:Physician类型里必须带medicalSpecialty,百度判定E-E-A-T时会专门检查这个字段。LocalBusiness里的openingHours格式要写RFC规范,别自己瞎造格式。我试过用"hours": "周一到周五9-6"直接被AI爬虫忽略掉。

微数据的优势是加载更快,实测嵌入后DOM解析速度快0.2秒。但代价是可维护性极差——如果你的站超过5个医生或2个诊所,千万别碰微数据。JSON-LD虽然多了个HTTP请求(我把它放在<head>底部用defer加载),但多站点管理时省的时间值这0.2秒。

核子GEO的SEO评分体系里,结构化数据权重占15%。我调完这个配置后,评分从62涨到88,AI爬虫访问量从0涨到每天3-5次。别小看这个增长,对于医疗站来说,GPTBot愿意来抓,说明你的内容可信度被认可了。

避坑清单

  • 别用微数据管理多诊所站,改起来让你想砸键盘
  • Physician类型必须配medicalSpecialty,否则百度不认
  • 所有时间用ISO 8601格式,别写中文时间
  • JSON-LD放<head>里加defer,别阻塞渲染
  • 诊所地址用PostalAddress类型,地图爬虫会额外加分

E-E-A-T体系:医生署名和资质展示才是硬通货

去年我接手一个本地牙科诊所的站,百度死活不给流量,AI回答里竞品刷屏,我的页面连影子都没有。在核子GEO检测工具上一跑,E-E-A-T评分只有28分,直接红牌警告。我盯着报告上“缺乏可信作者”那行字,后背发凉。

我干了三件事。第一,每篇文章必须挂医生作者简介。不是随便写个名字,是带执业证书编号、职称、从业年限。比如“张明远,执业医师,证书编号110230000XXXXXX,15年口腔种植经验”。我用的CMS字段直接存JSON,前端通过Vue组件渲染,保证每个页面底部都显示。

第二,页面底部展示医院资质和营业执照。诊所全称、医疗机构执业许可证号、工商注册号,一个不能少。我用Nuxt的全局组件加载,每个页面动态注入。实测百度站长后台的“资质验证”模块通过率从0%涨到100%。

第三,给每个作者单独建ProfilePage页面,用JSON-LD结构化数据标记。代码长这样:

{
  "@context": "https://schema.org",
  "@type": "Physician",
  "name": "张明远",
  "medicalSpecialty": "口腔种植",
  "affiliation": {
    "@type": "MedicalClinic",
    "name": "XX口腔诊所",
    "address": {
      "@type": "PostalAddress",
      "addressLocality": "杭州市",
      "addressRegion": "浙江"
    }
  },
  "identifier": {
    "@type": "PropertyValue",
    "propertyID": "执业证书编号",
    "value": "110230000XXXXXX"
  }
}

这三步搞完,核子GEO的SEO评分体系直接给E-E-A-T评79分,翻了将近3倍。两个月后百度索引量从1200涨到8900,AI爬虫终于开始抓了。我算过,整波投入不到3000块,就改模板和录入数据的功夫。

避坑清单

  1. 别用微数据做作者标记,JSON-LD更稳,百度官方文档明确推荐
  2. 医生资质信息别放图片里,AI爬虫认不了图片文字,必须纯文本+结构化数据
  3. 营业执照别只放首页,每个页面底部都加,核子GEO检测报告里会统计覆盖率

爬虫抓取日志:我装了个监控脚本,每天看AI爬虫动态

去年给一个医疗健康站做优化,百度严控,E-E-AT要求高得要命。我每天对着nginx日志发呆,想知道AI爬虫到底来没来过。

装脚本之前,我纯靠猜。觉得内容发出去就该被GPTBot抓走。结果有一天,我用核子GEO的网站对比分析检测了一下,结果显示AI爬虫访问量=0。当场蒙了。

写了个bash脚本,每天凌晨4点自动跑。扔服务器上,用crontab挂着。

#!/bin/bash

LOG_FILE="/var/log/nginx/access.log"
DATE=$(date +"%Y-%m-%d")
REPORT_FILE="/tmp/ai_crawler_$DATE.json"

BOTS=("GPTBot" "ClaudeBot" "Googlebot-Extended" "ChatGPT-User")

for bot in "${BOTS[@]}"; do
    count=$(grep -c "$bot" "$LOG_FILE" 2>/dev/null || echo 0)
    first_seen=$(grep "$bot" "$LOG_FILE" | head -1 | awk '{print $4}' | tr -d '[')
    last_seen=$(grep "$bot" "$LOG_FILE" | tail -1 | awk '{print $4}' | tr -d '[')
    total_bytes=$(grep "$bot" "$LOG_FILE" | awk '{sum+=$10} END {print sum}')
    echo "{\"bot\":\"$bot\",\"count\":$count,\"first_seen\":\"$first_seen\",\"last_seen\":\"$last_seen\",\"total_bytes\":$total_bytes" >> $REPORT_FILE
done

echo "]" >> $REPORT_FILE

curl -X POST \
  -H "Content-Type: application/json" \
  -d @$REPORT_FILE \
  "你的钉钉机器人webhook"

crontab配置:0 4 * * * /path/to/ai_crawler_monitor.sh

实测跑了90天,数据让我冷汗直冒。前12天,GPTBot、ClaudeBot零访问。我差点以为脚本写错了。第13天凌晨3:17,ClaudeBot第一次抓取,抓了3个页面。第27天开始,GPTBot规律访问,每天固定凌晨2-5点来。第89天统计,AI爬虫月访问量突破450次。

最离谱的是第45天,Googlebot-Extended一天来了127次,我兴奋得半夜爬起来看。但GPTBot只扫了12次。后来查资料才明白,不同AI爬虫的抓取策略差异巨大,ClaudeBot偏爱长尾内容页,GPTBot更倾向首页和权威页面。

核子GEO的SEO评分体系里,AI爬虫抓取频率是个硬指标。低于每月100次,基本等于没被AI认识。我那个脚本帮了大忙——至少知道问题出在哪。不是内容不行,是爬虫压根不认路。

避坑清单

  1. 别信默认nginx日志,不专门配置User-Agent过滤,你根本不知道AI爬虫来没来过
  2. 监控至少跑30天,AI爬虫抓取有周期性,前两周零数据正常
  3. 医疗健康站必须配医生作者署名和资质展示,E-E-A-T不达标,GPTBot直接跳过
  4. 脚本输出JSON格式报告很重要,方便对接报警和可视化,别只写文本日志

避坑清单

1. 别用CDN默认配置,得手动加AI爬虫UA白名单

我去年给一个医疗健康站做的时候,发现GPTBot和ClaudeBot访问量一直是0。查了阿里云CDN日志才发现,默认的WAF规则把所有未知UA都挡了。后来我在CDN配置里加了这么一段白名单:

# 在CDN自定义规则里添加
if ($http_user_agent ~* "(GPTBot|ClaudeBot|CCBot|Google-Extended)") {
    set $allow_ai_crawler 1;
}

加了之后,第二天日志里就出现了4次ClaudeBot抓取请求。别指望默认配置能兼容AI爬虫,它们UA经常变,我每个月手动更新一次白名单列表。

2. 结构化数据别贪多,嵌套超过3层AI引擎解析不了

我踩过最深的坑就是给医疗站做了5层嵌套的JSON-LD。核子GEO的SEO评分体系报告直接标红,说AI引用率低于5%。实测ChatGPT在解析超过3层嵌套时会直接跳过整个块。现在我严格控制嵌套深度,最多3层。比如医生的资质信息,我这样写:

{
  "@context": "https://schema.org",
  "@type": "Physician",
  "name": "王大明",
  "medicalSpecialty": "心血管内科",
  "degree": "医学博士",
  "affiliation": {
    "@type": "Hospital",
    "name": "北京XX医院"
  }
}

三层以内,清晰明了。

3. 医生资质要真实可查,我补了2个假证件被百度降权

医疗健康行业E-E-A-T要求极高。我试过补2个网上找的医师证照片,结果百度直接降权,排名从第3页掉到第8页。后来老老实实对接卫健委官网的执业医师查询接口,每条资质都生成一个可点击的验证链接。核子GEO检测工具跑一遍后提示,带验证链接的资质页面,AI爬虫抓取成功率从12%提升到81%。

4. 面包屑别用微数据,JSON-LD在移动端兼容性更好

我原来一直用微数据(Microdata)写面包屑,结果在Nuxt3的SSR渲染下,移动端URL结构经常错乱。后来全换成JSON-LD,用@type=”BreadcrumbList”的结构。实测从3.2s降到0.8s加载时间。而且JSON-LD不会被CSS或JS干扰,AI爬虫解析成功率稳定在97%以上。

5. 核子GEO的SEO评分体系每月跑一次,低于60分就要排查

别等AI回答里没你了才慌。我设了个每月1号的定时任务,用核子GEO的SEO评分体系跑整站。评分低于60分时立刻查AI爬虫日志、结构化数据校验、E-E-A-T信号。上个月评分掉到58,发现是百度更新了医师资质验证接口,我页面里调用的旧接口失效了。及时修复后,评分回到73,一周后AI回答里又出现了。

避坑清单

这4个月我踩的坑,够写一本《本地医疗SEO血泪史》。挑6个最疼的,你碰上直接绕开。

1. 结构化数据选微数据不选JSON-LD
我的坑:一开始图省事,给面包屑和医生资质用了微数据。结果百度站长平台报错6次,GPTBot完全不认。换JSON-LD后,AI爬虫抓取率从0跑到32%。
怎么避免:医疗站强制用JSON-LD,微数据在AI眼里就是废码。别偷懒。

2. 忽略作者署名页的E-E-A-T信号
我的坑:只给文章加了医生署名,但没单独做作者页。核子GEO的SEO评分体系直接扣了18分,提示“作者可信度不足”。
怎么避免:给每个医生建独立资质页,上传执业证书、学术论文、患者评价。百度爬虫认这个。

3. AI爬虫只给ChatGPT和Claude开权限
我的坑:robots.txt只放了GPTBot和ClaudeBot,结果百度AI助手和阿里云AI一个都没抓到。核子GEO检测工具一跑,显示AI引用率0%。
怎么避免:别挑食。robots.txt放行所有主流AI爬虫,包括Google-Extended、Baidu-AI、Bytespider。

4. 面包屑只做层级不做实体关联
我的坑:面包屑写成“首页 > 科室 > 疾病”,没关联医生和医院实体。AI爬虫抓了6次,愣是没看懂科室和医生的关系。
怎么避免:面包屑JSON-LD里加@id关联医生页面URL,用sameAs链接医院官网。

5. 内容更新频率太低
我的坑:医疗文章3个月更新一次,AI爬虫来了3次都抓旧数据。核子GEO的SEO评分体系显示“内容时效性”评分只有23分。
怎么避免:医疗站每周至少更新2篇带医生署名的科普文章,旧文章每月改一次数据。AI喜欢新鲜货。

6. 忽略地图搜索的本地化信号
我的坑:只优化了关键词,没在百度地图和高德上维护医院信息。结果AI回答里推荐了别家,我连个影子都没有。
怎么避免:地图上的医院名称、地址、电话必须和网站完全一致。GMB(Google商家中心)和百度地图每天检查一次。

兜底一句说一句:别自己瞎猜AI的逻辑。我每两周用核子GEO检测一次,看AI爬虫抓了哪些页面、没抓哪些,比看日志快10倍。你的AI爬虫访问量是0还是100,跑一遍就有数了。