sitemap.xml 到底还有没有用
这些年 SEO 圈有个说法:「Google 早就不看 sitemap 了,提交也没用。」这话对了一半。对于已经收录、权重高的老站,爬虫本身抓得很勤,sitemap 的边际作用确实不大。但对新站、更新频繁的站、以及结构较深(点击层级超过三层)的站,sitemap 依然是让搜索引擎第一时间发现新页面的重要渠道。2020 年后 Google 甚至支持了 sitemap 索引文件(sitemap index),能一次提交几万个页面,Bing 也跟进支持。
更重要的是,sitemap 是你主动告诉搜索引擎「我站上有哪些页面」的清单。与其干等着爬虫随机发现,不如把地图递过去。这篇文章从 sitemap 的正确格式讲起,到自动生成、分片、GSC 提交、以及「提交了却没收录」的排查,形成一条完整闭环。
一个合法的 sitemap.xml 长什么样
标准格式是 sitemap 协议定义的,根元素必须带命名空间:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/hello-world.html</loc>
<lastmod>2026-10-12</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
</urlset>
几个字段的取舍要说清楚。loc 是唯一必填项,必须是完整绝对 URL,且协议、域名要和站点的规范版本一致(用了 https 就别写 http,带 www 就统一带 www)。lastmod 是最有价值的字段,但它必须「诚实」——只有内容真的变了才更新,否则搜索引擎会逐渐忽略它。很多人每天把全站 lastmod 刷成今天,结果就是搜索引擎彻底不信任这个字段。
changefreq 和 priority 这两个字段,Google 官方已经明确表示「不使用」。Bing 曾经参考过,但现在也基本忽略。所以别在这两个上面花心思,保留默认即可,重点保证 loc 正确、lastmod 准确。
用 PHP 给动态站自动生成 sitemap
如果你的站是 PHP + 数据库,最省事的做法是写一个 sitemap.php,让它输出 XML。下面这段代码从数据库读出所有已发布文章,输出标准的 urlset:
<?php
header('Content-Type: application/xml; charset=utf-8');
$pdo = new PDO('mysql:host=127.0.0.1;dbname=mysite;charset=utf8mb4', 'user', 'pass');
$base = 'https://example.com';
echo '<?xml version="1.0" encoding="UTF-8"?>';
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
$stmt = $pdo->query("SELECT path, updated FROM pages WHERE status=1 ORDER BY updated DESC");
foreach ($stmt as $row) {
$loc = htmlspecialchars($base . $row['path'], ENT_XML1);
$lm = date('Y-m-d', $row['updated']);
echo "<url><loc>$loc</loc><lastmod>$lm</lastmod></url>";
}
echo '</urlset>';
注意这里用 ENT_XML1 而不是默认的 HTML 转义,因为 XML 的转义规则和 HTML 不完全一样。URL 里的与号(&)必须转义成对应的实体形式,否则整个 sitemap 会解析失败——这是新手最常见的错误之一,一提交 GSC 就报「无法读取 sitemap」。
然后加一条 rewrite 规则,让 /sitemap.xml 指向这个 PHP。Nginx 里这样写:
location = /sitemap.xml {
rewrite ^ /sitemap.php last;
}
站点大了必须分片:sitemap index
单个 sitemap 文件有两个硬限制:最多 50000 个 URL,且未压缩前不超过 50MB。超过了就必须拆成多个文件,再用一个索引文件(sitemap index)把它们串起来:
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://example.com/sitemap-posts-1.xml</loc>
<lastmod>2026-10-12</lastmod>
</sitemap>
<sitemap>
<loc>https://example.com/sitemap-posts-2.xml</loc>
<lastmod>2026-10-11</lastmod>
</sitemap>
</sitemapindex>
注意 index 的根元素是 sitemapindex 而不是 urlset,每个条目用 <sitemap> 包裹而不是 <url>,内部只放 loc 和 lastmod,不要再套二级结构。给 GSC 提交时,只提交 index 这一个地址,它会自动顺着索引去抓分片。
分片时建议按内容类型拆,比如文章一个文件、分类页一个文件、标签页一个文件,这样「哪一类没被收录」一眼就能看出来,方便定位问题。
生成后立刻自检:别把坏地图递给搜索引擎
生成完先本地验证,用 xmllint 检查格式是否合法:
curl -s https://example.com/sitemap.xml | xmllint --noout -
没有报错说明语法没问题。接着抽查几件事:所有 loc 是否都能返回 200(可以用 xargs 批量 curl 头部);有没有误把 404、302、被 noindex 的页面塞进去;域名协议是否统一。凡是 sitemap 里出现 404,搜索引擎对你的整张地图的信任度都会下降。
还有一件事:确保 robots.txt 里用一行指向它。很多人只提交 GSC 却忘了写 robots,结果别的搜索引擎(Bing、DuckDuckGo 背后的索引)找不到你的地图:
Sitemap: https://example.com/sitemap.xml
在 Google Search Console 里正确提交
登录 GSC,先在「资源」里确认你的站点验证方式是「网域」还是「网址前缀」——这两种验证方式对应不同的数据视图,别提交到错误的资源下。然后左侧菜单进入「Sitemap(站点地图)」,输入 sitemap.xml(如果你把它放在域名根目录,直接填文件名即可),点提交。
提交后状态会显示「已成功读取」,过几天会显示「已发现的网页数」。这里有个关键认知:发现数 ≠ 收录数。GSC 显示发现了 500 个 URL,不代表 500 个都进了索引。真实收录情况要去「网页(Pages)」报告里看,那里才有「已编入索引」和「已发现 - 尚未编入索引」的明细。
「提交了却没收录」的分层排查
如果 sitemap 提交成功,索引里却迟迟没有页面,按下面顺序排查:
第一层,抓取层面。用 GSC 的「网址检查」工具,输入具体 URL,看「查看抓取的网页」——如果返回的是 403、429(被防火墙拦了爬虫)或者超时,就是服务器在拒绝 Googlebot。去 Nginx 日志里 grep 一下 Googlebot,看有没有被 deny 规则误伤,或者 CDN 的 WAF 拦了。这是最常见也最隐蔽的一类问题。
第二层,渲染层面。如果是 JS 渲染的站,Googlebot 拿到的是空壳 HTML,需要靠渲染队列。用「网址检查」的「屏幕截图」和「测试实际网页」看渲染后的内容,如果菜单、正文都没渲染出来,就要考虑服务端渲染(SSR)或预渲染(prerender)。
第三层,质量层面。页面被判定为「已发现 - 尚未编入索引」,多半是内容质量问题:内容太薄、和已有页面高度重复、或者站点整体信任度不够。新站普遍存在一个「观望期」,Google 先收着但不给排名,通常几周到几个月后逐步放量。这不是技术问题,急不来。
第四层,指令层面。确认页面 head 里没有 noindex,canonical 指向的是自己而不是别的页面,robots meta 没有误伤。这些低级错误一旦发生,sitemap 提交再多也没用。
动态站与静态站:两种自动化生成路径
前面给的 PHP 方案适合「每次访问实时生成」的动态站。它简单直接,但有两个小问题:每次请求都要查库,访问量大时略微浪费;而且如果数据库查询慢,sitemap 响应也会变慢。对中小站完全够用,但更稳妥的做法是「定期生成静态文件」。
思路是:不让 sitemap 实时查库,而是用 cron 定时跑一个脚本,把结果写成静态的 sitemap.xml 文件,直接由 Nginx 当静态文件发出去。这样响应快、不占数据库。crontab 里加一行,每天凌晨生成一次:
10 3 * * * /usr/bin/php /var/www/example/cron/build_sitemap.php > /dev/null 2>&1
脚本逻辑和动态版一样,只是把 echo 改成写入文件。用 PHP 的话,file_put_contents 写到 Nginx 根目录下的 sitemap.xml 即可。注意写完要保证文件权限正确(一般 www-data 可读),否则 Nginx 读不到会返回 403。
对于纯静态站(比如用 Hugo、Hexo 生成的),很多生成器本身就带 sitemap 插件,比如 Hugo 内置 sitemap.xml 模板,构建时自动产出。这类站只要确保构建配置里没把 sitemap 关掉,然后把它加进 robots.txt 和 GSC 就够了,不需要自己写生成逻辑。选路径的原则很简单:内容存的形态决定生成方式——数据库动态、文件静态,别硬凑。
lastmod 怎么取才准确
lastmod 的价值全在「准确」两个字上,而准确的来源是「内容最后修改时间」这个字段。数据库里应该有一个 updated 字段(Unix 时间戳或 datetime),在每次编辑保存时更新。如果你用的是 Typecho、WordPress 这类现成 CMS,这个字段通常已经有了,直接读就行。
坑在于:很多 CMS 的「发布时间」(created)和「修改时间」(modified)是分开的,别拿前者当 lastmod,否则文章改了地图上也不体现,搜索引擎就认为你没更新。另一个反向坑是模板渲染会顺带更新 modified——比如你换了个主题、动了一下 CSS,结果全站 modified 都变了,地图里 500 个页面的 lastmod 全刷成今天,搜索引擎一看就知道你在瞎报。所以 lastmod 最好绑定「正文内容」的修改,而不是「模板或渲染」的变化。
Google 对 lastmod 的容忍度是逐渐降低的:如果它反复发现你报的 lastmod 和实际内容变化对不上,就会选择忽略这个字段,之后你就算真的更新了也可能不触发重抓。所以这是一个「一旦不诚实就很难挽回」的字段,务必老实对待。
小结
sitemap.xml 不是玄学,也不该被神化。它是你主动递给搜索引擎的一张清单,价值在于「让新页面更快被发现」。做对它需要三件事:格式合规、URL 全 200、lastmod 诚实。提交到 GSC 后,把 sitemap 的「发现数」和「Pages 报告的收录数」分开看,遇到不收录按抓取、渲染、质量、指令四层排查。工具用对了,新站的收录节奏会顺畅很多。