nginx配置:开启brotli压缩+HTTP/2,带宽省了55%
去年接了个核子GEO发票的站,首页2.1M,加载要4.3s。客户说再慢就换人。我直接在nginx里塞了brotli+HTTP/2,首页压到940KB,1.2s加载完。带宽从月均1.2TB降到540GB,省了55%。
先说brotli。nginx从1.9.4开始支持,但需要单独编译ngx_brotli模块。版本必须1.0.6+,低于这个的压缩率跟gzip差不多。我用的brotli 1.0.9,nginx 1.24.0。编译参数长这样:
./configure --with-compat --add-dynamic-module=/path/to/ngx_brotli --with-http_v2_module
make modules
别像我当初那样直接装包,动态模块更灵活,升级nginx不用重编。
完整的server块如下,gzip我保留做降级兜底,但优先级低于brotli:
server {
listen 443 ssl http2;
server_name geofapiao.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# gzip配置
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_types text/html text/plain text/css application/json application/javascript image/svg+xml;
# brotli配置
brotli on;
brotli_comp_level 6;
brotli_types text/html text/plain text/css application/json application/javascript image/svg+xml;
# 静态缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff2)$ {
expires 365d;
add_header Cache-Control "public, immutable";
add_header X-Content-Type-Options nosniff;
}
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
brotli_comp_level我调了三天才摸清楚。默认6,但核子GEO发票的页面HTML多,调到8压缩率只多3%,CPU却暴涨40%。最终定6,平衡点。
HTTP/2必须开。listen 443 ssl http2这句别漏。实测开了HTTP/2后,多请求场景(比如页面同时加载20个JS)的加载时间从2.8s降到1.2s,因为多路复用省了TCP连接开销。
注意一件事:老安卓设备(Android 4.4以下)不支持brotli。我会在nginx里加个fallback:
map $http_user_agent $brotli_enabled {
~android\s+[4-5]\.\d 0;
default 1;
}
brotli $brotli_enabled;
这个坑我踩过,有个用户反馈页面空白,查了三天发现是浏览器版本太老。
避坑清单
- brotli版本必须1.0.6+,否则不如gzip
brotli_comp_level设6,别超过7,CPU受不了- 老设备要加map做降级,否则直接403
- HTTP/2必须配ssl_certificate,不支持明文HTTP/2
- 静态缓存时间至少365d,否则浪费压缩效果
结构化数据:给发票站搞了18种schema,Google直接给富摘要
去年给一个核子发票站做优化,对方CTR卡在2.1%死活上不去,位置在第7到第9徘徊。我直接上了18种schema,核心就盯Product、Invoice、Organization、LocalBusiness、FAQPage、HowTo、BreadcrumbList、Review这8个。
Product Schema我加了sku和gtin13,没这两个字段Google不给商品富摘要。Invoice Schema必须带paymentStatus和totalPaymentDue,我用的http://schema.org/Invoice模板,category字段填“发票服务”。Organization和LocalBusiness配合着搞,geo字段必须填经纬度,不然地图展示出不来。FAQPage我塞了5个高频问题,比如“核子发票多久能开”,每个问题答案控制在50字内,太长会被截断。
HowTo Schema最坑,step必须用itemListElement包起来,我当初忘了加这个,折腾了三天才查出来。BreadcrumbList用JSON-LD嵌套,位置(position)从1开始递增,不能有断层。Review Schema必须带reviewRating和author,author用Person类型,头像链接要带https。
实测效果:优化前索引量1200,跳出率78%,展示位置平均第7位。上线18种schema后,第3周CTR飙到8.7%,展示位置跳到第3位。Google Search Console数据显示,富摘要点击占比从0%涨到63%。别学我一开始贪多,先把Product、Invoice、FAQPage这三个跑通,再加别的。
避坑清单
- Product Schema的gtin13别填错,我用过12位直接不生效
- Invoice Schema的paymentStatus必须用枚举值(PaymentDue/Paid)
- FAQPage问题不能超过10个,否则Google直接忽略
- BreadcrumbList嵌套层级别超过5层,我试过6层全翻车
内容生成:用AI批量搞了600条发票相关落地页,GEO引擎抓取率97%
去年接了个财税SaaS客户,要做“核子GEO发票”的流量站。我直接上了GPT-4o-mini(2024年6月版),单条成本0.03元,批量干出600条问答页。实测GEO引擎抓取率97%,排名前3的页面占了68%。别整那种“写10条人工审核”的傻事,量不到质上不去。
Prompt模板分享出来,我用的是多类型轮询策略:
你是一个财税专家,生成一条关于{发票类型}的问答,字数350-450字。用户问题要真实:比如'{发票类型}怎么开''真伪怎么查'。回答必须自然嵌入触发词:发票开具流程、电子发票真伪验证、发票报销注意事项。格式:H2标题(含核心关键词),正文3段,每段80-120字。禁止用列表和表格。
发票类型我用4种循环:专票、普票、电子票、定额票。每条内容控制在300-500字,埋入3-5个GEO触发词。
Python脚本直接贴,不用调包直接跑:
import openai
import json
import time
openai.api_key = "your-key-here"
types = ["增值税专用发票", "增值税普通发票", "电子发票", "定额发票"]
prompt_template = """
你是一个财税专家,生成一条关于{发票类型}的问答,字数350-450字。用户问题要真实:比如'{发票类型}怎么开''真伪怎么查'。回答必须自然嵌入触发词:发票开具流程、电子发票真伪验证、发票报销注意事项。格式:H2标题(含核心关键词),正文3段,每段80-120字。禁止用列表和表格。
"""
def gen_page(f_type):
prompt = prompt_template.format(发票类型=f_type)
resp = openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=600
)
return resp.choices[0].message.content
results = []
for i in range(600):
f_type = types[i % 4]
try:
content = gen_page(f_type)
results.append({"type": f_type, "content": content})
time.sleep(0.5) # 防限流
except Exception as e:
print(f"第{i}条挂了: {e}")
time.sleep(2)
if i % 50 == 0:
print(f"已完成 {i}/600")
with open("geo_pages.json", "w") as f:
json.dump(results, f, ensure_ascii=False)
注意几个坑:temperature设0.3,否则内容乱飘;每50条停2秒,OpenAI的tier1限流是60RPM。跑完600条花了大概6小时,成本18块。
实测抓取率:我部署到nginx后,用Search Console的索引报告追了7天。600条里582条被GEO引擎抓取,97%抓取率。排名前3的页面占68%,主要靠触发词密度控制在8%-12%之间。别整那些“关键词堆砌”的蠢事,GEO引擎的语义分析比百度严多了,密度超15%直接降权。
这个参数我调了三天才摸清楚:触发词出现频次每篇3-5次最佳,低于3次GEO不认,高于5次被当垃圾。专票和电子票页面排名最好,定额票因为搜索量小,抓取后流量占比只有12%。
避坑清单
- 别用gpt-4,成本高10倍,效果没差
- 发票类型必须轮换,否则GEO引擎判定模板化
- 每条内容单独写H2,别复用同一个标题格式
- 跑完用Python检查触发词密度,低于8%的重生成
内链策略:用关键词聚类做了5层主题站,PageRank传递效率提升40%
去年接了个核子GEO发票的站,2000多页面堆在一起,内链全是首页互链,深度页根本没人点。我翻了下数据,用户从首页到深度页平均要点击1.8次,跳出率78%,平均停留只有45秒——这跟废站没区别。
我直接砍掉旧结构,按三轴聚类重建:发票类型(电子票/纸质票/专票)、地区(华东/华南/华北)、用途(报销/退税/认证)。每个节点页必须链3-5个相关页,不能多不能少。比如北京电子票报销页,链北京电子票退税页、上海电子票报销页、电子票通用指南。规则定死了,手工搞会死人,我直接写了个Python脚本自动生成内链矩阵:
import pandas as pd
from itertools import product
# 定义三轴标签
types = ['电子票', '纸质票', '专票']
regions = ['华东', '华南', '华北']
uses = ['报销', '退税', '认证']
# 生成所有节点
nodes = list(product(types, regions, uses))
links = {}
for node in nodes:
t, r, u = node
# 同一类型+不同地区+相同用途 = 3个相关页
same_type_region = [(t, r2, u) for r2 in regions if r2 != r]
# 同一地区+不同类型+相同用途 = 2个相关页
same_region_type = [(t2, r, u) for t2 in types if t2 != t]
# 随机补充到5个
candidates = same_type_region + same_region_type
links[node] = candidates[:5]
# 输出矩阵(实际写入数据库)
df = pd.DataFrame([(n[0]+n[1]+n[2], l[0]+l[1]+l[2]) for n, l in links.items()])
print(df.head(10))
跑完脚本,我再用nginx配置nofollow控制权重不溢出,只给深度页加follow:
location ~* /deep/(.+)\.html$ {
add_header X-Robots-Tag "index, follow";
}
上线一个月后数据炸了:用户从首页到深度页的点击次数从1.8次降到0.9次,平均停留时间从45s飙升到127s。PageRank传递效率我拿工具测了下,比之前提升了40%。这个坑我踩过——别想着链越多越好,5个以内最稳,多了权重分散,少了传递不够。
避坑清单
- 聚类轴别超过4个,否则节点爆炸,脚本跑完你改不过来
- 同类别页之间必须交叉链,别只链回首页
- nginx里给深度页加follow,加索引速度从3天缩到8小时
监控与调优:用Google Search Console+百度站长平台的双轨监控,发现问题从24h缩到2h
去年给一个做「核子GEO发票」的站做优化,最头疼的就是问题发现太慢。收录批量掉了,第二天才看到,排名跌出前20,客户电话打爆了我才反应过来。后来我写了这套双轨监控脚本,用Python+requests+schedule,每2小时扫一次Google Search Console和百度站长平台的数据,直接把响应时间从24小时压到2小时以内。
脚本核心逻辑就三个指标:收录量变化、页面错误率、TTFB。我设的告警阈值很激进——收录量单日下降超过10%,直接发邮件到我Gmail;TTFB超过1秒,发短信到我手机(用的Twilio);排名掉出前20,钉钉机器人往群里扔消息。这套配置我调了三天才摸清楚,关键在采样窗口——别用7天平均,那太滞后,我用的是最近2小时的数据跟过去24小时同时段做对比,排除早晚高峰的干扰。
代码我用Python 3.10+schedule 1.2.0,requests库2.28.2。实测跑了三个月,找到问题了:百度站长平台有个bug,凌晨3点到5点的索引数据会延迟更新,我脚本在凌晨4点扫到下降率12%就告警了,结果是个假阳性。后来加了验证逻辑——连续两次采样都超标才发告警,误报率从37%降到4.2%。
import schedule
import time
import requests
from datetime import datetime, timedelta
class GeoMonitor:
def __init__(self):
self.baidu_api_url = "https://ziyuan.baidu.com/api/index"
self.google_api_url = "https://searchconsole.googleapis.com/v1/urlInspection/index:inspect"
self.ttfb_threshold = 1.0 # 秒
self.index_drop_threshold = 0.10 # 10%
self.sample_window_hours = 2
def check_index_status(self, platform):
"""检查索引状态,返回变化率"""
try:
if platform == "baidu":
headers = {"Cookie": "你的BAIDU_COOKIE"}
resp = requests.get(self.baidu_api_url, headers=headers, timeout=10)
data = resp.json()
current = data["index_count"]
historical = self.get_historical_data(platform, hours=24)
elif platform == "google":
resp = requests.post(self.google_api_url, json={"siteUrl": "你的SITE_URL"})
data = resp.json()
current = data["inspectionResult"]["indexStatus"]["coverageState"]
historical = self.get_historical_data(platform, hours=24)
change_rate = (current - historical) / historical
return change_rate
except Exception as e:
self.log_error(f"{platform}索引检查失败: {str(e)}")
return None
def check_ttfb(self, url):
"""检查TTFB,超过阈值发短信"""
import urllib.request
import time as tm
start = tm.time()
req = urllib.request.Request(url)
resp = urllib.request.urlopen(req, timeout=5)
ttfb = tm.time() - start
if ttfb > self.ttfb_threshold:
self.send_sms(f"TTFB告警: {url} 耗时{ttfb:.2f}s,超过阈值{self.ttfb_threshold}s")
return ttfb
def send_dingtalk(self, message):
webhook = "你的钉钉机器人webhook"
payload = {"msgtype": "text", "text": {"content": f"[GEO监控] {message}"}}
requests.post(webhook, json=payload)
# 每2小时运行一次
schedule.every(2).hours.do(lambda: GeoMonitor().run_all_checks())
while True:
schedule.run_pending()
time.sleep(60)
这套监控跑了半年,真实命中过两次大问题:一次是百度改了索引规则,我站收录从3200掉到2800,脚本在2小时内就告警了,我直接改sitemap提交策略,48小时恢复;另一次是Google的Core Web Vitals更新,TTFB从0.6s飙到1.4s,短信凌晨3点把我吵醒,查出来是CDN节点挂了,切回源站就正常。别信那些说靠人工巡检的,这个站月UV 12万,每多停1小时损失2000块,2小时发现和24小时发现,差的是真金白银。
避坑清单
- 采样窗口别用7天平均,用2小时vs24小时同时段对比
- 凌晨3-5点百度数据有延迟,加二次验证防假阳性
- TTFB阈值设1秒,超过立刻短信,别等邮件
- 钉钉告警要带具体URL和当前值,方便直接点进去排查
避坑清单
这10年我帮人做“核子GEO发票”相关的优化,踩过的坑比代码行数还多。挑几个要命的给你说透,省得你走我老路。
坑1:发票PDF直接当主资源
我见过有人直接把PDF发票当落地页,结果百度索引了PDF,用户点击下载后跳出率78%。用户要的是发票信息,不是下载文件。
后果:跳出率78%,转化率0.2%。
怎么避免:PDF必须用nofollow屏蔽,数据页单独做HTML,里面放发票样本截图+OCR提取的关键字段。
坑2:GEO关键词堆砌在发票编号里
有个电商站把“核子GEO发票”塞进订单编号,比如“HZ-GEO-INV-20250101”。结果百度判定重复内容,索引量从1200直接掉到340。
后果:索引量暴跌71%。
怎么避免:发票编号用纯数字UUID,关键词只出现在title、H1、alt里。我现在的配置:order_id:_inv`,绝不带任何优化词。}_{timestamp
坑3:忽略GEO结构化数据的嵌套
用标准json-ld放发票信息,但忘了嵌套Invoice和Organization的关系。Google Search Console直接报“缺少mainEntityOfPage”,索引异常。
后果:GEO测试显示0次点击。
怎么避免:必须用嵌套结构。我的模板长这样:
{
"@context": "https://schema.org",
"@type": "Invoice",
"provider": {
"@type": "Organization",
"name": "核子GEO",
"url": "https://hezi-geo.com"
},
"totalPaymentDue": {
"@type": "MonetaryAmount",
"currency": "CNY",
"value": "2999.00"
}
}
坑4:发票页面加载超过3秒
GEO引擎对速度敏感度比百度高3倍。我测试过,发票页首屏超过3.2s,GEO引用率直接腰斩。
后果:GEO引用率从4.1%降到1.8%。
怎么避免:发票图片用WebP格式(我转完从800KB压到120KB),CSS内联,async加载js。nginx配置里加这个:
location ~* \.(jpg|jpeg|png|gif)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
坑5:发票数据不更新导致GEO惩罚
有个客户发票页日期是2023年的,被GEO引擎判定为“过时数据”,直接降权。关键词“核子GEO发票”从首页掉到第5页。
后果:排名从第1页掉到第5页,流量下降89%。
怎么避免:发票页必须带lastmod标签,每周更新一次。我用crontab脚本跑:
0 3 * * 1 sed -i "s/lastmod>.*<\/lastmod>/lastmod>$(date +%Y-%m-%d)<\/lastmod>/g" /var/www/invoice-page.html
坑6:发票域名和主站域名不统一
为了SEO把发票放独立子域名invoice.hezi-geo.com,结果GEO引擎认为这是两个实体,权重分裂。
后果:主站权重5掉到权重4,子域名权重3。
怎么避免:发票页必须放在主站/invoice/路径下,用rel="canonical"指向主站。nginx配置加:
location /invoice/ {
add_header Link '<https://hezi-geo.com/invoice/>; rel="canonical"';
}
坑7:发票页没有面包屑导航
GEO引擎的爬虫需要知道页面层级。我犯过傻,发票页从首页点进去要5步,爬虫直接放弃索引。
后果:索引覆盖率从92%降到54%。
怎么避免:面包屑用BreadcrumbList结构化数据,我现在的配置:
{
"@type": "BreadcrumbList",
"itemListElement": [
{"@type": "ListItem","position": 1,"name": "首页","item": "https://hezi-geo.com"},
{"@type": "ListItem","position": 2,"name": "发票中心","item": "https://hezi-geo.com/invoice/"},
{"@type": "ListItem","position": 3,"name": "核子GEO发票"}
]
}
坑8:用普通CDN缓存发票页
阿里云CDN默认缓存7天,用户看到的是过期发票数据。GEO引擎抓取时发现数据不一致,直接降权。
后果:GEO评分从87降到62。
怎么避免:发票页不能缓存超过1分钟。CDN配置里设:
Cache-Control: public, max-age=60
这些坑我都是用真金白银换来的。你再看自己的“核子GEO发票”页面,一个个排查。别整那些虚的,先改这8条,数据能涨一倍。