← 記事

Google に記事を見落とされないために——SSR Blog の SEO で踏んだ落とし穴

· 10 分で読めます
Astro SEO SSR Sitemap JSON-LD
SSR Blog の実践

Google に記事を見落とされない

SSR Blog の SEO 失敗から学んだこと

Astro + PostgreSQL で Blog を構築した際の SEO の各判断とその理由、そして記事が検索結果から消えかけた落とし穴を紹介します。

問題

SSR Blog の SEO はなぜ静的サイトと違うのでしょうか?

1

動的コンテンツが Sitemap に載らない

SSR ページは runtime に生成されますが、@astrojs/sitemap に含まれるのは build-time の静的ページだけです。

記事を公開しても sitemap に載らなければ、Google はその存在すら知りません。

2

Tool Schema だけが隠れたコストではない

MCP token と同じく、SEO にも「隠れたコスト」があります。meta tag の欠落、hreflang の誤り、canonical の不一致です。

67% 多言語サイトでの hreflang の実装ミス

3

SSR のパフォーマンスは諸刃の剣

SSR ならコンテンツをリアルタイムで更新できますが、TTFB は DB クエリの所要時間に左右されます。

キャッシュ戦略なし = request のたびに DB query が走ります。

技術スタック

SSR Blog の構成

Astro 5
フレームワーク
PostgreSQL
データベース
Drizzle
ORM クエリ層
Tailwind
スタイル
Cloudflare
Tunnel でのデプロイ
SEO の判断

私たちは何をしたのでしょうか?

動的 sitemap から robots.txt まで 5 つの層を補い、それぞれに明確な利用者を想定しました。

動的 Sitemap
sitemap-posts.xml は DB から公開済み記事をリアルタイムで取得し、build 時に自動で patch して取り込みます: sitemap-index.xml
🔍 Google / Bing のクローラー
JSON-LD 構造化データ
各記事の BlogPosting schema を自動生成します。タイトル、著者、公開日時、言語はすべて DB のフィールドに対応させています
📊 検索結果のリッチスニペット
Canonical URL
SSR ページでは canonical を自分自身に向けて自動設定します。 http://localhost と https://blog.joneshong.com が重複と判定されるのを防ぎます
🔗 重複排除の仕組み
hreflang による多言語対応
zh-TW ↔ en の双方向 alternate link に x-default fallback を追加します。67% のサイトがここを間違えています
🌐 多言語検索
robots.txt
クローラーにすべての公開ページへのアクセスを許可し、 /admin/ と /api/をブロックします。2 つの sitemap を指定します
🤖 クローラー制御
パフォーマンス

Core Web Vitals の最適化

0 KB
JS baseline
Astro はデフォルトで JS がゼロ
必要なときだけ
React island hydration
検索、グラフ
遅延
フォント読み込み戦略
CJK システムフォント + Latin lazy load
SSR
動的コンテンツ
リアルタイム更新

Astro の切り札:デフォルトでは JavaScript を一切送りません。React コンポーネントは操作が必要な箇所だけ hydrate し(client:only)、それ以外はすべて静的 HTML です。

失敗の記録

私たちはどうやって問題に気づいたのでしょうか?

最初から設計できていたわけではありません。sitemap がどこからも参照されていない、読了時間が 73 分になる、Nginx subpath のページが真っ白になる——この 3 つの問題にぶつかって補いました。

記事を公開 → Google で見つからない

sitemap-posts.xml どこからも参照されていませんでした

@astrojs/sitemap が sitemap-index.xml を生成する時点では静的ページしか含まれません。動的な sitemap-posts.xml は無視されていました。対処法:build 後に sitemap-index を自動で patch し、posts sitemap を追加します。

学んだこと:自動化ツールがすべての case をカバーするとは限りません。SSR の動的ページは手動で漏れを補う必要があります。

12 分の記事が 73 min read と表示される

Rich HTML の content.length には CSS 全体が含まれます

content.length / 400 読了時間を計算しますが、Rich HTML 形式の content には 25000+ 文字の CSS + HTML tags が含まれます。正しい読了時間を出すには、先に tags を strip する必要があります。

学んだこと:content の長さに関わるロジックでは、まず content の形式を確認する必要があります。

Nginx subpath proxy でページが真っ白になる

Vite dev server + subpath = 行き止まり

Vite の ES module import は絶対パスを使うため、nginx の sub_filter を書き換えてもきりがありません。解決策:dev preview では Vite dev server を使わず、build 済みの SSR server を使います。

学んだこと:Astro islands の component-url 属性も sub_filter で書き換える必要があります。

ツール

自分の head を貼り付けて確認

上のリストには 6 項目あり、そのうち 3 項目はサイトをクロールせずに検証できます。自分の記事のどれか 1 つの <head> ソースコードを貼り付けるだけです。canonical、hreflang、JSON-LD の 3 項目はすべてブラウザー内で解析し、何も送信しません。残る 3 項目(sitemap への登録、robots.txt、TTFB)は実際にあなたのドメインへリクエストを送る必要があり、ブラウザーのクロスサイト制限では対応できません。その 3 項目は、上の prompt を AI に渡してクロールしてもらってください。

確認するのは「貼り付けた文字列」であって、公開中のページではありません。ブラウザーの DevTools の Elements パネル(JS 実行後の DOM)を貼り付けると、クローラーが見ているものと結果が異なる可能性があります。クローラーと同じ内容を確認するには、「ページのソースを表示」(view-source)を使ってください。

この部分を持ち帰る

あなたの AI Agent にコピー

記事を公開すれば Google に見つかると思っていても、実は存在を知られていないことがあります——SSR blog のページは runtime に生成されますが、@astrojs/sitemap が拾うのは build 時の静的ページだけです。動的な記事は sitemap に書き込まれません。meta tag が欠けているならせいぜい順位が下がる程度でも、インデックスされなければ検索結果から完全に消えます。

以下を AI に貼り付けてください。一般的な知識で答えさせず、実際にあなたのサイトをクロールし、この記事のリストと照合して、何が欠けているかを突き止めてもらいます:

私は SSR blog の SEO 失敗談(https://blog.joneshong.com/zh/blog/hello-world)を読んでいます。 著者は Astro + PostgreSQL でサイトを構築し、SEO に関する 5 つの判断と SSR 特有の 3 つの落とし穴を挙げています。 私のサイトにも同じ問題がないか調べてください。一般的な SEO の知識だけで答えず、実際の出力を直接検証してください: 私のサイト:[URL を入力]。SSR / SSG / 完全な静的サイトのどれかです(不明ならまず判定してください)。 以下を記事で挙げられた落とし穴の順に、実際のサイトで確認してください: 1. 私の sitemap を取得し、「最近公開した動的ページ」が本当に含まれるか確認してください—— SSR で最もよくある落とし穴は、動的ページが登録されず、Google に存在を知られないことです 2. 記事の head タグを 1 つ確認してください。canonical は自分自身を指していますか? localhost やプレビュー用ドメインが残っていませんか? 3. hreflang に双方向の alternate + x-default はありますか?(この記事の実測では、多言語サイトの 67% がこの点を間違えています) 4. 私の robots.txt を取得してください。/admin/ や /api/ のような非公開パスをブロックしていますか? sitemap を指していますか? 5. 各記事に JSON-LD BlogPosting はありますか?フィールドは実際のデータから生成されていますか、それとも固定値ですか? 6. 私のサイトが SSR なら、DB query によって TTFB が遅くなっていませんか?キャッシュ層はありますか? 実際に欠けている項目や間違っている項目を、見つけた証拠とともに報告してください。「記事が検索結果から消えるかどうか」を基準に優先順位を付けてください。 私のサイトが完全な静的サイトなら、1 と 6 は該当しません。無理に当てはめず、該当しないと明記してください。
参考資料

関連する記事

この 5 本の記事はそれぞれ 1 つの判断に対応しており、隠れたコストのたとえから hreflang のエラー率 67% というデータまで扱っています。

記事 重要な理由
Apideck: Your MCP Server Is Eating Your Context Window 「隠れたコスト」というたとえの着想源
SEO for Astro: How to Make the Fastest Framework Also the Smartest Astro SEO の実装に関する包括的なガイド
JSON-LD Blog Post Example BlogPosting schema の実装例
Multilingual SEO and Hreflang Guide 67% というエラー率のデータの出典
How to Improve Core Web Vitals SSR のパフォーマンスを最適化する具体的な方法
結論

SEO はエンジニアリングであり、まじないではない

すべての判断には理由があり、すべての失敗には教訓があります。
SSR Blog の SEO は meta tag を設定すれば終わりではなく、継続して改善するエンジニアリングです。

// 判断の流れ

問題 1:動的な記事が sitemap に載らない
→ 手動で補う sitemap-posts.xml

問題 2:SSR にキャッシュがない
→ 静的ページは prerender + 動的ページは SSR

問題 3:多言語 SEO は間違いやすい
  → hreflang 双方向 + x-default

検証:Google Search Console で手動送信 + 監視

// まとめ
Astro SSR + 自動 SEO pipeline
登録漏れなし、重複なし、遅延なし
✦ プロンプトをコピー