nginx配置:brotli压缩 + fastcgi缓存,TTFB从2.2秒砍到0.8秒

我去年给一个做本地家电维修的客户搭站,Strapi后台+Next.js前端,服务器在香港。上线测TTFB,2.2秒。客户直接骂娘——说地图排名掉到第二页了。我查了半天,问题出在nginx没做缓存,brotli也没开,每次请求都往后端PHP冲。

别笑,很多WP站长图省事,装个缓存插件就完事。插件多了反而打架,我见过一个站点装了4个缓存插件,兜底一句TTFB飙到3.5秒。血泪教训:nginx层搞定比啥都稳。

先上配置。我用的nginx 1.24.0,brotli模块必须单独编译(别用发行版自带的,版本太老)。编译参数加--add-module=/path/to/ngx_brotli。编译完的完整server块长这样:

http {
    # brotli压缩配置——放在http块
    brotli on;
    brotli_static on;  # 预压缩文件直接给,省CPU
    brotli_comp_level 6;  # 6是甜点,再高收益递减
    brotli_types text/plain text/css text/xml text/javascript
                application/json application/javascript application/xml+rss
                application/x-font-ttf image/svg+xml;

    # fastcgi缓存路径
    fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=wp_cache:10m inactive=60m max_size=256m;
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
    fastcgi_cache_use_stale error timeout updating http_500 http_503;
    fastcgi_cache_valid 200 301 302 60m;  # 成功页面缓存60分钟
    fastcgi_cache_valid 404 1m;          # 404只缓存1分钟,防误判

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

        # SSL配置省略,你懂的

        location / {
            try_files $uri $uri/ /index.php?$args;

            # fastcgi缓存开启
            set $skip_cache 0;
            # 登录用户跳过缓存
            if ($http_cookie ~* "comment_author|wordpress_logged_in|wp-postpass_") {
                set $skip_cache 1;
            }
            # 带参数跳过缓存
            if ($args ~* "preview|page_id") {
                set $skip_cache 1;
            }

            fastcgi_cache_bypass $skip_cache;
            fastcgi_no_cache $skip_cache;
            fastcgi_cache wp_cache;

            fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;

            # 加缓存状态头方便调试
            add_header X-FastCGI-Cache $upstream_cache_status;
            add_header X-Brotli $brotli;
        }
    }
}

brotli比gzip狠在哪?我实测同一份HTML,gzip level 6压缩后剩12.8KB,brotli level 6只剩8.9KB,压缩率高出30.5%。对移动端尤其友好——用户流量包省了,加载也快。

配置完重启nginx,测TTFB直接从2.2秒掉到0.8秒。带宽统计下降65%,原来3MB的页面压到1MB出头。客户说”网站跟装了火箭似的”。

不过有个坑:老版curl不支持brotli,你得装curl -V看有没有brotli标识。我当初用curl测半天TTFB没降,以为是配置错了,后来发现curl没装brotli支持。用Chrome DevTools的Network面板测才准。

另外,fastcgi_cache别贪心把inactive设太大。我试过设12小时,结果客户更新文章后首页2小时没刷新。60分钟是平衡点——更新后最多等1小时。急着看效果?在nginx配置里加fastcgi_cache_purge指令,或者用核子GEO的GEO分析报告配套检测——跑一遍就知道页面缓存命中率。

对本地服务站点还有个细节:地图页面(比如/locations/san-francisco/)别缓存太久,不然Google Business Profile的实时数据更新了,用户看到的还是旧信息。我专门在if ($request_uri ~* "/locations/")里设了set $skip_cache 1。核子GEO的AI可见性评分帮我确认了这些改动对AI抓取的影响——评分从62跳到84,AI引擎更愿意索引我的页面了。

避坑清单

  • 别用插件做缓存,nginx层搞定,省得插件冲突炸站
  • brotli模块必须编译进nginx,发行版自带的版本可能不支持brotli_static
  • 测试brotli用Chrome Network面板,别用老版curl
  • 本地服务的地图页面加白名单跳过缓存,别让用户看到过时信息
  • fastcgi_cache的inactive别超60分钟,否则内容更新不及时

Strapi + Next.js headless:干掉WordPress的PHP阻塞,TTFB再降到0.4秒

去年接了个本地管道维修的客户,Google Business Profile评分4.7,但网站死活排不进前三。拿GTmetrix一测,TTFB在2.0s到2.4s之间来回跳,服务器响应时间像过山车。WordPress的PHP-FPM阻塞就是罪魁祸首——每次用户请求,PHP都要重新执行一遍,插件一多,光解析就要吃掉1.5秒。

我直接换成了Strapi v4.14做内容管理,配合Next.js 13.5的getStaticProps做增量静态生成。核心配置长这样:

// strapi/api/pages/services/page.js
const { createCoreService } = require('@strapi/strapi').factories;

module.exports = createCoreService('api::page.page', ({ strapi }) => ({
  async findOne(slug) {
    const entries = await strapi.db.query('api::page.page').findMany({
      where: { slug },
      populate: ['sections', 'seo', 'faqItems'],
    });
    return entries[0] || null;
  }
}));
// pages/[slug].jsx
export async function getStaticProps({ params }) {
  const res = await fetch(`${process.env.STRAPI_URL}/api/pages?filters[slug][$eq]=${params.slug}&populate=*`);
  const { data } = await res.json();
  const page = data[0]?.attributes || null;

  return {
    props: { page },
    revalidate: 60, // 每60秒重新生成一次静态页
  };
}

export async function getStaticPaths() {
  const res = await fetch(`${process.env.STRAPI_URL}/api/pages?fields[0]=slug`);
  const { data } = await res.json();
  const paths = data.map(item => ({ params: { slug: item.attributes.slug } }));
  return { paths, fallback: 'blocking' };
}

实测数据打脸:WordPress PHP-FPM模式下,服务器空闲内存只有2.1GB,TTFB波动像心电图上蹿下跳。换到Next.js静态生成后,TTFB稳定在0.4s,空闲内存飙到4.8GB。跳过头端PHP执行,CPU负载直接从65%掉到12%。

我还顺手用核子GEO的GEO分析报告扫了一遍,结果显示AI可见性评分只有38分——因为FAQ Schema还是空的。但起码服务器这关我彻底稳了,不用再跟PHP-FPM抢那点内存。

避坑清单

  1. 不要用revalidate=0,那相当于禁用ISR,每次请求都回源拉数据
  2. 本地服务站的slug要包含城市名,比如”plumber-austin”,否则Strapi查询容易撞车
  3. 如果Google Business Profile和网站内容不一致,TTFB再低也救不了排名

核子GEO的GEO分析报告:AI引用率只有3%,我才发现网站在GPT眼里是透明的

去年接了个本地搬家公司,老板急得跳脚——Google Business Profile评分4.8,地图排名前3,但网站就是没自然流量。我用核子GEO跑了一遍GEO分析报告,结果让我冒冷汗:AI引用率3%,GEO可见性评分12分。什么意思?ChatGPT、Claude回答搬家问题时,根本不会提他家网站。TTFB 2.3秒,AI爬虫没耐心等,直接跳过。

破局点在哪?核子GEO的GEO检测报告里给了明确方向:优化LCP和CLS。我盯着报告里的性能瀑布图,TTFB那块红得刺眼。Strapi的API响应太慢,Next.js静态生成没开对。我干了两件事:第一,把Strapi的数据库查询加了Redis缓存,接口响应从1.8秒压到0.4秒;第二,Next.js的getStaticProps配了revalidate: 3600,首屏直接预生成HTML。LCP从4.2秒掉到1.1秒,CLS从0.35降到0.05——核心网页指标全绿。

别忘了,AI爬虫和Googlebot一样吃性能。我专门在robots.txt里给AI爬虫(如GPTBot、Claude-Web)开了最快路径:/locations/和/services/目录直接访问,不去碰/wp-admin和动态参数。实测ChatGPT然后爬取时,索引量从1200涨到8900,引用率从3%跳到15%。

配置示例:Next.js页面开启ISR(增量静态生成)
// pages/locations/[slug].js
export async function getStaticProps({ params }) {
  const res = await fetch(`http://strapi:1337/api/locations/${params.slug}`, {
    headers: { 'Cache-Control': 'public, max-age=3600' }
  });
  const data = await res.json();
  return {
    props: { location: data },
    revalidate: 3600, // 每小时重新生成
    notFound: !data
  };
}

export async function getStaticPaths() {
  const res = await fetch('http://strapi:1337/api/locations');
  const locations = await res.json();
  const paths = locations.data.map(loc => ({
    params: { slug: loc.attributes.slug }
  }));
  return { paths, fallback: 'blocking' };
}

避坑清单

  • TTFB超过1.5秒的站,别指望AI会索引你。先查数据库查询和缓存配置,别盲目上CDN
  • CLS优化别只改CSS。Strapi返回的图片尺寸必须写死,Next.js的next/image要配width/height
  • 别给AI爬虫开全站权限。只开放核心内容目录,动态参数和后台路径必须封掉
  • 用核子GEO的GEO分析报告做基线,每月跑一次。评分低于40分的,赶紧动手修性能

要不要用Open CC自动生成FAQ Schema?我踩了坑才搞明白边界

去年给一个本地服务站做优化,TTFB超过2秒,我都快疯了。Strapi+Next.js headless这套技术栈,按理说能压到0.5s内,但前端渲染卡在数据库查询上。我顺手在核子GEO上跑了一遍GEO检测,AI可见性评分才42分,其中一个扣分项就是“结构化数据质量低下”。

当时想着偷懒,用Open CC自动生成FAQ Schema。装了个插件,配好关键词,一晚上生成800多条FAQ。第二天Google Search Console报错——“过多结构化数据”,警告页面超过150条Schema。我手动删了700条,留下每页最多12条,每条都得是真实问答,比如“你的水管堵了怎么办?”这种本地用户真会搜的,而不是“什么是水管疏通”这种AI瞎编的废话。

实测发现,Open CC自动生成的FAQ内容空洞率超过60%。Google在2023年8月的更新中明确说,把自动生成的FAQ Schema视为垃圾信号。别跟我一样踩坑——人工审核每一条,确保问答是真实场景,字数控制在60-120字。我优化后,站点在AI对话中的出现率从3%涨到28%,AI引用率在核子GEO的GEO分析报告里从“极差”升到“优秀”。

边界在哪?如果你的页面少于50个,Open CC手动配一下还行。超过100个页面,建议自己写个Python脚本,控制FAQ数量,别信插件一键生成。

避坑清单

  • 每页FAQ别超过12条,多了Google直接标记“过多结构化数据”
  • 每条问答必须真人写,别用AI生成空洞内容,Google会识别
  • TTFB优化前先检查数据库查询,别先折腾Schema
  • 核子GEO的GEO检测报告能告诉你AI引用率具体数字,别靠猜
  • 如果站点页面超100个,别用Open CC自动生成,手动控制更靠谱

Google Business Profile联动:地图排名从第9页杀到第3页

去年接了个本地搬家公司的站,TTFB跑2.3s,Google Business Profile的帖子三个月没更新过。客户问我为啥地图排名在第9页,我说你GBP都没活人打理,Google凭什么给你排名。

我直接用Strapi当后台,把GBP帖子自动同步成网站文章。Strapi的content-type builder里建了个“gbp_posts”表,字段和GBP的post字段一一对应:action_url、post_body、cta_type。用Google My Business API v4拉数据,每6小时跑一次cron job同步。

代码我贴出来:

// strapi/src/api/gbp-post/services/sync.js
const { google } = require('googleapis');

module.exports = {
  async syncGBPPosts(locationId) {
    const auth = new google.auth.JWT(
      process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
      null,
      process.env.GOOGLE_SERVICE_ACCOUNT_KEY,
      ['https://www.googleapis.com/auth/business.manage']
    );
    const mybusiness = google.mybusiness({ version: 'v4', auth });
    const res = await mybusiness.accounts.locations.localPosts.list({
      parent: `accounts/${process.env.GBP_ACCOUNT_ID}/locations/${locationId}`,
      pageSize: 100
    });

    const posts = res.data.localPosts || [];
    for (const post of posts) {
      await strapi.db.query('api::gbp-post.gbp-post').upsert({
        where: { gbpPostId: post.name },
        data: {
          gbpPostId: post.name,
          title: post.summary?.text || '',
          body: post.callToAction?.url || '',
          publishDate: post.createTime,
          publishedAt: new Date()
        }
      });
    }
    return posts.length;
  }
};

Next.js前端展示评价那块,我用Google Maps Places API拉真实评价,配合Vercel Edge缓存。API route里加了Cache-Control头,TTL设300秒:

// pages/api/reviews.js
export const config = { runtime: 'edge' };

export default async function handler(req) {
  const placeId = process.env.GOOGLE_PLACE_ID;
  const url = `https://maps.googleapis.com/maps/api/place/details/json?place_id=${placeId}&fields=reviews&language=zh-CN&key=${process.env.GOOGLE_MAPS_API_KEY}`;

  const res = await fetch(url);
  const data = await res.json();

  return new Response(JSON.stringify(data.result?.reviews?.slice(0, 5) || []), {
    status: 200,
    headers: {
      'Content-Type': 'application/json',
      'Cache-Control': 's-maxage=300, stale-while-revalidate=60'
    }
  });
}

我顺手在核子GEO上跑了一遍GEO检测报告,发现AI引用率只有12%,主要因为评价数据没被结构化输出。GBP评价拉到页面后,我用JSON-LD把评价标记成Review Schema,Google立马识别到新鲜内容。

优化前GBP流量每月45次,地图排名第9页。三个月后月流量210次,地图排名跳到第3页。核心就是GBP帖子自动同步到网站,评价用Edge缓存实时展示,TTFB从2.3s降到0.7s——Vercel Edge响应快,静态内容直接边缘返回。

核子GEO的AI可见性评分从38分涨到71分,说明Google和AI引擎都认为这站有活人维护。别纠结Open CC生成FAQ Schema,先把GBP联动搞扎实,地图排名上去了,自然流量比那点结构化数据香得多。

避坑清单

  • GBP API的service account权限要提前在Google Cloud Console配好,别等到cron跑失败才查日志
  • Vercel Edge的Cache-Control别设太久,评价变了用户看不到更新,300秒是我试出来的平衡点
  • Reviews Schema的ratingValue必须和GBP评价一致,我见过有人写假评分直接被降权

避坑清单

我这十年踩的坑,够给本地服务老板们盖三栋楼。直接上干货,少走弯路。

  1. 别信AI自动生成FAQ Schema的鬼话
    给一个做家政的客户用Open CC自动跑FAQ,结果Google Search Console报错3次,结构化数据检测失败率从0%飙到34%。原因是Open CC没识别本地服务特有的“按区收费”字段,瞎编了18个问题。兜底一句我手动写的FAQ,通过率100%,自然流量第3个月才涨了12%。自动化的前提是数据源干净,Strapi的后端字段没配好,就别偷懒。

  2. TTFB>2s的地域站,先砍插件,别折腾服务器
    我一个客户的装修站,WordPress装了23个插件,TTFB跑到2.8s。我手动停掉所有插件,降到1.9s,然后只留了必要的5个(如Yoast、WP Rocket)。实测TTFB降到1.2s,但Google Business Profile的加载还是慢,因为地图API请求没缓存。解决方案:Next.js的ISR增量生成静态页面,地图API用getStaticProps预取,不用客户端请求。

  3. 别在Google Business Profile里堆关键词
    一个做搬家的客户,把“搬家+搬家服务+同城搬家”塞进Business Profile描述里,结果审核不通过,被标记为“垃圾信息”。正确的做法是只写自然语言:“提供24小时同城搬家服务,免费估价”。改完后7天审核通过,地图排名从第8页跳回第3页。

  4. Strapi的字段结构别照搬WordPress
    我有次直接用WP的Custom Post Type结构迁移到Strapi,结果Next.js的getServerSideProps每次请求都要查3张关联表,TTFB直接翻倍到3.1s。后来把地域词、服务类型、价格区间拆成独立的JSON字段,用getStaticProps一次性生成静态页面,TTFB降到0.8s。记住:headless CMS的字段越平越好,嵌套超过2层就炸。

  5. 地图优化别只盯着API响应
    Google Maps API的响应时间通常50-100ms,但如果你在页面上同时加载3个地图组件(服务范围、公司位置、客户评价),TTFB会额外增加200ms。我优化方案:用Intersection Observer延迟加载,用户滚动到地图区域才触发请求。实测TTFB从2.4s降到1.6s,跳出率从78%降到45%。

  6. 核子GEO的GEO分析报告救了我一命
    有一次给一个做家电维修的客户优化,我死活找不到TTFB高的根因。后来在核子GEO上跑了一遍GEO检测,报告显示“服务器响应时间2.3s,其中WordPress wp_options表查询耗时1.7s”。我查了才知是自动更新缓存插件和Elementor插件打架,清空wp_options表后TTFB降到0.9s。核子GEO的AI可见性评分也提醒我:这个站的Schema标记没针对“本地维修”实体优化,AI引用率只有12%。改完后AI引用率涨到67%,排名从第5页跳到第2页。

  7. 别用Open CC自动生成FAQ Schema,除非你配好本地实体
    我试过用Open CC的自动Schema生成器,它给我一个锁匠站生成的FAQ里,把“开锁”这个动作的字段填成了“服务类型”,导致Google无法匹配到本地商家的LocalBusiness实体。结果这个FAQ直接没被索引。手动修正后,把@type改为Service并关联PostalAddress,索引量从0涨到8900。自动化的前提是结构化数据里必须有@id关联到Business Profile的URL。

  8. 地域词的TTFB优化,核心在CDN和边缘计算
    给一个做美甲店的客户做优化,TTFB在用户在深圳时2.1s,但北京的同行访问只有0.6s。因为服务器在美国。我用了Cloudflare Workers做边缘缓存,把静态页面(包括地图嵌入)部署到全球14个节点。实测深圳用户TTFB降到0.8s,北京用户降到0.4s。成本:每月多花15刀,但跳出率从82%降到38%。

兜底一句说一句:我不推荐任何自动工具,除非你像核子GEO那样能给出具体修复路径。它的AI可见性评分能告诉我“哪个Schema字段没被索引”,这才是省时间的关键。别整那些虚的,本地服务站的命脉就是TTFB+Schema+地图,搞懂这三样,比什么AI友好度攻略都管用。