個人開発でサイトを2つ運用しています。ITの入門サイト IT業界案内図(it-nyumon.com) と、エンジニア向けの職務経歴書作成ツール keireki.dev です。
作っているあいだは、公開した日がゴールだと思っていました。実際は逆で、公開してからのほうがやることが多い。この1〜2か月、2つのサイトに入れた改善と、そこで見つかった「作った本人には見えていなかったもの」を記録として残しておきます。
特に keireki.dev では、サイトマップに載せたページの半分以上が Google に登録されないままでした。調べると、SPA(シングルページアプリケーション)の作りに由来する問題がいくつも見つかりました。同じ作り方をしている人には、たぶん他人事ではありません。
この記事で扱う改善を、先に表にまとめておきます。
| 課題 | 変更内容 | 確認できた結果 |
|---|---|---|
| 数字を見に行くのが続かない | 3サイトの数字を毎週月曜に Notion へ自動で投稿 | Check が続くようになった |
| it-nyumon.com が読みづらく、回遊しにくい | 配色とコントラスト、追従サイドバー、2列カード、パンくず、飾りの英語ラベルの削除 | 効果測定はこれから |
| keireki.dev の最初の画面で、何ができるか分からない | 完成した書類とボタン2つを最初の画面に置き、サービス名を変更 | 効果測定はこれから |
| keireki.dev の19ページ中11ページが未登録のまま | 本文を静的 HTML に焼き込み、リンクを <a href> に、canonical とソフト404を修正 | 本文0バイトの HTML が約43KB に(登録状況の変化はこれから) |
公開した瞬間が、いちばん人が来ない
リリース直後はXでの告知で少し人が来ます。ただそれは一過性で、数日で元に戻ります。そこから先は検索から見つけてもらうしかなく、検索経由の流入が育つまでには数か月かかります。
この期間が個人開発でいちばん心が折れやすいところだと思っています。作るのは楽しいけれど、直すのは地味です。それでも、サイトが育つかどうかは公開後の改善で決まります。要するに、Plan と Do で終わらせずに Check と Act をどれだけ回せるかという話です。

Check の材料は、自分の感想ではなくデータで用意する
改善のネタを「なんとなく気になるところ」から拾うと、だいたい自分の好みの話になります。そこで見る場所を先に決めました。
- Google Analytics 4
訪問者数、よく見られているページ、流入元 - Google Search Console
検索クリック、表示回数、掲載順位、インデックス状況
ただ、管理画面を毎週自分で開きに行くのは続きません。実際に続きませんでした。なので、3サイト分の数字を毎週月曜の朝にNotionへ自動で投稿する仕組みを Cloudflare Workers で作って、そこを読むだけにしました。前週比と、順位が惜しいクエリまで書き出すようにしてあります。
「見に行かないと分からない」を「勝手に届く」に変える。これだけで Check が続くようになりました。
改善①:見た目を変えた(it-nyumon.com)
IT業界案内図は、IT業界の全体像や用語を図解で説明する入門サイトです。公開当初は白基調のモノトーンで作っていました。

変更した理由は2つあります。
ひとつは読みづらさと、回遊のしにくさ。白地に薄いグレーの文字が多く、記事を読み終わったあとの行き先も用意していませんでした。1ページ見て離脱、が続いていました。
もうひとつは扱う内容が広がって、当初のコンセプトと合わなくなったこと。もともと「IT業界を路線図として案内する」というコンセプトで、キャッチコピーも「IT業界は、路線図にすると迷わない。」でした。ところが運用しているうちに、AIツールの使い方や用語解説など、路線図とは関係のない記事が増えていきました。看板と中身がずれた状態です。
そこで、テーマカラーを濃紺+金に変え、コピーも「IT業界は、図解にするとよくわかる。」に置き換えました。実際にやったのは次のあたりです。
- 配色を濃紺+金に統一し、本文のコントラストを上げた
- 記事ページに追従サイドバー(目次+おすすめ記事)を追加した
- 記事一覧を2列カード+カテゴリフィルターに作り替えた
- 主要ページにパンくずリストを追加した
- 用語ページから記事への内部リンクを増やした
- 飾りで置いていた英語ラベル(「GUIDE 5 —」のようなもの)を全ページから消した
最後の英語ラベルは、作っているときは「それっぽくてかっこいい」と思って入れていたものです。読む人にとっては意味のない文字が先頭にあるだけなので、全部消しました。デザインのつもりで置いたものが、読む邪魔をしていることがあります。
改善②:最初の画面を変えた(keireki.dev)
keireki.dev は、エンジニア向けの職務経歴書・履歴書をブラウザ内だけで作れるツールです。入力データをサーバーに送らないのが売りで、2026年7月にリリースしました。

旧トップページは、左に使い方の3ステップ、右に説明文とテンプレート一覧という構成でした。自分では「必要な情報が全部ある」と思っていたのですが、あらためて初見のつもりで開くと問題が2つありました。
- 何ができるサイトか一目で分からない
文字で説明はしているものの、完成物のイメージが画面のどこにも見えない - 作成開始への導線が弱い
最初の画面に「ここを押せば始まる」と分かるボタンがない
変更後は、完成した書類そのものを最初の画面に大きく置き、「職務経歴書を作成」「履歴書を作成」の2つのボタンを並べました。強みである「登録不要・データ送信なし・完全無料」も、スクロールしなくても見える位置に上げています。
あわせて、サービス名も「職務経歴書ジェネレーター」からドメインと同じ keireki.dev に変えました。旧名は同名のサービスが他にもあり、検索結果は大手の転職ポータルで埋まっていて、名前で覚えてもらうにも検索で見つけてもらうにも不利だったためです。
いちばん大きかったのは、見た目ではなくHTMLの中身の問題だった
ここからが本題です。デザインを直して満足していた9月頭、Search Console を眺めていて妙なことに気づきました。
サイトマップに載せた19ページのうち、11ページが「検出 — インデックス未登録」のまま、6週間一度もクロールされていなかったのです。内部リンクのレポートも0件でした。リンクを張った覚えはあるのに、0件です。

調べて分かったこと:サーバーが返すHTMLが、ほぼ空だった
keireki.dev は Vite + React のSPAです。ページ遷移も含めて、画面はすべてJavaScriptが描いています。ブラウザで見れば当然すべて表示されるのですが、サーバーが最初に返すHTMLの中身はこれだけでした。
<body class="font-ja">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
本文もリンクも、ここには1文字もありません。ここまでは確認できた事実です。
一方で、Google は JavaScript を実行して内容を読むので、SPA だというだけで読まれないわけではありません。ただしレンダリングは、HTML の取得とは別の工程として行われます。keireki.dev の場合はさらに、後で書くとおり内部リンクが <a href> になっておらず、canonical も全ページ同じでした。未登録が続いた原因を1つには絞れていませんが、クローラーが最初に受け取る HTML に本文もリンクも無かったのは確かなので、ここから直すことにしました。
自分は「記事の質が足りないから検索に出ないのだろう」と思っていました。調べてみると、それ以前に、クローラーが入口で本文もリンクも受け取れない状態でした。
対策:ビルドのときに本文を静的HTMLへ焼き込む
正攻法はSSR(サーバーサイドレンダリング)への作り替えですが、個人開発でそこまでやると手が止まります。代わりに、ビルドの最後に一手間足すだけの方法にしました。
vite buildで出力したdistをローカルサーバーで配信する- ヘッドレスChromium(Playwright)で各ページを開く
- 描画が終わった
#rootの中身を取り出し、そのページのHTMLファイルに書き戻す
// ビルド後に走らせるスクリプト(要点だけ)
const page = await browser.newPage()
await page.goto(`http://127.0.0.1:${port}${route}`)
await page.waitForSelector('#root h1')
const body = await page.$eval('#root', (el) => el.innerHTML)
// dist/<slug>.html の空の <div id="root"></div> を、描画済みのHTMLで置き換える
writeFileSync(file, html.replace(MARKER, `<div id="root">${body}</div>`))
Reactは初回描画のときに #root の中身を捨てて描き直すので、二重に表示されることはありません。クローラーとJavaScript無効の環境には焼き込んだHTMLが見え、通常のブラウザではこれまで通りSPAとして動きます。
あわせて、描画結果が空・h1が無い・内部リンクが無いページがあったらビルドを失敗させるチェックも入れました。同じ事故に半年後の自分が気づけないのがいちばん怖いからです。
結果として、たとえばガイド記事のページは本文0バイトのHTMLから、約43KBの本文入りHTMLを返すようになりました。
ついでに見つかった、地味な穴3つ
この件を追いかけている途中で、他にも3つ見つかりました。どれも「動いているから気づかない」タイプの問題です。
| 症状 | 何が起きていたか |
|---|---|
| canonicalが全ページ同じ | SPAなので index.html の canonical が全ルートで使い回され、どのページも「トップの重複」として扱われていた |
| 存在しないURLが200を返す | SPAのフォールバック配信で、無いURLでもトップの中身が200で返っていた(ソフト404) |
| 内部リンクが0件 | 画面内の遷移を navigate() のボタンで書いていた。クリックすれば動くが、<a href> ではないのでクローラーからはリンクに見えない |
3つ目は特にSPAらしい落とし穴だと思います。人間には動いて見えるものが、クローラーには存在しない。ボタンを <Link>(実体は <a href>)に書き換えるだけの修正でした。
直そうとして本番を2回止めた
ここは正直に書いておきます。ソフト404を直そうとして配信ルール(Cloudflare Pages の _redirects)をいじったところ、本番サイトを2回落としました。
404.htmlを置くと、Pages の自動SPAフォールバックが止まってアプリ画面が開かなくなる/* /index.html 200は無限ループ判定で無視されていた(効いているつもりで効いていなかった)
どちらも手元の簡易HTTPサーバーでは再現しません。ホスティング固有の挙動だからです。以来、配信まわりを触るときは wrangler pages dev(本番と同じランタイム)で確認し、さらにプレビュー環境にデプロイして「全ページ200/存在しないURLは404」を見てからマージする、というルールにしました。なお、Cloudflareでの公開手順そのものはAIで作ったアプリを公開する手順にまとめてあります。
Act のフェーズで壊すと Check の材料も濁ります。直し方のルールを決めることまで含めてPDCAだと思っています。
SPAで作った人が今すぐできる確認
同じ問題を抱えていないかは、3分で確認できます。
# 本文の一部が、返ってきたHTMLに含まれているか
curl -s https://example.com/your-page | grep -c "記事中の特徴的な一文"
# 0 なら、そのページの本文はサーバーのHTMLに存在しない
- Search Console の「URL検査 → 取得したHTML」でも同じことが見られます
- 「ページ」レポートで 「検出 — インデックス未登録」が長く続いているURLがあれば要注意です
- 「リンク」レポートの内部リンクが極端に少ない場合も、同じ原因のことがあります
数字がどう動いたかは、次の記事で
ここまで書いておいて何ですが、今回の改善で訪問者がどれだけ増えたのかは、まだ書けません。

改善を入れたのが9月頭で、インデックスの再登録にも検索順位の反映にも時間がかかります。今の時点で「アクセスが伸びました」と書いたところで、それが改善の効果なのか、季節や曜日のブレなのか区別がつきません。
なので、変更前後の訪問者数・検索クリック・表示回数・インデックス済みページ数を、十分なデータが溜まった時点で別記事にまとめます。効果がなかったならそれも含めて書きます。やりっぱなしにせず、効果測定まで含めて1周です。
まとめ:作って終わりにしないために
- 公開日はスタート地点
検索から人が来るまでには数か月かかる - Check の材料は自動で届くようにする
見に行かないと分からない仕組みは続かない - 見た目の問題は自分の目で気づける
読みづらさ、導線、看板と中身のズレ - 技術的な問題は道具を見ないと気づけない
SPAの空HTMLは、ブラウザで見ているかぎり永久に気づけない - 直し方のルールも決める
本番と同じ環境で確認してから出す
個人開発は、公開したあとに誰も催促してくれません。だからこそ、回す仕組みを先に用意しておくと続きます。次はデータを持って戻ってきます。
作る側の話は「NoToDo」開発記にも書いています。あわせてどうぞ。
次に読む記事



