Astro 站点 SEO:2026 完整指南
让 Astro 站点获得排名所需的一切:元数据、站点地图、结构化数据、i18n 与 Core Web Vitals。
- Astro
- Technical SEO
如果自然搜索对你很重要,那么 Astro 是你能选择的最佳框架之一——这并非偶然。它的整个设计理念(输出 HTML、默认零 JavaScript、构建时渲染)几乎完美契合搜索引擎真正看重的东西。你现在正在阅读的这个站点就是用 Astro 构建的,所以下文的一切都经过实战检验,而非纸上谈兵。
本指南将走遍一个 Astro 项目完整的 SEO 面:元数据与 canonical、sitemap 与 robots、JSON-LD 结构化数据、多语言路由、Core Web Vitals,以及那些悄悄拖累你排名的陷阱。每一节都附带可直接粘贴进自己项目的真实代码。
为什么 Astro 适合 SEO
现代站点上的大多数 SEO 问题都能追溯到同一个根源:爬虫下载到的页面,并不是用户看到的页面。一个 React 或 Next 客户端渲染的路由,发出去的是一个空的 <div id="root"></div>,真正的内容要等水合(hydrate)之后才出现。Google 能 渲染 JavaScript,但它是在延迟的第二轮里、用有限的预算去做的——想了解这条流水线内部到底发生了什么,可以看我们关于 JavaScript SEO 与渲染 的深度解析。
Astro 直接绕开了整个问题:
| 关注点 | 典型 SPA | Astro(默认) |
|---|---|---|
| 初始 HTML | 空壳 | 完整渲染的内容 |
| 发出的 JavaScript | 整个应用包 | 零,除非你主动选用 |
| 爬取时可见的内容 | 渲染轮之后 | 首次请求即可见 |
| 首字节时间 | 服务端计算 | 来自 CDN 的静态文件 |
Astro 的 Islands 架构 意味着交互性是按组件主动选用的。你只对那一个需要 JavaScript 的搜索框或轮播图做水合,其余一切都保持为纯粹、可爬取的 HTML。默认输出是静态的(SSG)、语义化的、可即时索引的。
🧑💻 开发者视角:Astro 中最大的那个 SEO 胜利,是一个「不做决定」的结果。你什么都不做,就能得到服务端渲染的 HTML,且没有客户端包。其他框架则要你去争取这一点。
元数据与 canonical
标题标签、meta description、canonical URL 和 Open Graph 标签是基本盘。大多数团队犯的错误是把它们散落在各个页面里,导致彼此渐渐不同步。解决办法是用一个统一的布局,接收 props 并在所有地方产出一致的 <head> 标记。
创建 src/layouts/BaseLayout.astro:
---
interface Props {
title: string;
description: string;
image?: string;
}
const { title, description, image = "/og-default.png" } = Astro.props;
// Build an absolute canonical from the current URL + your site origin.
const canonical = new URL(Astro.url.pathname, Astro.site).href;
const ogImage = new URL(image, Astro.site).href;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonical} />
<meta property="og:image" content={ogImage} />
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
</head>
<body>
<slot />
</body>
</html>
为了让 Astro.site 能够解析,在 astro.config.mjs 中设置它:
export default defineConfig({
site: "https://example.com",
});
现在每个页面只需把数据传进去:
---
import BaseLayout from "../layouts/BaseLayout.astro";
---
<BaseLayout
title="SEO for Astro Sites: The Complete 2026 Guide"
description="Make your Astro site rank with metadata, sitemaps, and more."
>
<h1>...</h1>
</BaseLayout>
💡 提示:一个自引用的 canonical(页面指向自己干净的 URL)是抵御查询字符串、跟踪参数和尾部斜杠变体所带来的重复内容问题的最有效手段。
Sitemap 与 robots
sitemap 告诉搜索引擎哪些 URL 存在、它们何时变更。Astro 有一个第一方集成——安装并注册它:
npx astro add sitemap
该命令会把你的配置改成:
import sitemap from "@astrojs/sitemap";
export default defineConfig({
site: "https://example.com",
integrations: [sitemap()],
});
执行 astro build 后,你会在 dist/ 里得到 sitemap-index.xml 以及分页的子 sitemap。该集成只有在设置了 site 时才工作——那个 origin 会作为每个 URL 的前缀。
对于 robots.txt,跳过静态文件,改用一个动态端点,这样 sitemap 的 URL 会与你的配置保持同步。创建 src/pages/robots.txt.ts:
import type { APIRoute } from "astro";
const robots = (sitemapURL: URL) => `
User-agent: *
Allow: /
Sitemap: ${sitemapURL.href}
`.trim();
export const GET: APIRoute = ({ site }) => {
const sitemapURL = new URL("sitemap-index.xml", site);
return new Response(robots(sitemapURL), {
headers: { "Content-Type": "text/plain" },
});
};
⚠️ 注意:robots.txt 中的
Disallow阻止的是 爬取,而不是 索引。一个被 disallow 的 URL,只要有外部链接指向它,仍可能以一条裸链接的形式出现在搜索结果中。要把一个页面挡在索引之外,应当让它被爬取,并改为加上<meta name="robots" content="noindex">。
结构化数据(JSON-LD)
结构化数据是你赢得富结果(rich results)的方式——文章署名、面包屑、FAQ 折叠面板、星级评分。Google 会把它当作 script 标签里的 JSON-LD 来读取。在 Astro 中干净的做法是:在 frontmatter 里构建对象,在布局里把它序列化。
在 BaseLayout.astro 中,接收一个可选的 schema prop,并用 set:html 输出它,这样引号不会被转义:
---
interface Props {
title: string;
description: string;
schema?: Record<string, unknown>;
}
const { title, description, schema } = Astro.props;
---
<head>
<!-- ...other tags... -->
{schema && (
<script
type="application/ld+json"
set:html={JSON.stringify(schema)}
/>
)}
</head>
一个博客文章页面会传入一个 Article 对象:
---
const schema = {
"@context": "https://schema.org",
"@type": "Article",
headline: "SEO for Astro Sites: The Complete 2026 Guide",
datePublished: "2026-06-21",
author: { "@type": "Person", name: "Your Name" },
};
---
<BaseLayout title="..." description="..." schema={schema}>
💡 提示:手写 schema 对象很容易出错。我们的 Schema 生成器 能为常见类型产出有效的 JSON-LD,让你可以直接粘贴进 prop。务必在 Google 的 Rich Results Test 中复查输出。
多语言(i18n)
如果你提供多种语言,规则简单而不留情面:一个页面的每个语言版本都必须通过 hreflang 声明其他所有版本,包括自引用。弄错这一点,Google 就会提供错误的语言,或把这些变体当作重复内容。
在 astro.config.mjs 中配置路由:
export default defineConfig({
site: "https://example.com",
i18n: {
defaultLocale: "en",
locales: ["en", "fr", "ja"],
routing: { prefixDefaultLocale: true },
},
});
设置了 prefixDefaultLocale: true 后,内容位于 /en/、/fr/、/ja/ 之下——不存在一个会与某个语言路径相争的含糊根路径。然后在 <head> 中产出 hreflang 簇:
---
const locales = ["en", "fr", "ja"];
// Strip the current locale prefix to get the shared path segment.
const path = Astro.url.pathname.replace(/^\/[a-z]{2}/, "");
---
{locales.map((loc) => (
<link
rel="alternate"
hreflang={loc}
href={new URL(`/${loc}${path}`, Astro.site).href}
/>
))}
<link
rel="alternate"
hreflang="x-default"
href={new URL(`/en${path}`, Astro.site).href}
/>
x-default 这一条告诉 Google:当没有任何语言与用户匹配时该展示哪个版本。把翻译保持为平行文件(content/blog/en/post.mdx、content/blog/fr/post.mdx),上面那段路径运算才能保持简单。
Core Web Vitals
Astro 的静态输出让你抢先一步:没有水合开销,没有会引起布局抖动的客户端框架,HTML 直接由 CDN 提供。这就免费覆盖了 Largest Contentful Paint(LCP)和 Interaction to Next Paint(INP)的大部分。仍然会咬你一口的,是图片和字体这两样东西。
使用 Astro 内置的 <Image /> 组件——它会生成现代格式、调整尺寸,并强制指定尺寸:
---
import { Image } from "astro:assets";
import hero from "../assets/hero.png";
---
<Image
src={hero}
alt="Descriptive alt text"
width={1200}
height={630}
format="webp"
loading="eager"
/>
进入绿区的简短清单:
- 给每张图片设置明确的
width/height,以防止布局抖动(CLS)。 - 对 LCP 图片使用
loading="eager",对首屏以下的一切使用lazy。 - 自托管字体并加上
font-display: swap,这样文本会在字体加载完成前先渲染出来。 - 只预加载那一个最重要的字体文件;不要把它们全部预加载。
- 让任何
client:*指令远离首屏可见的内容。
| 指标 | 它衡量什么 | Astro 的默认表现 |
|---|---|---|
| LCP | 最大元素的绘制时间 | 强——静态 HTML,由 CDN 提供 |
| CLS | 视觉稳定性 | 若图片有尺寸则强 |
| INP | 输入响应性 | 极佳——默认无水合 |
常见陷阱
以下是那些能通过本地测试、却仍会拖垮排名的错误:
client:only内容对爬虫不可见。 一个client:only组件在服务端 什么都不渲染——没有降级的 HTML。如果重要内容藏在其中,它就不会被索引。请改用client:load(它仍会服务端渲染),或者更好,把内容保留在纯.astro中。- 尾部斜杠不一致。 Astro 的
trailingSlash选项("always"、"never"、"ignore")必须与你的主机提供文件的方式以及你内部链接所使用的形式相匹配。不匹配会产生/page和/page/这两个 URL,从而分散链接权重。选定一种,在配置里设好,并让每一条内部链接都与之一致。 - 缺失
site配置。 没有设置site,canonical 和 sitemap 会悄悄失效,或产出相对 URL。这是导致 sitemap 为空或无效最常见的原因。 - 忘了给单薄页面加
noindex。 标签归档页和分页页往往不带来任何价值。给它们加上noindexmeta 标签,而不是任由它们稀释你的爬取预算。 - 依赖重定向来做规范化。 canonical 标签是一个提示;301 才是一条指令。对于真正的重复(HTTP 对 HTTPS、www 对非 www),应在主机或 CDN 层面强制执行重定向——不要只靠 canonical 标签。
⚠️ 注意:永远要用真实的产物来验证,而不是用 dev server。运行
astro build并检查dist/——打开生成的 HTML,确认你的内容、meta 标签和 JSON-LD 确实在静态输出里。astro dev可能会掩盖渲染上的差异。
收尾
Astro 给了你一个大多数框架要你手工搭建的地基:服务端渲染的 HTML、默认零 JavaScript,以及爬虫和 Core Web Vitals 都喜爱的快速静态输出。你的工作是在其上叠加那些有意为之的信号——一个注入一致元数据与 canonical 的统一布局、一个 sitemap 和动态 robots 端点、用于富结果的 JSON-LD、为每种语言准备的正确 hreflang 簇,以及自律的图片与字体处理。
把这些做好,你就几乎抹掉了搜索引擎给你更低排名可能依据的每一个技术理由。
下一步:
- 把地基打对:Build 层
- 理解爬虫如何处理 JS:JavaScript SEO 与渲染
- 快速生成有效标记:Schema 生成器