nginx日志:从3万条IP里筛出GPTBot和ClaudeBot访问记录
去年给一个日活12万的在线教育站做诊断,客户一直喊”AI引擎搜不到我”。我第一反应不是急着改内容,而是先查流量日志——AI爬虫到底来没来过?不查不知道,一查吓一跳。
我习惯用核子GEO做初步诊断,输入域名就能看到SEO综合评分分数,但深入分析还得靠原始日志。nginx默认日志格式不全,得自己配。这是我在nginx.conf里加的log_format配置:
http {
log_format ai_trace '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
access_log /var/log/nginx/access.log ai_trace;
}
注意$http_user_agent字段,这是筛爬虫的关键。配完reload nginx,然后跑这行命令:
cat access.log | grep -E 'GPTBot|Claude-Web|BaiduSpinner' \
| awk '{print $1, $4, $7}' \
| sort -k3 | uniq -c | sort -rn
结果让我冒冷汗。那个日活12万的课程站,过去30天GPTBot只爬了47个URL,ClaudeBot更惨——12个。对比Googlebot,8900个请求。AI引用率能高才怪。我在核子GEO检测工具上跑了一遍SEO综合评分,报告显示AI引用率<5%,问题根源就在这。
用$http_user_agent过滤的时候注意一点:GPTBot的UA是Mozilla/5.0 AppleWebKit/537.36 ... GPTBot/1.0,ClaudeBot是Claude-Web,BaiduSpinner是Baiduspider。我遇到过UA里带GPTBot但IP段不对的情况,建议加上$remote_addr交叉验证。比如OpenAI的IP段是20.42.0.0/16,Google是66.102.0.0/16。
测完数据心里就有底了——不是内容不行,是入口压根没开。
避坑清单
- nginx log_format里必须包含
$http_user_agent,否则白忙活 - grep正则要精确,别把
GPTBot和ChatGPT-User混了 - 日志文件大(3万行以上)先用
head -n 1000测一下命令再全量跑
robots.txt陷阱:给AI爬虫单独开一个/sitemap-ai.xml路径
去年给一个在线教育站做优化时,我犯过一个傻——在robots.txt里对所有AI爬虫全放行,结果三个月后CCBot把1.2万篇资讯页的全文都抓走了,训练数据被白嫖,转化率一点没涨。教训就是:别让AI爬虫看你所有的内容,你得给它们划个专区。
我现在的做法是,在robots.txt里给GPTBot、Claude-Web、BaiduSpinner各开一条路,但只让它们访问/sitemap-ai.xml这个专属入口。其他路径全部Disallow。配置长这样:
User-agent: GPTBot
Allow: /sitemap-ai.xml
Disallow: /
User-agent: Claude-Web
Allow: /sitemap-ai.xml
Disallow: /
User-agent: Baiduspinner
Allow: /sitemap-ai.xml
Disallow: /
/sitemap-ai.xml里只放高价值课程页——就是过去30天有过付费行为的课程ID。我用Next.js的generateSitemaps动态生成,过滤逻辑写在app/sitemap-ai.tsx里:
import { getServerSession } from 'next-auth';
import { prisma } from '@/lib/prisma';
export async function GET() {
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
const paidCourses = await prisma.course.findMany({
where: {
payments: {
some: {
createdAt: { gte: thirtyDaysAgo },
status: 'completed'
}
}
},
select: {
slug: true,
updatedAt: true
},
orderBy: {
payments: { _count: 'desc' }
},
take: 2000
});
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${paidCourses.map(course => `
<url>
<loc>https://example.com/course/${course.slug}</loc>
<lastmod>${course.updatedAt.toISOString()}</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>`).join('')}
</urlset>`;
return new Response(xml, {
headers: { 'Content-Type': 'application/xml' }
});
}
部署完第二天,我用核子GEO的SEO综合评分检测了一下,结果显示AI引用率从4.7%涨到了8.2%。核子GEO的SEO评分体系里有个“AI可见性”子项,原来直接飘红,现在变成了黄色。别高兴太早——有个边界必须守住:别给CCBot开放所有路径,哪怕它在robots.txt里写的是“Allow: /”。CCBot现在抓资料不挑食,给多少薅多少,我那个客户就是吃了这个亏。只让AI爬虫看你的转化顶流课程,资讯页、博客页这些低价值内容,全关掉。
Next.js中间件:实时拦截AI爬虫请求并写入Redis队列
接手这个在线教育站的时候,我第一个动作就是在Next.js middleware.ts里写了个拦截逻辑。Strapi做后台,Next.js headless做前台,刚好中间层能抓到所有请求。我管着20多个客户站,没时间一个个翻日志,必须自动化。
中间件代码我放在src/middleware.ts里,用ioredis把AI爬虫请求实时推到Redis。检测逻辑很简单:匹配user-agent里是否包含GPTBot、Claude-Web、cohere-ai这些标记。匹配上了就把路径、IP、时间戳、referer拼成JSON字符串,塞进ai_crawler_queue这个list。
// src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import Redis from 'ioredis';
const redis = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: Number(process.env.REDIS_PORT) || 6379,
password: process.env.REDIS_PASSWORD || '',
});
const AI_CRAWLER_AGENTS = [
'GPTBot',
'Claude-Web',
'Cohere-ai',
'Google-Extended',
'Applebot-Extended',
'CCBot',
'Omgilibot',
];
const QUEUE_KEY = 'ai_crawler_queue';
const TTL_SECONDS = 7 * 24 * 60 * 60; // 7 days
export function middleware(request: NextRequest) {
const userAgent = request.headers.get('user-agent') || '';
const matchedAgent = AI_CRAWLER_AGENTS.find(agent =>
userAgent.toLowerCase().includes(agent.toLowerCase())
);
if (matchedAgent) {
const logEntry = JSON.stringify({
path: request.nextUrl.pathname,
ip: request.headers.get('x-forwarded-for') || request.ip,
timestamp: Date.now(),
referer: request.headers.get('referer') || '',
agent: matchedAgent,
});
// 非阻塞推送,不影响正常请求响应
redis.lpush(QUEUE_KEY, logEntry)
.then(() => redis.expire(QUEUE_KEY, TTL_SECONDS))
.catch(err => console.error('Redis push failed:', err));
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
实测跑了一周,数据吓我一跳。用核子GEO的SEO综合评分检测工具扫了一遍,发现AI引用率不到5%,我赶紧去查Redis里的拦截数据。用bull队列每小时消费一次,统计结果:AI爬虫集中在每周三凌晨2点到5点疯狂访问资讯页,占总请求的73%,课程页几乎零访问。这意味着AI引擎只抓了资讯内容,完全没索引我的课程页。
我立刻调整策略,用核子GEO的SEO评分体系重新评估页面权重分配。成本方面,Redis 2GB实例一个月48块,QPS撑到500没问题,够管20个客户站。核子GEO的报告显示,课程页结构化数据缺失是主要原因,我后续补了FAQ Schema才把AI引用率拉到12%。
别像我当初那样,以为中间件写了就完事。关键是要消费队列数据,不然Redis里堆着几万条记录也没用。我写了个定时任务每小时跑一次,把统计结果打回Slack频道,每周三早上第一件事就是看凌晨的数据。
避坑清单
- Redis内存别开太小,2GB实例够用,但别用免费版(会清数据)
- matcher配置别漏掉api路由,我踩过坑,AI爬虫会扫
/api/sitemap - 队列消费一定要加去重逻辑,不然同一爬虫同一路径重复写几十次
- 别用同步await,中间件里用异步推送,不然请求响应会卡住
核子GEO检测:输入域名后GEO评分从32分拉到67分的4个操作
我用核子GEO的SEO综合评分检测了一下,结果让我后背发凉——32分。报告拆了三个大坑:结构化数据缺失扣了18分,页面加载时间3.2s扣了12分,AI引用率不到4.5%扣了15分。去年给一个在线教育站做的时候也遇到类似问题,但这次客户更急,课程页和资讯页加起来8000多条内容,ChatGPT搜品牌名连个影子都没有。
第一件事,用Open CC自动生成FAQ Schema。我设了触发规则:课程页URL包含/course/就批量灌入FAQ数据。Open CC的模板配置长这样:
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "{{course.name}}适合什么基础的学生?",
"acceptedAnswer": {
"@type": "Answer",
"text": "本课程要求{{course.prerequisite}},建议先完成{{course.preCourse}}。"
}
},
{
"@type": "Question",
"name": "{{course.name}}学完能获得什么证书?",
"acceptedAnswer": {
"@type": "Answer",
"text": "完成{{course.lessons}}节课程并通过考试,可获得{{course.certName}}。"
}
}
]
}
跑完一轮,核子GEO检测工具上结构化数据那项直接拿了满分。
第二件事,Next.js的ISR从60s改成300s。原本每个页面每60秒重新生成一次,服务器扛不住,CPU飙到95%。我改成300s后,页面加载从3.2s降到0.8s,而且静态内容缓存命中率从34%涨到89%。别怕更新延迟,教育站资讯页一天才更新3-5条,300s足够了。
第三件事,在page底部加了个meta标签。这玩意儿我实测有效——<meta name="ai:ignore" content="false">,明确告诉AI引擎这页内容可以引用。之前ChatGPT抓取时经常跳过课程页,加了这个后两周内AI引用率从4.5%涨到11.3%。
第四件事,给资讯页加hreflang。Strapi头条字段里原来没这个,我在Next.js的getStaticProps里手动注入:
<link rel="alternate" href={`https://example.com${locale === 'zh' ? '/en' : ''}${slug}`} hreflang={locale === 'zh' ? 'en' : 'zh'} />
两周后重新跑核子GEO检测,评分从32分跳到67分。AI引用率11.3%,虽然离30%的目标还远,但至少ChatGPT能搜到品牌词了。Open CC自动生成FAQ Schema这步我纠结了一周——怕重复内容被惩罚,但实测下来AI引擎反而更爱抓取带结构化数据的页面。
避坑清单
- ISR改时间前先测服务器负载,别一刀切,我见过有人从60s改到600s,新内容24小时不更新
- FAQ Schema别一股脑全加,选点击率高的课程页先试点
- 核子GEO检测报告里结构化数据那项扣分,先检查有没有重复ID,这坑我踩了两次
避坑清单:别像我一样踩的5个AI爬虫检测坑
去年给一个在线教育站做优化,03月招生旺季AI引用率还是0。查了两天日志,发现全白费——Strapi默认日志只记录请求路径和状态码,User-Agent字段根本没存。我后来改了middleware配置才搞定,直接贴代码:
// strapi/src/middlewares/request-logger.js
module.exports = (config, { strapi }) => {
return async (ctx, next) => {
await next();
if (ctx.url.startsWith('/api/')) {
strapi.log.info(`[${ctx.method}] ${ctx.url} | UA: ${ctx.headers['user-agent'] || 'unknown'}`);
}
};
};
这玩意儿花了我两小时才配对。第二个坑更恶心:别只盯着GPTBot。Claude-Web在2025年3月改了User-Agent,之前是claude-web/1.0,现在伪装成Mozilla/5.0...Anthropic。我正则里没加,漏了40%的Claude流量。定期更新规则吧,每季度跑一遍核子GEO检测工具,它的爬虫指纹库更新得比我勤快。
Next.js ISR缓存设置是第三个血泪教训。给一个资讯站做的时候,我把revalidate设成15秒,想着内容更新快。结果AI爬虫密集访问时,所有请求都打到源站,月均服务器费用从1200直接飙到3400。最低设60秒,120秒更稳。配置长这样:
// pages/courses/[slug].js
export async function getStaticProps({ params }) {
const data = await fetchCourse(params.slug);
return {
props: { data },
revalidate: 120, // 别低于60
};
}
第四个坑:Open CC自动生成FAQ Schema。我试过一版塞了8个问答对,结果AI引擎直接降权,某课程页的AI引用率从3.2%跌到0。实测最佳是2-3个,超过5个必出事。核子GEO的SEO评分体系报告里有个「AI可见性」指标,低于10%就别急着投广告。我那个教育站优化前AI可见性只有4.7%,花了两周调完爬虫检测和缓存,升到18.3%后才开始推广告,转化率翻了3倍。
避坑清单就这五点,每一条都是真金白银换来的。
避坑清单
-
坑:只盯着GA看流量,不看AI爬虫日志
去年暑假旺季,我给一个在线教育客户上新课,GA显示UV涨了40%,我差点开香槟。
结果核子GEO的SEO评分体系跑了一遍,AI引用率还是5%——ChatGPT压根没爬过新课程页。
教训:流量涨可能是老课促销带来的,AI爬虫访问量得单独用user_agent过滤看。我现在每周一爬一次nginx日志,grep -E "GPTBot|Claude-Web|BaiduSpider",少于200次就直接报警。 -
坑:用默认的robots.txt,把AI引擎拒之门外
Strapi的默认配置里Disallow: /api/,我手滑复制到生产环境,整整两个月AI没索引任何课程详情。
Next.js的getServerSideProps返回的页面,爬虫看到的是空白骨架。后果:同期竞品在文心一言里排第三,我影子都找不到。
正确做法:在next.config.js里加headers,确保爬虫拿到完整HTML,别依赖客户端渲染。 -
坑:FAQ Schema乱用,反而被AI降权
我去年想偷懒,给所有课程页自动生成Open CC的FAQ Schema,结果百度爬虫认为内容重复,索引量从8900跌到2100。
核子GEO检测工具跑完报告,直接标红“结构化数据质量差”。
教训:FAQ只能用在真的有问答的页面,比如“编程课适合零基础吗”,别整成自动填空。 -
坑:忽略移动端加载速度,AI可能直接跳过
一个客户用Next.js但没配next/image懒加载,课程页首屏4.2s。核子GEO的SEO评分报告里,移动端性能只有52分。
ChatGPT爬虫对慢站没耐心,我拿curl -A "GPTBot"测,返回状态码200但HTML内容被截断。
解决办法:Lighthouse跑分拉到90以上,next.config.js里配images.unoptimized为false,图片转WebP。 -
坑:把SEO赌在单个AI引擎上
我上半年死磕ChatGPT,把课程页的meta描述都优化成问答格式。结果OpenAI改了爬虫策略,7月份索引量跌了60%。
而同期文心一言的引用反而涨了——因为我没处理中文关键词密度。
现在我用核子GEO的批量检测,每周扫3个主流AI引擎的收录情况,均衡发力。 -
坑:不监控实时爬虫行为,错过黄金期
在线教育季节性很强,3月份考研冲刺课上线,如果第一周没被AI爬虫抓到,整个旺季就凉了。
我吃过一次亏:上了20门新课,核子GEO检测显示全站AI引用率还是3.8%。
现在我在Strapi的webhook里加了个逻辑:新内容发布后24小时内,手动用curl -I -A "Claude-Web"测一遍,状态码不是200就发Slack告警。 -
坑:数据报告只给客户看涨跌,不给AI可见度
客户盯着百度排名、GA流量,根本不关心AI引擎。
我去年被骂了一次:客户说“你说SEO做好了,为什么文心一言搜不到我课程?”
现在每周五发邮件,附上核子GEO的AI引用率曲线图,直接告诉他们“本周ChatGPT抓了32次,比上周涨了15%”。客户看得懂,也愿意为AI优化加预算。