Astro 不附送 SEO 默认值。title、meta、canonical、hreflang,每一项都是你的责任。这篇是最短必备清单。
问题背景
SEO 基础不是加分项,是入场券。Astro 给你完整的 <head> 控制权,意味着你得自己把对的标签放进去。好消息是:写一个 <BaseHead> 组件就够全站用。
判断标准
- 准备发任何靠 Google 流量的 Astro 站。
- 有或将有多语言版本。
- 会定期发或更新内容。
- 想避开因标签缺失带来的长尾收录问题。
快速结论
只做一个 <BaseHead> 组件,全站都用它,绝不在单页内联 meta。
实操步骤
astro.config.mjs里把Astro.site配好——<head>里所有绝对 URL 都靠它:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://yourdomain.com',
trailingSlash: 'always',
build: { format: 'directory' },
});
- 写
src/components/BaseHead.astro,meta 标签只在这一个地方输出:
---
export interface Props {
title: string;
description: string;
lang?: 'en' | 'zh';
translationKey?: string;
ogImage?: string;
}
const { title, description, lang = 'en', translationKey, ogImage } = Astro.props;
if (!title || !description) {
throw new Error(`BaseHead 必须传 title + description。当前:${Astro.url.pathname}`);
}
const canonical = new URL(Astro.url.pathname, Astro.site).toString();
const ogImg = new URL(ogImage ?? '/og-default.png', Astro.site).toString();
---
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
<meta property="og:type" content="article" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonical} />
<meta property="og:image" content={ogImg} />
<meta property="og:locale" content={lang === 'zh' ? 'zh_CN' : 'en_US'} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImg} />
{translationKey && (
<>
<link rel="alternate" hreflang="en"
href={new URL(`/en/articles/${translationKey}/`, Astro.site).toString()} />
<link rel="alternate" hreflang="zh"
href={new URL(`/zh/articles/${translationKey}/`, Astro.site).toString()} />
<link rel="alternate" hreflang="x-default"
href={new URL(`/en/articles/${translationKey}/`, Astro.site).toString()} />
</>
)}
- 在
BaseLayout.astro顶部用一次:
---
import BaseHead from '../components/BaseHead.astro';
const { title, description, lang, translationKey } = Astro.props;
---
<html lang={lang}>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<BaseHead {title} {description} {lang} {translationKey} />
</head>
<body><slot /></body>
</html>
view-source:上你应该看到的 HTML 结构:
<title>Astro SEO 基础</title>
<meta name="description" content="Astro 站点 SEO 的最低必备..." />
<link rel="canonical" href="https://yourdomain.com/zh/articles/astro-seo-basics/" />
<meta property="og:title" content="Astro SEO 基础..." />
<link rel="alternate" hreflang="en" href="https://yourdomain.com/en/articles/astro-seo-basics/" />
<link rel="alternate" hreflang="zh" href="https://yourdomain.com/zh/articles/astro-seo-basics/" />
<link rel="alternate" hreflang="x-default" href="https://yourdomain.com/en/articles/astro-seo-basics/" />
- 用一行命令审上线后的站,任何缺 canonical / hreflang 的 URL 都会被打印出来:
for u in $(cat sitemap-urls.txt); do
curl -s "$u" | grep -E 'rel="canonical"|hreflang=' \
| wc -l | awk -v u=$u '$1 < 3 { print u, "标签缺失" }'
done
-
抽一个代表页 URL 丢进 Google 的 Rich Results Test——它会解析
<head>暴露隐性问题(canonical 错、OG 不合法等)。 -
部署完用 Search Console URL Inspection 主动给首页和一篇文章请求收录,不要被动等爬虫。
容易踩的坑
- 让单页内联
<title>,迟早有页面忘写。 - canonical 写相对路径——只有绝对路径是安全的。
- 漏 hreflang
x-default,Google 猜错默认语言。 - Open Graph 和 Twitter 标签重复但值不一致。
- 用 JS 在客户端设 meta,搜索引擎可能不执行。
这篇适合谁
不想花钱请 SEO 顾问、又想拿到可靠 SEO 的独立 Astro 站。
这篇不适合谁
内部工具站不想被收录——那种全站 <meta name="robots" content="noindex"> 就够。
FAQ
- 第一天就要上结构化数据吗: 不用。title、description、canonical、hreflang 起步够了。基础稳定后再加 Article、FAQPage 等结构化数据。
- canonical 总指向自己吗: 基本是的,除了分页列表指向第 1 页这类例外。self-canonical 是安全默认。
- 2026 一个好 title 多长: 50-60 个字符为佳。太长在 SERP 里被截断,没冲击力。
- 页面没 description 怎么办: 用首段做后备生成。空 description 会被 Google 自动生成,通常比你写的差。