Node.js 全站死链检测工具:从 sitemap 出发一次找全 404
做独立站,死链是排名和体验的双重杀手。买家点进一个链接结果是 404,马上关掉;谷歌爬虫反复撞到 404,会拉低对整个站的抓取效率和信任。站子一大,几十上百页里哪些链接已经失效,人工点一遍不现实。这篇写一个 Node.js 工具,从 sitemap 出发,把全站所有 URL 的返回状态一次查清,死链直接列出来。
sitemap 的官方说明在这里:Google sitemap 指南,链接要能被抓取的原则看这篇:可抓取的链接。
工具能检查什么
- 解析 sitemap(支持 sitemap index 嵌套)
- 并发请求每个 URL,记录 HTTP 状态码
- 分类:200 正常、3xx 重定向、404 死链、5xx 服务器错误、超时
- 输出死链清单,直接照着修
这个版本用 Node.js 原生 fetch,不需要装任何第三方依赖,Node 18 及以上直接跑。
完整代码
javascript
// deadlink-checker.js
// 用途:从 sitemap 出发,检查全站 URL 的返回状态,找出死链
// 运行:node deadlink-checker.js https://example.com/sitemap.xml
// 依赖:无(Node 18+ 自带 fetch)
const CONCURRENCY = 10; // 并发数,太大容易被目标站限流
function decodeEntities(s) {
return s
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, "'");
}
async function fetchStatus(url) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 10000); // 10 秒超时
try {
const res = await fetch(url, {
method: 'GET',
redirect: 'manual', // 手动处理,才能拿到 301/302 本身
signal: controller.signal,
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36' },
});
if (res.type === 'opaqueredirect') return 302;
return res.status;
} catch (e) {
return e.name === 'AbortError' ? 'TIMEOUT' : 'ERROR';
} finally {
clearTimeout(timer);
}
}
async function parseSitemap(url, urls = []) {
const status = await fetchStatus(url);
if (status !== 200) return urls;
const text = await (await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } })).text();
const locs = [...text.matchAll(/<loc>([^<]+)<\/loc>/g)].map((m) => decodeEntities(m[1].trim()));
for (const loc of locs) {
if (loc.endsWith('.xml')) {
await parseSitemap(loc, urls); // sitemap index 嵌套,递归解析
} else {
urls.push(loc);
}
}
return urls;
}
async function main(sitemapUrl) {
console.log(`解析 sitemap: ${sitemapUrl}\n` + '='.repeat(60));
const urls = await parseSitemap(sitemapUrl);
const unique = [...new Set(urls)];
if (!unique.length) {
console.log('sitemap 解析失败或没有 URL');
return;
}
console.log(`共 ${unique.length} 个页面,开始并发检查...\n`);
const results = { ok: [], redirect: [], notFound: [], serverError: [], other: [] };
const queue = [...unique];
const workers = Array.from({ length: CONCURRENCY }, async () => {
while (queue.length) {
const url = queue.shift();
const status = await fetchStatus(url);
if (status === 200) results.ok.push(url);
else if ([301, 302, 307, 308].includes(status)) results.redirect.push({ url, status });
else if (status === 404) results.notFound.push(url);
else if (typeof status === 'number' && status >= 500) results.serverError.push({ url, status });
else results.other.push({ url, status });
}
});
await Promise.all(workers);
console.log('='.repeat(60));
console.log(`正常 200: ${results.ok.length}`);
console.log(`重定向 3xx: ${results.redirect.length}`);
console.log(`死链 404: ${results.notFound.length}`);
console.log(`服务器错误 5xx: ${results.serverError.length}`);
console.log(`其他: ${results.other.length}`);
if (results.notFound.length) {
console.log('\n死链清单:');
results.notFound.forEach((u) => console.log(' 404 ' + u));
}
if (results.redirect.length) {
console.log('\n重定向清单:');
results.redirect.forEach((r) => console.log(` ${r.status} ${r.url}`));
}
if (results.serverError.length) {
console.log('\n服务器错误清单:');
results.serverError.forEach((r) => console.log(` ${r.status} ${r.url}`));
}
console.log('\n检查结束。');
}
const target = process.argv[2] || 'https://example.com/sitemap.xml';
main(target);
怎么用
bash
node deadlink-checker.js https://你的域名/sitemap.xml
把最后的地址换成你自己的 sitemap,运行。它会自动跟进嵌套的 sitemap index,把全站页面都查一遍,最后按状态码分类输出。
三个最常见的死链来源
产品下架但页面没处理。 外贸站产品经常上新下架,下架的产品如果直接删掉、没做 301 或没删内链,就留下一个 404。这些页面积累起来,站里就到处是坑。
换域名或改 URL 结构。 站点改版、换域名的过程中,老 URL 如果没做重定向,等于把之前积累的权重和收录全部扔了,还制造一批死链。
外链和内链指向了不存在的地址。 有些死链来自站内文章的旧链接,有些来自别处引用了你已经删掉的页面。前者你能自己修,后者要看情况做 301 或保留页面。
写在最后
死链检测门槛不高,难在定期做。站子一更新,就会冒出新的死链,建议每个月跑一次,把清单里的 404 逐个处理掉。觉得有用欢迎点赞、收藏、转发,有疑问评论区留言或私信交流。