<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ssr on plzhans blog</title><link>https://blog.plzhans.com/ja/tags/ssr/</link><description>Recent content in Ssr on plzhans blog</description><generator>Hugo</generator><language>ja</language><lastBuildDate>Fri, 11 Sep 2026 13:01:00 +0000</lastBuildDate><atom:link href="https://blog.plzhans.com/ja/tags/ssr/index.xml" rel="self" type="application/rss+xml"/><item><title>React SPA SEO 改善方法 - Cloudflare WorkersでメタタグからSSRまで</title><link>https://blog.plzhans.com/ja/posts/128-react-spa-seo-cloudflare-workers-ssr/</link><pubDate>Fri, 11 Sep 2026 13:01:00 +0000</pubDate><guid>https://blog.plzhans.com/ja/posts/128-react-spa-seo-cloudflare-workers-ssr/</guid><description>&lt;p&gt;&lt;picture&gt;&#10; &lt;source srcset="https://blog.plzhans.com/posts/128-react-spa-seo-cloudflare-workers-ssr/assets/1_3d822a0f-7e83-8126-8051-dfd669349a3e_hu_a019422e8cbae81c.webp" type="image/webp"&gt;&#10; &lt;img src="https://blog.plzhans.com/posts/128-react-spa-seo-cloudflare-workers-ssr/assets/1_3d822a0f-7e83-8126-8051-dfd669349a3e_hu_22bb2352b55ca59c.png" alt="空だったHTMLがエッジを通りながら埋められ、クローラーがその結果を読む流れを表した代表画像" width="1200" height="630" loading="lazy"&gt;&#10; &lt;/picture&gt;&lt;/p&gt;&#10;&lt;h2 id="概要"&gt;概要&lt;/h2&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;この記事は &lt;a href="../124-cloudflare-workers-static-site-guide/"&gt;Cloudflare Workers 静的サイトガイド - デプロイからSEOまで&lt;/a&gt; シリーズの一部です。&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Reactで作ったSPAを静的ホスティングに載せて使うケースが多いです。デプロイが単純で、サーバーがないので運用するものもありません。コストもほとんどかかりません。&lt;/p&gt;&#10;&lt;p&gt;ところが検索を気にし始めると、すぐに壁にぶつかります。&lt;strong&gt;サーバーが返す生のHTMLに、検索エンジンが読むものが一つもないから&lt;/strong&gt;です。&lt;/p&gt;&#10;&lt;p&gt;この記事は、その問題を&lt;strong&gt;バックエンドサーバーを新しく立てずに&lt;/strong&gt;Cloudflare Workersだけで解いていった記録です。3つの段階に分けて扱います。後ろに行くほど得られるものが大きく、手間もかかります。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;0段階 — 何もしない。&lt;/strong&gt; 生のHTMLが空の殻です。ほとんどのSPAがここにいます。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;1段階 — Workersでメタを埋める。&lt;/strong&gt; タイトル・説明・OG・構造化データがレスポンスのHTMLに入ります。作業量が少なく効果が明確です。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;2段階 — Workersで本文まで描く。&lt;/strong&gt; 実際の画面をサーバーでレンダリングして入れます。最も手間がかかりますが、クローラーが読む内容ができます。&lt;/p&gt;&#10;&lt;h3 id="この記事で扱うこと"&gt;この記事で扱うこと&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;GoogleがJSをレンダリングするのに、なぜ自分で入れる方がよいのか&lt;/li&gt;&#10;&lt;li&gt;Workersで &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; を埋める方法と、そのときの罠&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;entry-client&lt;/code&gt; / &lt;code&gt;entry-server&lt;/code&gt; の分離と &lt;code&gt;vite build --ssr&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;サーバーが受け取ったデータをブラウザに渡し、APIの往復を増やさない方法&lt;/li&gt;&#10;&lt;li&gt;実際に変わった数値と、踏んだ罠&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="扱わないこと"&gt;扱わないこと&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;各メタ項目を何で埋めるか&lt;/strong&gt; — SEO側のテーマです。SEO最適化の記事を参照&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;wranglerの使い方・リクエストの流れ・デプロイ&lt;/strong&gt; — &lt;a href="../125-cloudflare-workers-static-assets-routing-billing/"&gt;Cloudflare Workers 静的サイトホスティング - リクエストの流れと課金基準&lt;/a&gt; · &lt;a href="../126-cloudflare-workers-wrangler-dev-deploy/"&gt;wrangler 使い方 - Cloudflare Workers のローカル開発とデプロイ&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;エッジキャッシュ・Tiered Cache&lt;/strong&gt; — &lt;a href="../127-cloudflare-workers-cache-tiered-cache/"&gt;Cloudflare Workers キャッシュ設定方法 - エッジキャッシュとTiered Cache&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="前提"&gt;前提&lt;/h3&gt;&#10;&lt;p&gt;Cloudflareに静的サイトを載せたことがあり、&lt;code&gt;wrangler&lt;/code&gt; でローカルにWorkerを立ち上げたことがあると仮定します。初めてなら、上のデプロイの記事を先に読む方がよいです。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="問題--生のhtmlに検索エンジンが読む内容がない"&gt;問題 — 生のHTMLに検索エンジンが読む内容がない&lt;/h2&gt;&#10;&lt;p&gt;ビルドされたSPAのHTMLをそのまま受け取ってみましょう。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;curl -s https://example.com/products/1234&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ko&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;My Service&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;description&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;A generic site description&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;root&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;module&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/assets/index-a1b2c3.js&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;1.6KBほどの殻です。商品名も価格も説明もありません。すべてJSが実行され、APIのレスポンスが届いた後にはじめて作られます。&lt;/p&gt;</description></item></channel></rss>