はじめに
以前の記事で「個人開発のバイクサイトを3ヶ月でDAU420まで伸ばした話」を書きました。
あれから1ヶ月後の2026年3月27日。Google March 2026 Core Updateが始まりました。
4月初旬、Google Search Consoleを開いたときの衝撃は今でも忘れません。
DAU 420 → 150。
表示回数は半分以下、クリック数は3分の1。積み上げてきたものが一瞬で崩れた感覚でした。
この記事では、暴落してから4ヶ月間で何をやり、DAU200まで戻した全記録を書きます。「回復しました!」というキレイな話ではなく、まだ420には届いていない現在進行形のリアルな記録です。
**MotoHub(motohub.jp)**は、GooBike・BDS・Webikeの中古バイク在庫を横断検索できるプラットフォームです。Laravel 12 + Alpine.js + Tailwind CSSで構築し、現在16万台の在庫データ、4,569車種、12,260ショップを掲載しています。
何が起きたか — 数字で見る暴落
Before(3月26日時点)
| 指標 | 数値 |
|---|---|
| DAU | 420 |
| 月間表示回数 | 約120,000 |
| 月間クリック | 約3,200 |
| 平均掲載順位 | 26位 |
| インデックス済みページ | 約45,000 |
After(4月第2週)
| 指標 | 数値 | 変化 |
|---|---|---|
| DAU | 150 | -64% |
| 月間表示回数 | 約52,000 | -57% |
| 月間クリック | 約800 | -75% |
| 平均掲載順位 | 38位 | +12位悪化 |
特にひどかったのが車種モデルページ(/bikes/catalog/{slug})とショップページ(/shops/{slug})。この2カテゴリだけで全体トラフィックの7割を占めていたので、ここが落ちると全体が崩壊します。
落ちたページの特徴
落ちたページに共通していたのは「数字の羅列だけで、読む価値がない」こと。
車種ページの例:
- 在庫一覧テーブルがあるだけ
- 車種の歴史や特徴の説明がゼロ
- FAQなし、関連コンテンツなし
- ユーザーが「ふーん」で帰るだけのページ
Googleが言う「helpful content」の真逆でした。
原因分析 — 4つの問題
暴落後、1週間かけて原因を分析しました。
問題1: ページ品質が低い(最大の原因)
4,569車種 × 47都道府県 = 約21万ページを生成していましたが、中身は在庫テーブルと基本スペックだけ。コンテンツの「厚み」がゼロでした。
Googleのヘルプフルコンテンツシステムは、サイト全体の品質を評価します。薄いページが大量にあると、良いページまで道連れにされます。
問題2: ドメインパワーが弱い
外部からの被リンクは約10件。個人開発サイトなので当然ですが、コアアップデートではE-E-A-T(経験・専門性・権威性・信頼性)が重視されます。ドメインパワーが弱いサイトは影響を受けやすい。
問題3: 売り切れページの処理
売り切れた車両ページ(数万ページ)を 200 OK で返しつつ、中身は「この車両は売り切れました」の一文だけ。これが大量の低品質ページとして評価された可能性があります。
問題4: クロールバジェットの無駄遣い
検索結果ページ(/bikes/search?...)に noindex を付けていなかった時期があり、パラメータ違いの大量のページがインデックスされていました。
やったこと① — SEOの基盤修正(4月前半)
パニックにならず、まず基盤から直しました。
売り切れページの改善
Before: 「売り切れました」テキストのみ
After: 同車種の在庫一覧・類似車種の提案・相場情報を表示
売り切れ → 404ではなく200で返す
ただし在庫0台の車種ページ → noindex追加
在庫がある車種の売り切れ個別ページは200で返し、代替情報を充実させる。在庫が完全に0の車種ページだけ noindex にする。この判断が正解でした。
検索結果ページにnoindex
// すべてのsearch結果ページにnoindex
<meta name="robots" content="noindex, follow">
これだけで、Googleがクロールすべきページが明確になります。
JSON-LD構造化データの大量追加
12種類のJSON-LDコンポーネントを作成:
-
Website— サイト全体 -
Product— 個別車両 -
LocalBusiness— バイクショップ -
FAQPage— FAQ(後述のAI生成) -
BreadcrumbList— パンくずリスト(6パターン) -
BlogPosting— ブログ記事 -
ParkingFacility— 駐輪場
resources/views/components/jsonld/
├── website.blade.php
├── product.blade.php
├── model-product.blade.php
├── local-business.blade.php
├── parking.blade.php
├── breadcrumb.blade.php
├── breadcrumb-model.blade.php
├── breadcrumb-parking.blade.php
├── breadcrumb-shop.blade.php
├── breadcrumb-shop-area.blade.php
├── breadcrumb-station.blade.php
└── breadcrumb-search.blade.php
IndexNow設定
サイトマップ生成のたびにIndexNowでBing/Yandexに通知するようにしました。Googleは非対応ですが、Bingからのトラフィックも馬鹿にできない。
// GenerateSitemap.php の最後
$this->info('IndexNowに通知中...');
// サイトマップURLをIndexNow APIに送信
やったこと② — AI活用でコンテンツを一気に厚くする(4月後半)
ここからが勝負でした。4,569車種のページを手動で充実させるのは物理的に不可能。AIを使います。
Claude Sonnet 4でコンテンツ自動生成
2つのArtisanコマンドを作成:
# 車種の特徴・魅力・選び方を生成
php artisan bike:generate-content --chunk=50
# 車種の歴史・系譜を生成
php artisan bike:generate-history --chunk=50
仕組み:
- 車種名・排気量・カテゴリ・在庫数・価格帯をプロンプトに含める
- Claude Sonnet 4(
claude-sonnet-4-20250514)で500〜800字のコンテンツを生成 -
enriched_contentカラム(JSON)に保存 - 在庫5台以上の車種を優先処理
2,000車種分を処理。APIコスト約¥12,000。
人間が書いたら数ヶ月かかる作業を、3日で完了。
FAQ自動生成 + FAQPage JSON-LD
AIが生成したコンテンツからFAQを抽出し、構造化データとして出力:
// enriched_contentがある場合はAI生成FAQ
// ない場合はテンプレートFAQ
@if($model->enriched_content)
{!! json_encode($faqSchema) !!}
@endif
これにより、検索結果にFAQリッチスニペットが表示されるようになりました。CTRが目に見えて改善。
YouTube動画の埋め込み
php artisan youtube:fetch --limit=500
YouTube Data APIで車種名を検索し、レビュー動画をDBにキャッシュ(7日TTL)。車種ページに動画セクションを追加。
滞在時間が伸びる → ページ品質のシグナルが改善。
車種ページのUI大幅刷新
Before: スペック表 + 在庫一覧(2セクション)
After: 6タブ構成
- 在庫一覧(既存)
- 相場分析 — 年式別・走行距離別の価格チャート
- スペック — レーダーチャートで視覚化
- レビュー — ユーザーレビュー
- 動画 — YouTube埋め込み
- FAQ — AI生成FAQ
Chart.jsのチャートは遅延読み込み+ポーリング方式で描画:
// Chart.jsの読み込みを待つポーリング(10秒タイムアウト)
var attempts = 0;
var timer = setInterval(function() {
attempts++;
if (typeof Chart !== 'undefined') {
clearInterval(timer);
initYearChart();
} else if (attempts > 100) {
clearInterval(timer);
}
}, 100);
Chart.jsは<script defer>で読み込んでいますが、DOMContentLoadedのタイミングとChart.jsの読み込み完了が一致しない問題に3回ハマりました。IntersectionObserver + DOMContentLoaded + コールバック登録の組み合わせをすべて試して失敗。結局シンプルなsetIntervalが最も確実でした。
やったこと③ — コンテンツの面を広げる(4月〜5月)
SEOの基盤が整ったら、次はコンテンツの「面」を広げます。
ブログ機能をLaravelでフルスクラッチ実装
WordPressは使わず、Laravel内にブログシステムを構築しました。
なぜフルスクラッチ?
- 同じドメイン・同じデザインで統一感を持たせたい
- 内部リンクを自在に張りたい
- BlogPost → BikeModel のリレーションを活用したい
- MarkdownベースでGitHub管理したい
実装規模:
| カテゴリ | ファイル数 |
|---|---|
| Model(BlogPost, BlogSeries, BlogTag) | 5 |
| Controller | 10 |
| View(Blade) | 5 |
| Route | 1ファイル(blog.php) |
| Command | 4 |
| Migration | 5 |
| 合計 | 約30ファイル |
主な機能:
- Markdown → HTMLレンダリング
- シリーズ・タグ管理
- OGP画像の動的生成(Intervention Image)
- RSS/Atomフィード
- 読了時間の自動計算(日本語: 500文字/分)
-
[riders-map]ショートコードで地図埋め込み - 下書き/公開/スケジュール予約
現時点で18本の記事を公開。すべて実売データに基づいたデータドリブン記事です。
ツーリングスポット・道の駅のページ生成
touring_spots: 279件(全国のツーリングスポット)
roadside_stations: 124件(道の駅)
47都道府県別のインデックスページ + 各スポットの詳細ページ。サイトマップにも追加し、内部リンクでメインコンテンツと接続。
ライダーズマップ(POI 4.9万件)
駐輪場3,569件、駅9,032件、道の駅124件、その他のPOI含め約4.9万件の位置情報をマップに表示。Google Maps APIベースのインタラクティブマップです。
これ単体でSEO効果は限定的ですが、サイトの滞在時間とページ回遊率に効いています。
カテゴリLP 16ページ
プログラマティックSEO:
排気量別: 50cc / 125cc / 250cc / 400cc / 大型 / リッター
タイプ別: ネイキッド / スポーツ / アメリカン / オフロード /
スクーター / アドベンチャー / クラシック / ツアラー /
ミニバイク / ストリート
各ページにKPI6項目(在庫数・平均価格・最安値・人気車種等)、価格分布チャート、人気車種ランキングを自動生成。
ニュースページの完全実装
バイク業界のニュースを自動収集・表示するページ:
php artisan news:fetch # ニュース自動取得
php artisan news:thumbnails # サムネイル取得
php artisan news:ranking # ランキングニュース生成
現在518件のニュースを掲載。モデル別ニュースフィルタリング、コメント機能、いいね機能も実装。ニュースページ自体は直接的なSEO効果は薄いですが、サイトの「新鮮さ」をGoogleに伝えるシグナルになります。
エリアLP品質向上
既存の /bikes/area/{pref}/{slug} ページ(都道府県×車種)を大幅改善:
- タイトル改善(「{県}の{車種}中古車」→「{県}の{車種} 在庫{N}台 | 平均¥XX万〜」)
- KPI6項目追加
- 価格分布・年式分布チャート
- 関連エリアへの内部リンク
- JSON-LD強化
やったこと④ — SEO以外のDAU施策
Googleだけに頼るのは危険。それを痛感したので、他チャネルも開拓。
Yahoo!知恵袋
バイク関連の質問に毎日2〜3件回答。プロフィールにMotoHubのリンクを設置。
ポイント:
- 「中古バイク 相場」「250cc おすすめ」系の質問を狙う
- 回答の最後に「詳しい相場データはこちら」でリンク
- ベストアンサーを複数獲得 → ドメインパワーの微増
X(Twitter)リプライ
バイク購入検討中の人のポストに毎日10件リプライ。botではなく手動で、本当に役立つ情報を添えて。
Twitter/X Bot(自動投稿)も稼働中:
# 7本のArtisan Command
php artisan tweet:new-stock # 新着在庫
php artisan tweet:price-drop # 値下がり
php artisan tweet:bargains # お買い得
php artisan tweet:ranking # ランキング
php artisan tweet:reviews # レビュー
php artisan tweet:trending # トレンド
php artisan tweet:latest-report # 相場レポート
Google Discover対応
ブログ記事をDiscoverに載せるために:
<!-- layout.blade.php -->
<meta name="robots" content="max-image-preview:large, max-snippet:-1, max-video-preview:-1">
- 1200×630pxのOGP画像を全記事に設定
- Article JSON-LDの
imageフィールドを必ず出力 - 感情を動かすタイトル(「22万台のデータで判明した〜」)
LINE Push通知
お気に入り車種の値下げをLINE Messaging APIで通知:
// Flex Messageでリッチカード送信
$service = new LineNotificationService();
$service->sendPriceDropAlert($user, $listing, $priceDiff);
やったこと⑤ — インフラ改善
VPS 4GB → 8GB
ページ表示速度がSEOに影響するため、メモリを倍増。特にMeilisearchがメモリを食うので。
Redis AOF永続化
キャッシュが飛ぶとランキングページ等が一時的にエラーになる問題があったため、AOF永続化を設定。
Meilisearch差分同期
全件同期から needs_reindex フラグ方式に変更。16万件の全件同期は重すぎた。
4ヶ月間のコミットログ
数字で振り返ると:
| 期間 | コミット数 | 主な内容 |
|---|---|---|
| 4月前半 | 約80 | SEO基盤修正、noindex、JSON-LD |
| 4月後半 | 約70 | AI生成、UI刷新、YouTube統合 |
| 5月前半 | 約120 | ブログ、ツーリング、カテゴリLP |
| 合計 | 約300 |
760コミット中の300コミットがこの4ヶ月に集中。1日あたり平均6コミット。仕事しながらの個人開発としてはかなりのペース。
技術スタック全体像
最終的にこうなりました:
Backend: Laravel 12 (PHP 8.3)
Frontend: Blade + Alpine.js + Tailwind CSS v3
React (Quiz/Game用のIsland Architecture)
Search: Meilisearch
Cache: Redis (AOF永続化)
AI: Claude Sonnet 4 (コンテンツ生成)
GPT-4 Vision (バイク画像識別)
Bot: Twitter API v2 (自動投稿7種)
通知: LINE Messaging API
CDN: Cloudflare
Scraper: Python (Scrapy/httpx)
| 項目 | 数値 |
|---|---|
| PHPファイル(app/) | 302 |
| Bladeテンプレート | 156 |
| JSファイル | 82 |
| Artisanコマンド | 66 |
| ルート定義 | 838行 |
| マイグレーション | 79 |
| DBテーブル | 40+ |
現在地(2026年5月)
GSCの数字
| 指標 | 暴落前 | 最低値 | 現在 |
|---|---|---|---|
| DAU | 420 | 150 | 200 |
| 月間表示 | 120,000 | 52,000 | 58,500 |
| 月間クリック | 3,200 | 800 | 1,020 |
| 平均順位 | 26位 | 38位 | 10.7位 |
平均順位は26位→10.7位に大幅改善。 でもDAUはまだ420の半分以下。
これは何を意味するか?
表示回数が戻っていない。 つまりGoogleにインデックスされているページの「評価」は上がったけど、「掲載されるクエリの幅」が戻っていない。コアアップデートの影響はまだ残っています。
DBの現在値
| データ | 件数 |
|---|---|
| 車種マスタ | 4,569 |
| 在庫リスティング | 138,345台(在庫中) |
| 売却済み | 22,158台 |
| ショップ | 12,260 |
| ブログ記事 | 18本 |
| POI(マップ) | 48,886件 |
| 駐輪場 | 3,569件 |
| 駅 | 9,032件 |
| ニュース | 518件 |
学んだこと
1. Googleに依存するリスク
DAU420の時点で「順調!」と思っていた自分が甘かった。Google検索が唯一のトラフィックソースだったので、コアアップデート一発でほぼ全滅。
教訓: トラフィックソースを分散する。SNS、知恵袋、Push通知、メールマガジン。1つのチャネルに7割以上依存してはいけない。
2. 薄いページは負債になる
プログラマティックSEOで大量のページを作るのは簡単。でも中身がなければGoogleに評価されないどころか、サイト全体の評価を下げる。
教訓: ページを作る前に「このページに来た人は満足するか?」を問う。NoならnoindexかNot Found。
3. 個人開発でもデータは武器になる
16万台の在庫データは、大手メディアにはない武器。「22万台のデータ分析」という切り口はユーザーにもGoogleにも刺さる。
中古バイクの平均売却日数が41日とか、1〜2万kmが最もコスパ良いとか、こういうデータは実際にプラットフォームを運営していないと出せない。
4. Claude Codeが開発速度を変えた
4ヶ月で300コミットという異常なペースは、Claude Codeなしでは不可能でした。
具体例:
- ブログ記事のデータ収集(DBクエリ6本)→ 記事生成(Anthropic API)→ チャート画像生成(QuickChart)→ BlogPost保存。この一連の作業を1記事15分で完了
- JSON-LDコンポーネント12種の実装が半日
- カテゴリLP16ページの生成ロジックが2時間
- 車種ページのUI刷新(6タブ化)が1日
「AIに仕事を奪われる」のではなく、「AIで1人が10人分の実装ができる」が正確な表現。
5. 「前に進んだ感がない」時期の乗り越え方
4月中旬〜5月初旬は最もつらい時期でした。毎日コードを書いているのにDAUが150から動かない。
GSCのデータは2〜3日遅れで反映されるし、Googleの再評価には数週間かかる。この「効果が見えない期間」をどう過ごすかが分かれ目。
僕の場合はコミット数を指標にした。DAUではなく「今日は何を改善したか」にフォーカス。1日6コミットを続ければ、いつかGoogleも認めてくれるはず、と。
今後の計画
短期(5〜6月): DAU 300目標
- ブログ記事を30本に増やす(現在18本)
- Google Discover経由のトラフィック獲得
- 知恵袋・X経由の被リンク増加
中期(7〜9月): DAU 500目標
- ユーザー投稿コンテンツ(レビュー・ツーリングレポート)
- メールマガジン
- LINE公式アカウントのフォロワー獲得
長期(年末): DAU 1,000目標
- ASP提携(バイク買取・保険)でマネタイズ
- AdSense + アフィリエイトで月5万円
- トラフィックソース: 検索40% / SNS30% / Direct20% / その他10%
おわりに
コアアップデートで暴落するのは、正直めちゃくちゃ凹みます。3ヶ月かけて積み上げたものが1週間で崩れるんですから。
でも振り返ると、暴落がなければここまで本気で品質改善に取り組まなかったと思います。「数字の羅列だけのページを量産していた」という問題に気づけたのは、アップデートのおかげです。
DAU 200はまだ通過点。420を超えて、500、1,000と伸ばしていく過程も、また記事にします。
個人開発でバイクサイトを運営している人はたぶん僕だけだと思いますが、「Googleコアアップデートに被弾した個人開発者」は山ほどいるはず。この記事が誰かの参考になれば嬉しいです。
MotoHub: https://motohub.jp
GitHub: ausssxi/MotoHub
X: @motohub_jp