WordPress 分类目录 SEO 自定义 TDK 现代化方案:termmeta API + REST 字段 + Yoast 共存策略
WordPress 默认分类页没有独立 TDK,对 SEO 极不友好。网传 wp_options 存 SEO 字段的方法在 2026 年已过时——termmeta API 才是标准做法。本文给出基于 wp_termmeta 的完整代码(后台编辑界面 + 保存 + 前台输出 + REST API 暴露 + Schema 协同)、与 Yoast/RankMath 共存策略、AI 批量生成高质量描述、跨语言 / 多站点适配。
WordPress 默认的分类(Category)和标签(Tag)页面只有"分类名 + 上下级关系"——前台 <title> 直接用分类名,<meta description> 与 <meta keywords> 完全没有,对 SEO 极不友好:每个分类页拿不到独立 TDK,权重传递低、SERP 不给点击率。
网传的修法是自己写 seocategory.php 给分类编辑页加 SEO 字段——但 2026 年这套代码有几个明显的过时点:用了已被弃用的 get_option() 误用、没用 WordPress 4.4+ 推荐的 termmeta API、不支持 Gutenberg block 编辑器的现代化字段交互。这一篇把"WP 分类自定义 TDK"重写成 2026 年标准做法:用 termmeta + REST API field + 与 Yoast/RankMath 共存的兼容写法。
一、WordPress 分类的"分类元数据"机制
WordPress 4.4 引入了 termmeta 表(wp_termmeta),专门存"分类/标签"的额外字段——比 wp_options 当 KV 存储更规范。术语层级关系:
| 层级 | 表 | API |
|---|---|---|
| 分类法(taxonomy) | wp_term_taxonomy | register_taxonomy() |
| 具体分类(term) | wp_terms | wp_insert_term() / get_term() |
| 分类元数据 | wp_termmeta | add_term_meta() / update_term_meta() / get_term_meta() |
| 文章 ↔ 分类关系 | wp_term_relationships | wp_set_object_terms() |
原帖把 SEO 字段存到 wp_options 表(cat_set_$cat_id)——能用但不规范,且 wp_options 表是全站共享,分类删除时清理不掉。正确做法是用 termmeta API。
二、现代化代码:term_meta + 后台编辑界面
把 functions.php 或独立 mu-plugin 放下面这段:
// === WordPress 分类自定义 SEO TDK ===
// 1. 在分类编辑页加 SEO 字段
add_action('category_edit_form_fields', 'mysite_category_seo_fields', 10, 2);
function mysite_category_seo_fields($term, $taxonomy) {
$seo_title = get_term_meta($term->term_id, 'seo_title', true);
$seo_description = get_term_meta($term->term_id, 'seo_description', true);
$seo_keywords = get_term_meta($term->term_id, 'seo_keywords', true);
?>
<tr class="form-field">
<th><label for="seo_title">SEO 标题</label></th>
<td>
<input type="text" name="seo_title" id="seo_title" value="<?php echo esc_attr($seo_title); ?>" size="50">
<p class="description">留空则自动用:分类名 + 站点名</p>
</td>
</tr>
<tr class="form-field">
<th><label for="seo_description">SEO 描述</label></th>
<td>
<textarea name="seo_description" id="seo_description" rows="3" cols="50"><?php echo esc_textarea($seo_description); ?></textarea>
<p class="description">Meta description,建议 120-160 字符</p>
</td>
</tr>
<tr class="form-field">
<th><label for="seo_keywords">SEO 关键词</label></th>
<td>
<input type="text" name="seo_keywords" id="seo_keywords" value="<?php echo esc_attr($seo_keywords); ?>" size="50">
<p class="description">多关键词用英文逗号分隔,3-5 个为宜</p>
</td>
</tr>
<?php
}
// 2. 保存分类时写入 termmeta
add_action('edited_category', 'mysite_save_category_seo', 10, 2);
function mysite_save_category_seo($term_id, $tt_id) {
if (!current_user_can('manage_categories')) return;
if (isset($_POST['seo_title'])) {
update_term_meta($term_id, 'seo_title', sanitize_text_field($_POST['seo_title']));
}
if (isset($_POST['seo_description'])) {
update_term_meta($term_id, 'seo_description', sanitize_textarea_field($_POST['seo_description']));
}
if (isset($_POST['seo_keywords'])) {
update_term_meta($term_id, 'seo_keywords', sanitize_text_field($_POST['seo_keywords']));
}
}
// 3. 前台分类页的 <title> 与 meta 输出
add_filter('document_title_parts', 'mysite_category_title');
function mysite_category_title($parts) {
if (is_category()) {
$term = get_queried_object();
$custom_title = get_term_meta($term->term_id, 'seo_title', true);
if (!empty($custom_title)) {
$parts['title'] = $custom_title;
} else {
// 兜底:分类名 + 共 N 篇文章
$parts['title'] = $term->name . ' 相关文章合集(共 ' . $term->count . ' 篇)';
}
}
return $parts;
}
// 4. 输出 meta description / keywords
add_action('wp_head', 'mysite_category_meta', 1);
function mysite_category_meta() {
if (!is_category()) return;
$term = get_queried_object();
$description = get_term_meta($term->term_id, 'seo_description', true);
if (empty($description)) {
// 兜底:用分类的描述(在分类编辑页有原生 description 字段)
$description = $term->description ?: '保哥笔记 ' . $term->name . ' 分类下共 ' . $term->count . ' 篇文章合集。';
}
$description = wp_trim_words(strip_tags($description), 80, '...');
$keywords = get_term_meta($term->term_id, 'seo_keywords', true);
if (empty($keywords)) {
$keywords = $term->name;
}
echo '<meta name="description" content="' . esc_attr($description) . '">' . "\n";
echo '<meta name="keywords" content="' . esc_attr($keywords) . '">' . "\n";
}关键设计:
- 用 term_meta 而不是 options:分类删除时元数据自动级联删除(
wp_delete_term会调delete_term_meta),不留垃圾; - 权限校验:
current_user_can('manage_categories')防止越权写入; - sanitize 转义:sanitize_text_field 防 XSS;
- document_title_parts filter:标准的 WP 5.0+ 标题修改 hook,不直接覆盖 wp_title();
- 三级回退:自定义 → 分类原生 description → 自动拼接默认值。
三、与 Yoast / RankMath 共存的兼容性
WordPress 圈最常用的 SEO 插件 Yoast 和 RankMath 都自带"分类 SEO 字段"功能——它们存在 wp_termmeta 表里的 key 是各自约定的:
| 插件 | title 字段 key | description 字段 key |
|---|---|---|
| Yoast SEO | _yoast_wpseo_title | _yoast_wpseo_metadesc |
| RankMath | rank_math_title | rank_math_description |
| SEOPress | _seopress_titles_title | _seopress_titles_desc |
| 本文方案 | seo_title | seo_description |
如果同时装 Yoast 和自写代码会冲突——两边都改 wp_head 里的 meta,输出两份。两种处理:
3.1 优先用插件
如果决定用 Yoast / RankMath,不要再写自定义代码——直接在插件后台填字段即可。Yoast / RankMath 的 SEO 字段 UI 和兜底逻辑比本文写的更完善。
3.2 共存策略:检测插件存在跳过自定义
add_action('wp_head', 'mysite_category_meta', 1);
function mysite_category_meta() {
if (!is_category()) return;
// 如果装了 Yoast 或 RankMath,跳过自定义输出
if (defined('WPSEO_VERSION') || defined('RANK_MATH_VERSION')) {
return;
}
// ... 自定义输出
}这样自写代码只在没装 SEO 插件的轻量站才生效,避免与插件双重输出。
四、REST API 暴露分类 SEO 字段
WordPress 5.0+ 的 Gutenberg / 移动应用 / 第三方对接都依赖 REST API。要让 /wp-json/wp/v2/categories 返回这些 SEO 字段:
add_action('rest_api_init', function () {
register_rest_field('category', 'seo', [
'get_callback' => function ($object) {
return [
'title' => get_term_meta($object['id'], 'seo_title', true),
'description' => get_term_meta($object['id'], 'seo_description', true),
'keywords' => get_term_meta($object['id'], 'seo_keywords', true),
];
},
'update_callback' => function ($value, $object) {
if (!current_user_can('manage_categories')) return new WP_Error('rest_cannot_update', '无权限', ['status' => 403]);
if (isset($value['title'])) update_term_meta($object->term_id, 'seo_title', sanitize_text_field($value['title']));
if (isset($value['description'])) update_term_meta($object->term_id, 'seo_description', sanitize_textarea_field($value['description']));
if (isset($value['keywords'])) update_term_meta($object->term_id, 'seo_keywords', sanitize_text_field($value['keywords']));
return true;
},
'schema' => [
'description' => '分类 SEO 字段',
'type' => 'object',
'context' => ['view', 'edit'],
],
]);
});这样 GET /wp-json/wp/v2/categories/123 会多出 seo 字段;POST/PUT 同 URL 能改这些字段(前提是有 manage_categories 权限)。
五、与 Schema 结构化数据协同
给分类页加 CollectionPage Schema 让 Google 更精准识别:
add_action('wp_head', 'mysite_category_schema', 2);
function mysite_category_schema() {
if (!is_category()) return;
$term = get_queried_object();
$schema = [
'@context' => 'https://schema.org',
'@type' => 'CollectionPage',
'name' => get_term_meta($term->term_id, 'seo_title', true) ?: $term->name,
'description' => get_term_meta($term->term_id, 'seo_description', true) ?: $term->description,
'url' => get_term_link($term),
'mainEntity' => [
'@type' => 'ItemList',
'numberOfItems' => (int)$term->count,
],
];
echo '<script type="application/ld+json">' . wp_json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . '</script>' . "\n";
}六、批量为已有分类生成 SEO 字段
站点已有 100 个分类不可能手动一个个填。批量补充 SEO 字段的脚本:
// 一次性脚本:扫所有分类,为没填 SEO 的分类自动生成
$terms = get_terms(['taxonomy' => 'category', 'hide_empty' => false]);
foreach ($terms as $term) {
if (!get_term_meta($term->term_id, 'seo_title', true)) {
update_term_meta($term->term_id, 'seo_title', $term->name . ' 相关文章合集(' . $term->count . ' 篇)');
}
if (!get_term_meta($term->term_id, 'seo_description', true)) {
$desc = $term->description ?: ('保哥笔记 ' . $term->name . ' 分类下共 ' . $term->count . ' 篇深度技术与实战内容。');
update_term_meta($term->term_id, 'seo_description', $desc);
}
if (!get_term_meta($term->term_id, 'seo_keywords', true)) {
update_term_meta($term->term_id, 'seo_keywords', $term->name);
}
}
echo "完成,处理 " . count($terms) . " 个分类\n";这段脚本可以做成 WP-CLI 命令长期跑——新建分类时自动跑一次填充默认值。
七、用 AI 批量生成更高质量的 SEO 内容
简单的"分类名 + 共 N 篇" 是最低标准,但点击率不高。用 GPT API 生成质量更高的:
// 调 OpenAI / Claude API 给分类生成 SEO 文案
function generate_category_seo_with_ai($term_name, $term_count) {
$prompt = "请为博客分类「{$term_name}」生成 SEO meta description(≤ 155 字符),重点突出 SEO/技术/实战角度。";
$response = wp_remote_post('https://api.openai.com/v1/chat/completions', [
'headers' => [
'Authorization' => 'Bearer ' . OPENAI_API_KEY,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode([
'model' => 'gpt-4o-mini',
'messages' => [['role' => 'user', 'content' => $prompt]],
]),
'timeout' => 15,
]);
if (is_wp_error($response)) return null;
$body = json_decode(wp_remote_retrieve_body($response), true);
return $body['choices'][0]['message']['content'] ?? null;
}批量跑一次几分钟成本几块钱,得到所有分类的高质量描述。注意:AI 生成的内容必须人工审一遍——AI 偶尔会编造不存在的数据或夸张表述。
八、性能影响
每次访问分类页都要查 3 次 term_meta(title / description / keywords)。term_meta 自带 cache(WordPress 内部 wp_cache_get),所以同一请求内查多次只命中一次数据库。多请求层面用 transient 缓存全部 SEO 元数据:
function mysite_get_category_seo_cached($term_id) {
$key = 'cat_seo_' . $term_id;
$cached = get_transient($key);
if ($cached !== false) return $cached;
$data = [
'title' => get_term_meta($term_id, 'seo_title', true),
'description' => get_term_meta($term_id, 'seo_description', true),
'keywords' => get_term_meta($term_id, 'seo_keywords', true),
];
set_transient($key, $data, HOUR_IN_SECONDS);
return $data;
}
// 编辑分类时清缓存
add_action('edited_category', function ($term_id) {
delete_transient('cat_seo_' . $term_id);
});九、跨语言 / 多站点扩展
多语言站(WPML / Polylang)下,每个语言版本的分类是独立 term——term_meta 自然每个语言独立存。直接用本文代码即可,不需要特殊处理。
WordPress 多站点(Multisite)下,每个子站的 term_meta 是独立的(每个站独立 wp_termmeta 表),也不需要改。
十、调试与排查
10.1 后台填了字段前台不显示
检查:① 主题的 header.php 是否调用了 wp_head()(自定义 meta 通过 wp_head action 输出);② SEO 插件是否拦截了输出(Yoast 高优先级,会先输出);③ get_term_meta 返回的 term_id 是不是当前分类(用 var_dump 看)。
10.2 标题没生效
主题如果用了老式 wp_title() 而不是 wp_get_document_title(),document_title_parts filter 不会生效。改用 add_filter('wp_title', ...) 兼容老主题。最佳实践是主题升级到 add_theme_support('title-tag'),让 WP 自己输出 title。
常见问题解答
用 termmeta 而不是 options 真的有差别吗?
有。① 性能:termmeta 自带 wp_cache_get 二级缓存,多次读取同 key 不打数据库;options 也有缓存但 cat_set_xxx 这种动态 key 命中率低;② 维护:分类删除时 termmeta 自动级联清理,options 不会,会留垃圾;③ REST API 友好:register_rest_field 配 termmeta 是标准模式,options 要写更多 boilerplate。
这套代码与 Yoast / RankMath 冲突怎么办?
建议二选一:要么用插件(推荐,UI 完善 + 自动化更全),要么用自定义代码(轻量 + 完全可控)。如果非要共存,按 §3.2 检测插件存在时跳过自定义输出。如果两边都输出,搜索引擎会拿到两份 meta description——Google 会选第一个,但 SERP 显示混乱。
分类描述(自带 description 字段)和 SEO description 哪个更好?
分类原生 description 是 wp_term_taxonomy 表里的字段——只能在分类编辑页填,前台不直接输出 meta description。SEO description 专门给搜索引擎读。两者用途不同,建议分类原生描述写"对用户的介绍",SEO description 写"含关键词的精炼摘要"。
能不能给标签(tag)也加 SEO 字段?
能。把代码里 category_edit_form_fields 改成 post_tag_edit_form_fields,edited_category 改成 edited_post_tag,is_category() 改成 is_tag()。或者更通用:用 {$taxonomy}_edit_form_fields 动态匹配所有分类法。
批量分类生成 SEO 后,怎么验证生效?
用 Screaming Frog 或 Sitebulb 全站扫:检查每个分类 URL 的 title 和 meta description 是不是独特的、长度合理(title 60 字以内、description 120-160 字)、关键词密度合理。看 site:yoursite.com/category/ 在 Google 的展示结果。
WordPress 自定义 post type 的分类(如 Product Category)也能用吗?
能。WooCommerce 的 Product Category 是 product_cat 分类法。把代码里 category 替换为 product_cat 即可。注意 edited_product_cat hook 名也要对应改。WooCommerce 自带产品分类的 SEO 配置(在分类编辑页),但比较弱,自写代码可以覆盖更多场景。
分类页的 canonical URL 怎么处理?
WordPress 默认会给分类页输出 canonical 指向自身(基于 get_term_link),不需要额外处理。如果想覆盖:
add_action('wp_head', function() {
if (is_category()) {
$custom_canonical = get_term_meta(get_queried_object_id(), 'seo_canonical', true);
if ($custom_canonical) {
// 移除默认的,加自定义的
remove_action('wp_head', 'rel_canonical');
echo '<link rel="canonical" href="' . esc_url($custom_canonical) . '">';
}
}
}, 0);分类下文章数为 0 时还要 SEO 吗?
不要。空分类页对 SEO 是负资产——内容稀薄被 Google 判低质。空分类的处理:① 隐藏(get_terms(['hide_empty' => true]));② 直接返回 404;③ 重定向到父分类。生产建议方案 ②。
能给每个分类加封面图吗?
能,原理一样——多加一个 termmeta 字段 seo_image,在分类编辑页加上传图按钮。前台主题在分类页 header 里显示该图。这种"分类封面"对视觉一致性帮助很大,特别是电商或杂志型站点。
分类页面对 SEO 真的重要吗?
非常重要。分类页是站内中级权重节点——首页 → 分类 → 文章是经典权重传递路径。优秀的分类页能:① 拦截"宽词"流量(搜"SEO"进入分类页而不是首页);② 强化主题相关性信号;③ 转化分类成"专题落地页"。Yoast/RankMath 等专业 SEO 插件都把分类 SEO 列为基础功能,足见其重要性。
本文标题:《WordPress 分类目录 SEO 自定义 TDK 现代化方案:termmeta API + REST 字段 + Yoast 共存策略》
本文链接:https://zhangwenbao.com/wordpress-categories-seo-add-custom-titles-keywords-descriptions.html
版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0