文章系列导航
- 第01篇 - 选择困难症发作
- 第02篇 - 搭建过程之:披荆斩棘,晋级啦!
- 第03篇 - 搭建过程之:一路踩坑
- 第04篇 - 备份Hexo博客的源码目录,上传到Gitee仓库
- 第05篇 - 更换Hexo主题
- 第06篇 - 怎么给文章分类、打标签、展示目录
- 第07篇 - 怎么制作/about/、/categories/、/tags/页面
- 第08篇 - 让世界找到我------SEO大冒险(站点地图制作和提交)
- 第09篇 - 添加统计字数和阅读时长的插件
- 第10篇 - 怎么插入图片和视频
- 第11篇 - 怎么创建文章系列导航
- 第12篇 - 自动提交URL到搜索引擎(IndexNow + Google Search Console)
- 持续更新中...
本文目录
-
- [1 为什么需要自动提交](#1 为什么需要自动提交)
- [2 技术方案对比](#2 技术方案对比)
- [3 方案一:IndexNow 自动提交](#3 方案一:IndexNow 自动提交)
-
- [3.1 获取 API Key](#3.1 获取 API Key)
- [3.2 创建密钥验证文件](#3.2 创建密钥验证文件)
- [3.3 配置 Hexo](#3.3 配置 Hexo)
- [3.4 创建 IndexNow 插件](#3.4 创建 IndexNow 插件)
- [3.5 更新 .gitignore](#3.5 更新 .gitignore)
- [3.6 测试 IndexNow](#3.6 测试 IndexNow)
- [4 方案二:Google Search Console 自动提交](#4 方案二:Google Search Console 自动提交)
-
- [4.1 创建 Google Cloud 项目](#4.1 创建 Google Cloud 项目)
- [4.2 启用 Search Console API](#4.2 启用 Search Console API)
- [4.3 创建服务账号](#4.3 创建服务账号)
- [4.4 下载服务账号密钥](#4.4 下载服务账号密钥)
- [4.5 在 Search Console 中授权服务账号](#4.5 在 Search Console 中授权服务账号)
- [4.6 配置 Hexo](#4.6 配置 Hexo)
- [4.7 安装依赖](#4.7 安装依赖)
- [4.8 创建 Google Search Console 插件](#4.8 创建 Google Search Console 插件)
- [4.9 测试 Google Search Console](#4.9 测试 Google Search Console)
- [5 代理配置(可选)](#5 代理配置(可选))
-
- [5.1 创建代理管理脚本](#5.1 创建代理管理脚本)
- [5.2 使用代理](#5.2 使用代理)
- [5.3 使用代理的第二种方法](#5.3 使用代理的第二种方法)
- [6 验证效果](#6 验证效果)
-
- [6.1 IndexNow 验证](#6.1 IndexNow 验证)
- [6.2 Google Search Console 验证](#6.2 Google Search Console 验证)
- [7 常见问题](#7 常见问题)
-
- [Q1: IndexNow 提交失败怎么办?](#Q1: IndexNow 提交失败怎么办?)
- [Q2: Google Search Console 显示"Couldn't fetch"怎么办?](#Q2: Google Search Console 显示"Couldn't fetch"怎么办?)
- [Q3: 代理设置会影响正常上网吗?](#Q3: 代理设置会影响正常上网吗?)
- [Q4: 为什么要用缓存机制?](#Q4: 为什么要用缓存机制?)
- [Q5: 方案一:IndexNow 自动提交,首次部署会提交所有 URL 吗?](#Q5: 方案一:IndexNow 自动提交,首次部署会提交所有 URL 吗?)
- [8 优势总结](#8 优势总结)
- [9 参考资料](#9 参考资料)
在第08篇中,我们学习了如何制作和提交站点地图(sitemap)。但是,仅仅提交 sitemap 还不够快,搜索引擎可能需要几天甚至几周才会抓取你的新内容。
今天,我们来实现一个更高效的方案:在每次部署时,自动通知搜索引擎索引新增或变化的 URL。
1 为什么需要自动提交
传统方式的问题
- 被动等待:搜索引擎需要定期抓取 sitemap,可能需要几天到几周
- 效率低下:即使提交了 sitemap,搜索引擎也不知道哪些内容是新增的
- 索引慢:新文章可能很久才会出现在搜索结果中
自动提交的优势
- 主动通知:每次部署后立即通知搜索引擎
- 快速索引:新内容可能在几分钟到几小时内被索引
- 智能提交:只提交有变化的 URL,节省资源
- 覆盖全面:同时支持 Google、Bing、Yandex 等搜索引擎
2 技术方案对比
我们将实现两个自动提交方案:
| 方案 | 支持的搜索引擎 | 提交方式 | 配置复杂度 |
|---|---|---|---|
| IndexNow | Bing、Yandex 等 | 提交 URL 列表 | 简单 ⭐ |
| Google Search Console | 仅 Google | 提交 sitemap URL | 中等 ⭐⭐⭐ |
两者配合使用,可以覆盖所有主流搜索引擎。
3 方案一:IndexNow 自动提交
IndexNow 是一个开放协议,可以快速通知 Bing、Yandex 等搜索引擎。
3.1 获取 API Key
- 访问 Bing Webmaster Tools
- 登录并添加你的网站(如果还没有添加)
- 进入网站管理界面:
https://www.bing.com/webmasters/submiturl?siteUrl=你的网站地址- 例如:
https://www.bing.com/webmasters/submiturl?siteUrl=https://wittzh.github.io/
- 例如:
- 点击右上角的"设置"菜单(齿轮图标)
- 在下拉菜单中找到"API 访问"选项
- 点击进入后,可以看到"OAuth 客户端"和"API 密钥",点击"API 密钥",可以查看你的"API 密钥",如果没有,则进行创建。
- 复制生成的 API Key(类似:
3b259475ff0a4125835bd8b9ac314eee)
3.2 创建密钥验证文件
在 source/ 目录下创建一个文本文件,文件名为你的 API 密钥:
bash
# 在 source/ 目录下创建文件
echo "3b259475ff0a4125835bd8b9ac314eee" > source/3b259475ff0a4125835bd8b9ac314eee.txt
这个文件用于验证你拥有该网站的所有权。
3.3 配置 Hexo
在 _config.yml 文件末尾添加 IndexNow 配置:
yaml
# IndexNow配置 - 自动提交URL到搜索引擎
indexnow:
enable: true # 是否启用自动提交
key: "3b259475ff0a4125835bd8b9ac314eee" # 你的 API Key
keyLocation: "https://你的域名/3b259475ff0a4125835bd8b9ac314eee.txt" # 密钥文件URL
3.4 创建 IndexNow 插件
在 scripts/ 目录下创建 indexnow.js 文件:
javascript
'use strict';
/**
* IndexNow自动提交插件
* 在hexo deploy后自动向IndexNow API提交变化的URL
* 帮助搜索引擎快速发现和索引网站内容
*
* 优化:只提交有变化的URL(新增、更新、删除)
*/
const https = require('https');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
/**
* 读取并验证IndexNow配置
* @param {Object} hexo - Hexo实例
* @returns {Object|null} 配置对象或null
*/
function readConfig(hexo) {
const config = hexo.config.indexnow;
// 检查配置是否存在
if (!config) {
hexo.log.info('[IndexNow] 未配置indexnow,跳过提交');
return null;
}
// 检查是否启用
if (!config.enable) {
hexo.log.info('[IndexNow] indexnow.enable=false,跳过提交');
return null;
}
// 验证必需字段
if (!config.key) {
hexo.log.warn('[IndexNow] 配置错误: 缺少key字段');
return null;
}
if (!config.keyLocation) {
hexo.log.warn('[IndexNow] 配置错误: 缺少keyLocation字段');
return null;
}
// 从站点URL提取host
const siteUrl = hexo.config.url;
if (!siteUrl) {
hexo.log.warn('[IndexNow] 配置错误: 缺少站点URL');
return null;
}
const host = new URL(siteUrl).hostname;
return {
enable: config.enable,
key: config.key,
keyLocation: config.keyLocation,
host: host,
siteUrl: siteUrl
};
}
/**
* 从 sitemap XML 文件中提取 URL
* @param {String} filePath - sitemap 文件路径
* @returns {Array<String>} URL数组
*/
function extractUrlsFromSitemap(filePath) {
const urls = [];
try {
const content = fs.readFileSync(filePath, 'utf8');
// 使用正则表达式提取所有 <loc> 标签中的 URL
const locRegex = /<loc>(.*?)<\/loc>/g;
let match;
while ((match = locRegex.exec(content)) !== null) {
urls.push(match[1]);
}
} catch (error) {
// 文件不存在或读取失败,返回空数组
}
return urls;
}
/**
* 计算 URL 列表的哈希值(用于快速比较)
* @param {Array<String>} urls - URL数组
* @returns {String} 哈希值
*/
function calculateUrlsHash(urls) {
const sortedUrls = [...urls].sort().join('\n');
return crypto.createHash('md5').update(sortedUrls).digest('hex');
}
/**
* 加载上次提交的 URL 缓存
* @param {String} cacheFile - 缓存文件路径
* @returns {Object} 缓存对象 {urls: [], hash: '', timestamp: ''}
*/
function loadUrlCache(cacheFile) {
try {
if (fs.existsSync(cacheFile)) {
const content = fs.readFileSync(cacheFile, 'utf8');
return JSON.parse(content);
}
} catch (error) {
// 缓存文件不存在或损坏,返回空对象
}
return { urls: [], hash: '', timestamp: '' };
}
/**
* 保存 URL 缓存
* @param {String} cacheFile - 缓存文件路径
* @param {Array<String>} urls - URL数组
*/
function saveUrlCache(cacheFile, urls) {
try {
const cache = {
urls: urls,
hash: calculateUrlsHash(urls),
timestamp: new Date().toISOString()
};
fs.writeFileSync(cacheFile, JSON.stringify(cache, null, 2), 'utf8');
} catch (error) {
// 保存失败不影响主流程
}
}
/**
* 检测 URL 变化
* @param {Array<String>} currentUrls - 当前URL列表
* @param {Array<String>} cachedUrls - 缓存的URL列表
* @returns {Object} {changed: [], added: [], removed: []}
*/
function detectUrlChanges(currentUrls, cachedUrls) {
const currentSet = new Set(currentUrls);
const cachedSet = new Set(cachedUrls);
// 新增的 URL
const added = currentUrls.filter(url => !cachedSet.has(url));
// 删除的 URL
const removed = cachedUrls.filter(url => !currentSet.has(url));
// 所有变化的 URL(新增 + 删除)
const changed = [...added, ...removed];
return { changed, added, removed };
}
/**
* 收集所有需要提交的URL(从生成的文件中读取)
* @param {Object} hexo - Hexo实例
* @returns {Array<String>} URL数组
*/
function collectUrlsFromGenerated(hexo) {
const urls = new Set();
const log = hexo.log;
const publicDir = hexo.public_dir;
log.info('[IndexNow] 从生成的文件中收集URL...');
// 1. 从 post-sitemap.xml 读取文章URL
const postSitemapPath = path.join(publicDir, 'post-sitemap.xml');
const postUrls = extractUrlsFromSitemap(postSitemapPath);
log.info('[IndexNow] 从 post-sitemap.xml 读取到 %d 个URL', postUrls.length);
postUrls.forEach(url => urls.add(url));
// 2. 从 page-sitemap.xml 读取页面URL
const pageSitemapPath = path.join(publicDir, 'page-sitemap.xml');
const pageUrls = extractUrlsFromSitemap(pageSitemapPath);
log.info('[IndexNow] 从 page-sitemap.xml 读取到 %d 个URL', pageUrls.length);
pageUrls.forEach(url => urls.add(url));
// 3. 添加主 sitemap
urls.add(hexo.config.url + '/sitemap.xml');
return Array.from(urls);
}
/**
* 提交URL到IndexNow API
* @param {Object} config - 配置对象
* @param {Array<String>} urls - URL数组
* @returns {Promise} Promise对象
*/
function submitToIndexNow(config, urls) {
return new Promise((resolve, reject) => {
// 构建请求体
const postData = JSON.stringify({
host: config.host,
key: config.key,
keyLocation: config.keyLocation,
urlList: urls
});
// 配置HTTPS请求
const options = {
hostname: 'api.indexnow.org',
port: 443,
path: '/IndexNow',
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': Buffer.byteLength(postData)
},
timeout: 30000 // 30秒超时
};
// 发送请求
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
if (res.statusCode === 200) {
resolve({
success: true,
statusCode: res.statusCode,
message: '提交成功'
});
} else {
reject(new Error(`HTTP ${res.statusCode}: ${data || '未知错误'}`));
}
});
});
// 错误处理
req.on('error', (error) => {
reject(new Error(`网络错误: ${error.message}`));
});
req.on('timeout', () => {
req.destroy();
reject(new Error('请求超时(30秒)'));
});
// 发送数据
req.write(postData);
req.end();
});
}
// 在部署完成后提交URL
hexo.on('deployAfter', async function() {
const log = hexo.log;
try {
log.info('[IndexNow] 开始处理URL提交...');
// 读取配置
const config = readConfig(hexo);
if (!config) {
return;
}
log.info('[IndexNow] 配置读取成功');
// 从生成的文件中收集URL
const currentUrls = collectUrlsFromGenerated(hexo);
log.info('[IndexNow] 当前共收集到 %d 个URL', currentUrls.length);
if (currentUrls.length === 0) {
log.warn('[IndexNow] 没有URL需要提交');
return;
}
// 缓存文件路径
const cacheFile = path.join(hexo.base_dir, '.indexnow-cache.json');
// 加载上次提交的URL缓存
const cache = loadUrlCache(cacheFile);
log.info('[IndexNow] 上次提交了 %d 个URL (时间: %s)',
cache.urls.length,
cache.timestamp || '从未提交'
);
// 检测URL变化
const changes = detectUrlChanges(currentUrls, cache.urls);
if (changes.changed.length === 0) {
log.info('[IndexNow] ✓ 没有URL变化,跳过提交');
log.info('[IndexNow] 提示: 只有在URL新增、更新或删除时才会提交');
return;
}
// 显示变化统计
log.info('[IndexNow] 检测到URL变化:');
if (changes.added.length > 0) {
log.info('[IndexNow] 新增: %d 个URL', changes.added.length);
changes.added.forEach(url => {
log.info('[IndexNow] + %s', url);
});
}
if (changes.removed.length > 0) {
log.info('[IndexNow] 删除: %d 个URL', changes.removed.length);
changes.removed.forEach(url => {
log.info('[IndexNow] - %s', url);
});
}
log.info('[IndexNow] 总计: %d 个URL需要提交', changes.changed.length);
// 调用API提交变化的URL
log.info('[IndexNow] 正在提交到 api.indexnow.org...');
const result = await submitToIndexNow(config, changes.changed);
log.info('[IndexNow] ✓ 提交成功! 状态码: %d', result.statusCode);
log.info('[IndexNow] 已通知搜索引擎索引 %d 个变化的URL', changes.changed.length);
// 保存当前URL列表到缓存
saveUrlCache(cacheFile, currentUrls);
log.info('[IndexNow] URL缓存已更新');
} catch (error) {
log.warn('[IndexNow] 执行失败:', error.message);
log.warn('[IndexNow] 提交失败不影响部署流程');
}
});
3.5 更新 .gitignore
将缓存文件添加到 .gitignore,避免提交到 git:
# IndexNow URL 缓存文件
.indexnow-cache.json
3.6 测试 IndexNow
运行部署命令:
bash
hexo clean && hexo g && hexo d
你应该看到类似的日志输出:
[IndexNow] 开始处理URL提交...
[IndexNow] 配置读取成功
[IndexNow] 从生成的文件中收集URL...
[IndexNow] 从 post-sitemap.xml 读取到 13 个URL
[IndexNow] 从 page-sitemap.xml 读取到 2 个URL
[IndexNow] 当前共收集到 16 个URL
[IndexNow] 上次提交了 0 个URL (时间: 从未提交)
[IndexNow] 检测到URL变化:
[IndexNow] 新增: 16 个URL
[IndexNow] + https://你的域名/
[IndexNow] + https://你的域名/2026/01/25/new-article/
[IndexNow] ...
[IndexNow] 总计: 16 个URL需要提交
[IndexNow] 正在提交到 api.indexnow.org...
[IndexNow] ✓ 提交成功! 状态码: 200
[IndexNow] 已通知搜索引擎索引 16 个变化的URL
[IndexNow] URL缓存已更新
4 方案二:Google Search Console 自动提交
Google 不支持 IndexNow,需要使用 Google Search Console API。
4.1 创建 Google Cloud 项目
- 访问 Google Cloud Console
- 点击"新建项目"
- 输入项目名称(如:
hexo-blog-indexing) - 点击"创建"
4.2 启用 Search Console API
- 在 Google Cloud Console 中,选择刚创建的项目
- 点击左侧菜单"API 和服务" > "库"
- 搜索"Google Search Console API"
- 点击进入,然后点击"启用"
4.3 创建服务账号
- 点击左侧菜单"API 和服务" > "凭据"
- 点击"创建凭据" > "服务账号"
- 填写服务账号名称(如:
hexo-blog-service-account) - 点击"创建并继续"
- 跳过权限设置,点击"完成"
4.4 下载服务账号密钥
- 在"凭据"页面,找到刚创建的服务账号
- 点击服务账号邮箱进入详情页
- 切换到"密钥"标签页
- 点击"添加密钥" > "创建新密钥"
- 选择"JSON"格式
- 点击"创建",密钥文件会自动下载
4.5 在 Search Console 中授权服务账号
- 打开下载的 JSON 密钥文件,找到
client_email字段 - 复制该邮箱地址(类似:
hexo-blog-service-account@project-id.iam.gserviceaccount.com) - 访问 Google Search Console
- 选择你的网站
- 点击左侧"设置" > "用户和权限"
- 点击"添加用户"
- 粘贴服务账号邮箱地址
- 权限选择"所有者"(必须是所有者,如果页面语言是英文,则是 Owner)
- 点击"添加"
4.6 配置 Hexo
- 将下载的 JSON 密钥文件重命名为
google-service-account.json - 放到 Hexo 项目根目录(与
_config.yml同级) - 在
_config.yml末尾添加配置:
yaml
# Google Search Console 配置
google_search_console:
enable: true # 启用后改为 true
service_account_file: "google-service-account.json"
-
更新
.gitignore,添加:Google Search Console 服务账号密钥文件
google-service-account.json
4.7 安装依赖
bash
npm install googleapis
4.8 创建 Google Search Console 插件
在 scripts/ 目录下创建 google-search-console.js 文件:
javascript
'use strict';
/**
* Google Search Console 自动提交插件
* 在 hexo deploy 后自动向 Google Search Console 提交 sitemap
* 帮助 Google 快速发现和索引网站内容
*/
const { google } = require('googleapis');
const fs = require('fs');
const path = require('path');
// 设置代理以访问 Google API
process.env.http_proxy = 'http://127.0.0.1:7897';
process.env.https_proxy = 'http://127.0.0.1:7897';
/**
* 读取并验证 Google Search Console 配置
* @param {Object} hexo - Hexo 实例
* @returns {Object|null} 配置对象或 null
*/
function readConfig(hexo) {
const config = hexo.config.google_search_console;
// 检查配置是否存在
if (!config) {
hexo.log.info('[Google Search Console] 未配置 google_search_console,跳过提交');
return null;
}
// 检查是否启用
if (!config.enable) {
hexo.log.info('[Google Search Console] google_search_console.enable=false,跳过提交');
return null;
}
// 验证必需字段
if (!config.service_account_file) {
hexo.log.warn('[Google Search Console] 配置错误: 缺少 service_account_file 字段');
return null;
}
// 获取站点 URL
const siteUrl = hexo.config.url;
if (!siteUrl) {
hexo.log.warn('[Google Search Console] 配置错误: 缺少站点 URL');
return null;
}
// 确保 siteUrl 以 / 结尾(Google Search Console 要求)
const normalizedSiteUrl = siteUrl.endsWith('/') ? siteUrl : siteUrl + '/';
// 构建 sitemap URL
const sitemapUrl = siteUrl + '/sitemap.xml';
return {
enable: config.enable,
serviceAccountFile: config.service_account_file,
siteUrl: normalizedSiteUrl,
sitemapUrl: sitemapUrl
};
}
/**
* 创建 Google OAuth 认证客户端
* @param {String} serviceAccountFile - 服务账号密钥文件路径
* @param {Object} log - 日志对象
* @returns {Promise<GoogleAuth>} 认证客户端
*/
async function createAuthClient(serviceAccountFile, log) {
// 检查文件是否存在
if (!fs.existsSync(serviceAccountFile)) {
throw new Error(`服务账号密钥文件不存在: ${serviceAccountFile}`);
}
log.info('[Google Search Console] 加载服务账号密钥...');
// 创建认证客户端
const auth = new google.auth.GoogleAuth({
keyFile: serviceAccountFile,
scopes: ['https://www.googleapis.com/auth/webmasters']
});
log.info('[Google Search Console] 认证客户端创建成功');
return auth;
}
/**
* 提交 sitemap 到 Google Search Console
* @param {GoogleAuth} auth - 认证客户端
* @param {String} siteUrl - 网站 URL
* @param {String} sitemapUrl - sitemap URL
* @param {Object} log - 日志对象
* @returns {Promise<void>}
*/
async function submitSitemap(auth, siteUrl, sitemapUrl, log) {
log.info('[Google Search Console] 正在提交 sitemap...');
log.info('[Google Search Console] 站点: %s', siteUrl);
log.info('[Google Search Console] Sitemap: %s', sitemapUrl);
// 创建 webmasters API 客户端
const webmasters = google.webmasters({ version: 'v3', auth });
try {
// 调用 sitemap.submit API
await webmasters.sitemaps.submit({
siteUrl: siteUrl,
feedpath: sitemapUrl
});
log.info('[Google Search Console] ✓ Sitemap 提交成功!');
} catch (error) {
// 解析 API 错误
if (error.response) {
const status = error.response.status;
const message = error.response.data?.error?.message || error.message;
throw new Error(`API 调用失败 (${status}): ${message}`);
} else {
throw error;
}
}
}
// 在部署完成后提交 sitemap
hexo.on('deployAfter', async function() {
const log = hexo.log;
try {
log.info('[Google Search Console] 开始处理 sitemap 提交...');
// 读取配置
const config = readConfig(hexo);
if (!config) {
return;
}
log.info('[Google Search Console] 配置读取成功');
// 创建认证客户端
const auth = await createAuthClient(config.serviceAccountFile, log);
// 提交 sitemap
await submitSitemap(auth, config.siteUrl, config.sitemapUrl, log);
log.info('[Google Search Console] 已通知 Google 索引网站内容');
} catch (error) {
log.error('[Google Search Console] 执行失败:', error.message);
log.warn('[Google Search Console] 提交失败不影响部署流程');
// 提供常见错误的解决建议
if (error.message.includes('文件不存在')) {
log.warn('[Google Search Console] 提示: 请确保服务账号密钥文件已正确放置');
} else if (error.message.includes('403')) {
log.warn('[Google Search Console] 提示: 请确保服务账号已在 Google Search Console 中授予所有者权限');
} else if (error.message.includes('404')) {
log.warn('[Google Search Console] 提示: 请确保站点已在 Google Search Console 中验证');
}
}
});
4.9 测试 Google Search Console
运行部署命令:
bash
hexo clean && hexo g && hexo d
你应该看到类似的日志输出:
[Google Search Console] 开始处理 sitemap 提交...
[Google Search Console] 配置读取成功
[Google Search Console] 加载服务账号密钥...
[Google Search Console] 认证客户端创建成功
[Google Search Console] 正在提交 sitemap...
[Google Search Console] 站点: https://你的域名/
[Google Search Console] Sitemap: https://你的域名/sitemap.xml
[Google Search Console] ✓ Sitemap 提交成功!
[Google Search Console] 已通知 Google 索引网站内容
5 代理配置(可选)
如果你在国内访问 Google API 有困难,需要配置代理。
5.1 创建代理管理脚本
创建 proxy-functions.sh 文件:
bash
#!/bin/bash
# 开启代理
proxy_on() {
export http_proxy=http://127.0.0.1:7897
export https_proxy=http://127.0.0.1:7897
export HTTP_PROXY=http://127.0.0.1:7897
export HTTPS_PROXY=http://127.0.0.1:7897
export no_proxy=localhost,127.0.0.1
echo "✅ 代理已开启: http://127.0.0.1:7897"
}
# 关闭代理
proxy_off() {
unset http_proxy
unset https_proxy
unset HTTP_PROXY
unset HTTPS_PROXY
unset no_proxy
echo "❌ 代理已关闭"
}
# 查看代理状态
proxy_status() {
if [ -n "$http_proxy" ]; then
echo "✅ 代理状态: 已开启"
echo " HTTP: $http_proxy"
echo " HTTPS: $https_proxy"
else
echo "❌ 代理状态: 已关闭"
fi
}
echo "代理管理函数已加载!"
echo "使用方法:"
echo " proxy_on - 开启代理"
echo " proxy_off - 关闭代理"
echo " proxy_status - 查看代理状态"
5.2 使用代理
bash
# 加载代理函数
source proxy-functions.sh
# 开启代理
proxy_on
# 部署(会使用代理访问 Google API)
hexo deploy
# 关闭代理
proxy_off
5.3 使用代理的第二种方法
在 google-search-console.js 或 indexnow.js 的开头,添加以下代码,作用是,在运行js脚本期间,能访问外网:
js
process.env.http_proxy = 'http://127.0.0.1:7897';
process.env.https_proxy = 'http://127.0.0.1:7897';
注意 :将 127.0.0.1:7897 替换为你的实际代理地址和端口。
6 验证效果
6.1 IndexNow 验证
- 查看部署日志,确认提交成功
- 等待几分钟到几小时
- 在 Bing 中搜索你的文章标题,看是否已被索引
6.2 Google Search Console 验证
- 登录 Google Search Console
- 选择你的网站
- 点击左侧"索引" > "站点地图"
- 查看 sitemap.xml 的状态
- 应该显示"成功",并且"上次读取时间"是最近的部署时间
7 常见问题
Q1: IndexNow 提交失败怎么办?
检查清单:
- 确认 API Key 正确
- 确认密钥验证文件已部署到网站
- 检查网络连接
- 查看详细错误日志
Q2: Google Search Console 显示"Couldn't fetch"怎么办?
Google Search Console管理界面中,Sitemaps菜单下,如果提交的/sitemap.xml,在很长时间内,状态一直是"Couldn't fetch",那么可以参考以下的解决方案。
解决方案:
-
创建
source/robots.txt文件:User-agent: *
Allow: /Sitemap: https://你的域名/sitemap.xml
-
重新部署并等待几天
-
在 Search Console 中手动请求重新抓取
Q3: 代理设置会影响正常上网吗?
答案:如果代理关闭了,终端会无法访问网络。
建议 :使用 proxy_on 和 proxy_off 函数灵活控制,不要永久设置代理。
Q4: 为什么要用缓存机制?
原因:
- 避免重复提交相同的 URL
- 节省 API 调用次数
- 符合 IndexNow 最佳实践(只提交变化的 URL)
Q5: 方案一:IndexNow 自动提交,首次部署会提交所有 URL 吗?
答案:是的。首次部署时缓存为空,会提交所有 URL。之后只提交有变化的 URL。
8 优势总结
使用自动提交方案后:
- 快速索引:新文章可能在几分钟到几小时内被索引
- 智能提交:只提交有变化的 URL,节省资源
- 覆盖全面:同时支持 Google、Bing、Yandex 等搜索引擎
- 完全自动:每次部署自动执行,无需手动操作
- 详细日志:清晰显示提交的 URL 和结果
再也不用担心新文章迟迟不被搜索引擎收录了!