sitemap.xml 怎麼產生?靜態站與前端框架的自動化做法
sitemap.xml 是一份 網址清單,主動告知搜尋引擎「這個網站有哪些頁面」。它負責 索引流程 的第一關,讓網址被發現。
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<lastmod>2026-09-07</lastmod>
</url>
<url>
<loc>https://example.com/about</loc>
<lastmod>2026-08-15</lastmod>
</url>
</urlset>欄位與它們的實際作用
| 欄位 | 必要性 | 實際作用 |
|---|---|---|
<loc> | 必填 | 網址,必須是絕對網址且經過網址編碼 |
<lastmod> | 選填 | 最後修改時間,誠實填寫時有參考價值 |
<changefreq> | 選填 | 已被忽略 |
<priority> | 選填 | 已被忽略 |
changefreq 與 priority 不必寫
Google 明確表示不使用這兩個欄位。原因和 meta keywords 一樣:完全由網站自行填寫、無法驗證,結果是所有人都把 <priority> 填 1.0。
寫了不會有壞處,但也沒有效果。省下來的力氣放在 <lastmod> 的正確性上更值得。
lastmod 要誠實
<lastmod> 是唯一還有參考價值的選填欄位,但前提是 填得誠實:
| 做法 | 後果 |
|---|---|
| 內容真的改了才更新時間 | 搜尋引擎會參考它決定重新抓取的優先順序 |
| 每次部署都填成當天 | 一段時間後這個欄位會被整體忽略 |
用 Git 的最後提交時間
最可靠的來源是版本控制。用檔案的最後提交時間當 <lastmod>,就不會因為部署而全部變成同一天:
git log -1 --format=%cI -- docs/src/seo/indexing-sitemap.md時間格式用 W3C Datetime,2026-09-07 或 2026-09-07T14:30:00+08:00 都可以。
數量上限與拆檔
| 限制 | 值 |
|---|---|
| 單檔網址數 | 5 萬筆 |
| 單檔大小 | 50MB(未壓縮) |
超過就要拆成多個檔案,再用一個 索引檔 把它們列出來:
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://example.com/sitemap-posts.xml</loc>
<lastmod>2026-09-07</lastmod>
</sitemap>
<sitemap>
<loc>https://example.com/sitemap-products.xml</loc>
<lastmod>2026-09-06</lastmod>
</sitemap>
</sitemapindex>索引檔本身同樣受 5 萬筆限制,所以理論上可容納 25 億個網址。
拆檔的另一個好處
即使沒有超過上限,按內容類型拆檔(文章、商品、分類頁)也很有用,Search Console 的涵蓋範圍報告可以 按 sitemap 分別檢視,一眼看出是哪一類內容收錄有問題。
只放該收錄的網址
這些網址不該出現在 sitemap 裡
| 不該放 | 為什麼 |
|---|---|
標了 noindex 的頁面 | 一邊說「請收錄」一邊說「別收錄」,訊號矛盾 |
被 robots.txt 封鎖的路徑 | 爬蟲根本下載不了 |
| 回傳 轉址或錯誤 的網址 | 浪費爬取預算 |
| 非正規版本的網址 | 應該只放 canonical 指向的那一個 |
| 分頁的第 2 頁以後 | 通常沒有獨立收錄的價值 |
sitemap 應該只包含您希望被收錄、而且回傳 200 的正規網址。 這是 Search Console 涵蓋範圍報告出現大量錯誤最常見的原因。
怎麼讓搜尋引擎知道
兩種方式,都做最保險:
1. 在 robots.txt 宣告
這是被動的,但爬蟲每次來都會看到。
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml2. 在 Search Console 提交
Search Console 的「Sitemap」頁面可以主動提交,並看到 它實際讀到幾筆、有幾筆有問題,這個回饋是 robots.txt 宣告給不了的。
自動產生
不要手工維護
手工維護的 sitemap 一定會過期。新增頁面忘了加、刪除頁面忘了移除,最後變成一份充滿錯誤的清單。
多數框架有內建支援或現成外掛,通常只要給定網域,建置時就會掃過所有頁面產出:
// 框架設定檔,欄位名稱各家略有不同
export default {
sitemap: {
hostname: domain,
},
};網域要來自環境變數,這樣測試機不會產出指向正式網域的 sitemap:
const domain = (process.env.SITE_HOST || 'http://localhost:5173').replace(/\/$/, '');自己寫的話,骨架大致是這樣:
import { writeFileSync } from 'node:fs';
const pages = await collectPages(); // 掃目錄或讀路由表
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${pages
.map(
({ path, lastmod }) => ` <url>
<loc>${domain}${path}</loc>
<lastmod>${lastmod}</lastmod>
</url>`,
)
.join('\n')}
</urlset>
`;
writeFileSync('dist/sitemap.xml', xml, 'utf-8');動態網址怎麼處理
電商的商品頁、部落格的文章頁通常來自資料庫,不在檔案系統裡。兩種做法:
| 做法 | 適合 |
|---|---|
| 建置時查資料庫,產出靜態 sitemap | 內容更新不頻繁 |
| 由伺服器動態產生 | 內容持續新增,例如:每日上架商品 |
動態產生時記得加上快取,不要每次請求都去撈全表:
// 每小時重新產生一次就夠
app.get('/sitemap.xml', cache('1 hour'), async (req, res) => {
const urls = await db.getPublishedUrls();
res.type('application/xml').send(buildSitemap(urls));
});其他類型的 sitemap
| 類型 | 什麼時候需要 |
|---|---|
| 圖片 sitemap | 圖片來自其他網域、由程式碼插入,或圖片就是主要內容 |
| 影片 sitemap | 影片是主要內容且需要在影片搜尋曝光 |
| 新聞 sitemap | 有新聞內容且已加入 Google 新聞 |
一般網站都不需要。圖片與影片的曝光更依賴 替代文字 與 結構化資料 。
檢查清單
| 項目 | 標準 |
|---|---|
sitemap.xml 存在且可存取 | 必備 |
在 robots.txt 宣告位置 | 建議 |
| 已在 Search Console 提交 | 建議 |
| 只包含回傳 200 的正規網址 | 必備 |
沒有包含 noindex 或被封鎖的網址 | 必備 |
| 網址是絕對網址且網域正確 | 必備 |
| 單檔未超過 5 萬筆與 50MB | 必備 |
| 由建置流程自動產生 | 建議 |
<lastmod> 反映真實修改時間 | 建議 |
常見問題
小網站需要 sitemap 嗎?
頁面數少且內部連結完整的網站,爬蟲光靠連結就走得完,sitemap 的邊際效益不高。但它成本極低且能加速新頁面被發現,仍然建議放一份。真正必要的情況是網站很大、內部連結稀疏,或有 孤兒頁面 沒有被任何連結指到。
changefreq 和 priority 有用嗎?
實際上已被忽略。Google 明確表示不使用這兩個欄位,因為它們完全由網站自行填寫、無法驗證,結果是所有人都把優先權填最高。只有 <lastmod> 在填寫誠實時仍有參考價值。
單一檔案可以放幾個網址?
上限是 5 萬筆網址且未壓縮前不超過 50MB。超過就要拆成多個檔案,再用一個索引檔把它們列出來。索引檔本身同樣受 5 萬筆的限制,所以理論上可容納 25 億個網址。
sitemap 裡該放哪些網址?
只放您希望被收錄、而且回傳 200 的正規網址。標了 noindex 的頁面、被封鎖的路徑、轉址與錯誤頁面都不該出現在裡面,那會發出互相矛盾的訊號並浪費爬取預算。
提交之後多久會被收錄?
沒有保證時間,從幾小時到數週都有可能。sitemap 是建議而非命令,搜尋引擎仍依網站整體重要性與爬取預算決定順序。要加速單一頁面可以用 網址檢查工具 主動請求索引。