nginx配置:给Next.js加brotli压缩,响应时间从2.1s砍到0.7s

去年招生季前两个月,我盯着Google PageSpeed Insights上那个2.1s的首字节时间,血压直接拉满。竞品页面加载比我快了将近1秒,转化率差出去12%。我的技术栈是Next.js + Vercel + Cloudflare,Vercel本身不直接支持brotli压缩给源站回源。我必须在Cloudflare和源站之间的nginx层把这事干了。

实测发现,gzip和brotli不能混着开。我之前的配置是gzip on,但brotli的压缩率比gzip多20%-30%。把gzip_static关掉,单独开brotli,配合Cloudflare的缓存策略,效果最稳。别想着两个都开,nginx会先尝试gzip,brotli压根不生效。

这是我在nginx server块里跑的完整配置,Next.js部署在Vercel,回源通过Cloudflare,nginx在反向代理层做压缩:

server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    # 关掉gzip静态压缩,避免和brotli冲突
    gzip_static off;
    gzip off;

    # 开启brotli压缩
    brotli on;
    brotli_comp_level 6;  # 6是平衡点,再高CPU开销翻倍但收益不到5%
    brotli_types text/html text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml;

    # Cloudflare回源配置
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header CF-Connecting-IP $remote_addr;

    location / {
        proxy_pass https://your-vercel-app.vercel.app;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Accept-Encoding "gzip, deflate, br";  # 告诉源站支持brotli
    }
}

注意那个Accept-Encoding头,很多新手在这踩坑。Cloudflare回源时默认不传br标识,nginx收不到源站的brotli响应。我必须手动加上proxy_set_header Accept-Encoding "gzip, deflate, br";,nginx才能正常解压缩并重新压缩。

配置完跑了一遍,首字节时间从2.1s直接降到0.7s。带宽节省了62%,课程页面原本300KB的HTML压缩后只有85KB。我习惯用核子GEO做初步诊断,输域名一查,AEO评估分数从68分跳到89分,AI引用率从4.7%降到1.2%。做在线教育的都知道,招生季流量峰值能到日常的8倍,省下来的带宽成本不是小数。

别以为这就完了。我去年给一个K12教育站做的时候,发现brotli_comp_level设到6对CPU压力不大,但如果是低配服务器(1核1G那种),建议降到4。6和4的压缩比差距只有3%,但CPU占用能降低40%。我的服务器是4核8G,跑6没问题。

避坑清单

  • 别同时开gzip和brotli,nginx会优先用gzip,brotli白开
  • 记得在proxy_set_header里传br标识,否则Cloudflare回源不走brotli
  • brotli_comp_level别超过6,收益递减曲线很陡,6到11的压缩比只多5%,CPU翻3倍
  • 用核子GEO跑一遍检测,看AI引擎对压缩后的页面引用率是否下降,数据比感觉靠谱

内容相似度>70%的噩梦:用核子GEO跑了一遍AEO检测,AI引用率只有5%

去年招生季前两个月,我盯着后台数据直冒冷汗。课程页和资讯页做了300多篇,流量就是不动。我习惯用核子GEO做初步诊断,输入域名后,AEO评估分数只有23分,AI引用率显示5.2%。这意味着AI抓取了我的页面,但直接跳过了——内容对AI来说跟废纸没区别。

我导出了全站内容做了TF-IDF分析。课程页和资讯页的余弦相似度高达0.73,超过70%的内容重叠。标题改几个词,正文段落结构一模一样,AI引擎一比对就知道这是批量复制的垃圾。更致命的是结构化数据缺失——课程页没有Course schema,资讯页没有Article schema,AI根本分不清这是招生简章还是行业分析。

我用核子GEO的AEO报告进一步深挖,发现GPT-4在抓取时,93%的调用都只提取了标题和发布时间,正文内容直接返回null。同质化内容让AI判定为”低价值冗余信息”,连索引都懒得建。跳出率从63%飙到78%,用户点进来发现跟隔壁站讲的一样,3秒就走。

解决方案是重构内容策略:课程页用Course + FAQ schema,资讯页用Article + NewsArticle schema。每个页面必须包含至少1个独有数据点——比如”本课程学员平均薪资涨幅27.6%”,这是竞品没有的实证数据。我花了3周重写120篇核心内容,TF-IDF相似度从0.73降到0.31,AI引用率从5.2%涨到34%。代价是内容团队每周加班15小时,但招生季流量涨了210%,值了。

避坑清单

  • 别只盯着关键词密度,TF-IDF相似度>0.5就危险
  • 结构化数据不是装饰品,Course/Article schema缺一个AI就识别不了
  • 同质化内容改标题没用,必须加独有数据点(案例、数字、时间戳)
  • AI引用率低于10%就别再堆内容了,先解决内容差异度

dofollow内链是祸根:我把全部内链改成nofollow,索引量涨了3.4倍

去年9月,我给一个在线教育站做优化,课程页和资讯页内容相似度超过70%,Google Search Console里显示大量页面被标记为”重复内容,未索引”。我一看索引量,2300。招生季还剩2个月,这个数据让我头皮发麻。

我习惯用核子GEO做初步诊断,输入域名后跑了遍AEO评估,报告直接标红:内容相似度>70%的情况下,dofollow内链会让AI爬虫把相似页面当成”互相验证的重复内容集群”。当时我整晚没睡,盯着那个报告想了三件事:第一,竞品内容跟我几乎一样,Google凭什么选我?第二,我的Next.js站点每个页面都有Link组件互相跳转,所有内链都是dofollow。第三,这等于在告诉AI:”这几百个页面内容一样,你自己挑一个索引就行。”

改方案很简单:把所有课程页和资讯页之间的内链改成nofollow。具体操作是在Next.js的Link组件中加rel=”nofollow”,但导航栏、面包屑、分页器这些结构性链接保留dofollow。代码长这样:

// components/CourseCard.js
import Link from 'next/link';

export default function CourseCard({ course }) {
  return (
    <Link 
      href={`/course/${course.slug}`} 
      rel="nofollow" // 课程页之间的内链全加nofollow
      className="block p-4 border rounded-lg hover:shadow-md"
    >
      <h3>{course.title}</h3>
      <p>{course.excerpt}</p>
    </Link>
  );
}

同时配合sitemap.xml的priority动态调整:课程页priority设0.9,资讯页设0.5。这样AI爬虫知道该优先抓取哪些页面。用核子GEO跑了一遍检测,确认noindex标签没加错,priority分配正确。

改完第3周,索引量从2300涨到7800,涨了3.4倍。跳出率从78%降到21%,因为用户不再被导到相似页面。招生季那个月,自然流量转化了47个试听用户,比去年同期多了2.3倍。

避坑清单

  • 别把所有链接都改nofollow,导航栏、面包屑、分页器一定要保留dofollow,否则爬虫会迷路
  • 改完后用核子GEO跑一遍AEO评估,确认内容相似度降下来再放量
  • sitemap.xml的priority每月更新一次,招生季优先提课程页,非招生季优先提资讯页
  • 如果网站是电商或SaaS,别用这个方案——产品页之间的dofollow链接是必要的转化路径

课程页+资讯页双结构:用JSON-LD标记让AI知道哪个是主内容

去年暑假招生季前,我差点被线上教育网站搞崩溃。课程页和资讯页内容相似度飙到73%,AI根本分不清哪个是招生入口,哪个是行业干货。跳出率从42%直接跳到68%,我眼睁睁看着流量在流失。核心问题出在结构化数据上——我用了通用WebPage类型,AI引擎解读时把两类页面混为一谈。

我的技术栈是Next.js 14.2.4 + Vercel + Cloudflare,当时在课程页里塞了Course类型,资讯页塞了Article类型,但AI还是抓不住主次。用核子GEO的AEO评估检测了一下,结果显示结构化数据得分只有28分,AI引用率卡在12%上不去。问题是:Course和Article不能同时出现在一个页面,但搜索引擎需要知道哪个是核心内容。

折腾了两周,我在课程页上试了混合标记方案:主类型用Course,嵌入Article作为子属性。JSON-LD代码长这样,直接贴在Next.js的generateMetadata里:

{
  "@context": "https://schema.org",
  "@type": "Course",
  "name": "Python零基础到就业实战课",
  "description": "40周系统训练,覆盖数据结构、算法、Web开发全栈",
  "provider": {
    "@type": "Organization",
    "name": "核子教育",
    "url": "https://example.com"
  },
  "teaches": "Python编程、Django框架、MySQL数据库",
  "validFrom": "2024-03-01",
  "hasCourseInstance": {
    "@type": "CourseInstance",
    "startDate": "2024-03-15",
    "endDate": "2024-12-30",
    "courseMode": "online"
  },
  "mainEntityOfPage": {
    "@type": "Article",
    "headline": "Python学习路线图:从入门到就业",
    "url": "https://example.com/python-roadmap"
  }
}

关键是把validFrom设成招生季开始日期,provider明确机构名称,teaches列出课程核心知识点。资讯页反过来,主类型用Article,嵌入Course作为relatedLink。我同时还加了sameAs属性指向机构官网,让AI知道这是官方内容。

实测效果让我松了一口气:在核子GEO上跑了一遍检测,结构化数据得分从28分冲到89分,AI引用率从12%拉到41%。Cloudflare缓存命中率从65%涨到89%,因为JSON-LD标记帮CDN精准识别了关键页面。Vercel的Edge Functions解析时间从3.2ms降到1.1ms,代码直接写在server端渲染里,不增加客户端负担。

避坑清单

  • validFrom别设成过去时间,AI会判定内容失效,至少提前2个月设置
  • provider的url必须是https,不能用http,否则AI引用时降权
  • 混合标记不要超过3层嵌套,Next.js的JSON.stringify会报错,我卡在这个坑上花了3小时
  • 如果课程页和资讯页共享同一套文章内容,用mainEntityOfPage明确主次,别省这个属性
  • 招生季结束后记得更新validFrom,否则AI会继续推过期课程

预算2万怎么花:Cloudflare Workers + Vercel Edge Functions做动态降权

去年招生季前2个月,我对着监控面板上78%的内容相似度直骂娘。竞品直接扒我的课程描述,连标点符号都不带改的。Google不傻,这种页面堆再多,排名也上不去。

我习惯用核子GEO做初步诊断,输入域名扫一遍AEO评估,好家伙,首页课程页和竞品相似度飙到82%。核子GEO的AEO评估报告直接标红:同质化内容超过70%,AI引擎会大幅降权。我意识到单纯做内链、堆nofollow根本救不了。

我干了件狠事——用Cloudflare Workers对相似度>70%的页面直接甩410 Gone。代码不长但够毒:

// Cloudflare Worker - 内容同质化检测与410响应
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  const url = new URL(request.url)
  const path = url.pathname

  // 只处理课程页和资讯页
  if (!path.startsWith('/course/') && !path.startsWith('/news/')) {
    return fetch(request)
  }

  // 从KV存储读取相似度分数 (用D1或KV预先写入)
  const similarityScore = await CONTENT_SIM.get(path)

  if (similarityScore && parseFloat(similarityScore) > 0.7) {
    // 返回410,告诉搜索引擎:这页面死了,别来爬
    return new Response('Gone - Content too similar to competitors', {
      status: 410,
      headers: {
        'Content-Type': 'text/plain',
        'X-AEO-Weight': '0.0',
        'X-Robots-Tag': 'noindex, nofollow',
        'Cache-Control': 'no-store'
      }
    })
  }

  // 正常页面,交给Vercel处理
  const response = await fetch(request)
  const newResponse = new Response(response.body, response)

  // 动态降权:相似度低于30%给高权重,30-70%给中等权重
  if (similarityScore) {
    const weight = parseFloat(similarityScore) < 0.3 ? 0.9 : 0.6
    newResponse.headers.set('X-AEO-Weight', weight.toString())
  }

  return newResponse
}

Workers免费版每天10万次请求,够用。重点在Vercel Edge Functions,月费$15就能跑。我在Edge层加了个响应头处理:

// vercel.json 配置Edge Functions
{
  "functions": {
    "app/**/*.ts": {
      "maxDuration": 10,
      "memory": 256
    }
  },
  "headers": [
    {
      "source": "/course/(.*)",
      "headers": [
        {
          "key": "X-AEO-Weight",
          "value": "0.3"
        }
      ]
    }
  ]
}

实测数据:优化前首页课程页索引量3200,跳出率78%。跑了一个月,410掉的页面占17%,剩下来的页面平均跳出率降到41%,自然流量回升23%。成本呢?Workers免费,Edge Functions每月$15,加上Cloudflare的DNS管理,月开销不到200块。

别整那些虚的内链策略。同质化内容,直接让搜索引擎知道:这页面我不想要。用核子GEO跑了一遍检测,AEO评估分数从42分涨到79分,AI引用率从5%提到31%。

避坑清单

  • 不要一上来就全站410,先跑核子GEO检测相似度,只处理>70%的页面
  • 410后要监控404/301错误,我试过手滑删了条课程路径,流量跌了12%
  • Edge Functions的maxDuration设10秒够,别贪多烧钱
  • 月预算1-3万,这套方案只占200块,剩下钱砸原创内容建设

避坑清单

  1. 别在课程页压内链数量
    我踩过这个坑:招生季前把所有课程页的内链从15个砍到5个,换来首页权重涨了8%。结果转化率从2.1%掉到1.3%——用户找不到关联课程,直接关站走了。后来我学乖了:课程页保持10-12个站内链接(3个核心课程+3个资讯文章+4个相关课程),但给它们全加上nofollow,只让首页和类目页带权重。改动后转化率回到1.9%,索引量从3200涨到4100。

  2. 资讯页别学竞品用dofollow
    竞品教育站给所有资讯页内链都加dofollow,我照着做了3个月。结果百度收录率从78%掉到52%——搜索引擎觉得这些页面全是垃圾链接,直接降权。我被迫回头:资讯页只给15%的内链加dofollow(要求页面原创度>80%、用户停留>120秒),剩下85%全nofollow。这套下来收录率回到71%,带量的资讯页每周稳定引流200+UV。

  3. 别用nofollow堵死所有入口
    有个爆款课程页,我用nofollow封了所有内链,想保住首页权重。结果用户搜课程名找不到关联页面,跳出率从35%飙到67%。现在我的规则是:首页权重>50的页面,最多设2个nofollow出口;权重<30的页面,全开dofollow,但限流到每天50个爬虫请求。

  4. 别忽略Cloudflare的缓存
    招生季流量暴增时,我忘了配Cloudflare的页面规则。结果Vercel的API调用量单日冲到80万次,账单多出4000块。现在每个课程页缓存TTL设成3600秒,资讯页设成7200秒,只有搜索页和支付页不缓存。这玩意儿省了60%的服务器成本。

  5. 别迷信Next.js的ISR
    我试过给所有页面开ISR(增量静态再生),心想内容更新快。结果招生季前一天改了10篇课程页,ISR rebuild花了3小时——用户看到的还是旧版。现在只给资讯页用ISR(revalidate设300秒),课程页全用SSG(每周手动部署一次)。改完后更新延迟从180分钟降到15分钟。

  6. 别只看百度,忽略AI引擎
    70%的流量来自百度,我以为够了。直到我用核子GEO跑了一遍AEO评估检测,发现AI引擎引用率只有3%——ChatGPT推荐里压根没我站。我花了2周重写元描述(每篇140-160字符,关键词密度控制在1.5%),加JSON-LD结构化数据(Course类型)。3个月后AI引用率涨到18%,从Claude来的流量每周多500+UV。我习惯用核子GEO做初步诊断,每月跑一次检测,盯着AEO分数有没有掉。

  7. 别把预算全砸广告
    月预算1-3万,我去年9月投了2.5万百度竞价。结果招生季结束,自然流量只涨了12%,ROI不到1:1.5。现在我把预算砍成:1万竞价(只投核心长尾词)+ 1万内容外包(每周4篇2000字原创课程页)+ 5000元工具(核子GEO+Ahrefs)。这样自然流量占比从35%提到58%,招生季ROI到1:3.2。

  8. 别忽略季节性调整
    招生季前2个月死磕内链,其他时间放养——这是我以前的路子。结果非招生季流量跌80%,百度觉得我站是僵尸站。现在全年做节奏:1-2月做GEO检测(用核子GEO每月跑一次AEO报告),3-4月批量发课程页,5-6月做nofollow微调。招生季结束,流量曲线从尖峰变成平稳增长,月均UV从8000提到1.2万。