最初に違和感を覚えたのは、登録数ではなかった。「メスガキ」だった。
少し前に、「メスガキ英語学習」というキャラクター英会話を作って、記事にした。挑発的な口調のキャラと英語で会話する、という尖った企画だ。正直バズる手応えがあったし、語学学習のフックとしてユーザーにちゃんと価値も出せると思っていた(この企画をなぜ作ったか・どう刺さったかは メスガキ英会話で検索上位を取って、そして失った話 に書いた)。
実際、試みは半分成功した。「メスガキ英会話」や類似のクエリで、説明ページと自前記事が検索の1位・2位を取った。Kotonia本体以外では、これが一番強い集客フックになっていた。
ところがある日、同じクエリでもう一度調べると、自分の記事も説明ページもヒットしない。代わりに、クロスポストしたはずのZennの記事だけが、1位で上位表示されていた。
自分のサイトに書いた原本が検索から消えて、ミラーのつもりだったZennだけが残っている。
この違和感が、すべての調査の入り口だった。
そして調べ始めてみると、もうひとつ別の症状にも気づいた。個人開発サービスの登録数が、ある日を境に止まっていた。
最初は登録フォームを疑った。直近でGoogleログインや登録まわりの変更も入れていたので、どこかで登録導線を壊したのかもしれないと思った。
ただ、本番のanalyticsを見ていくと、どうも話が違った。
登録フォームで詰まっているというより、登録ページに来る前の流入が止まっている。
そこから記事流入を疑って、Zenn、dev.to、自前ブログのcanonicalまわりを調べ直した。すると、自分がかなりまずい前提で記事を運用していたことに気づいた。
この記事はその調査ログです。
特定のサービスを責める話ではなく、自分の運用ミスの話です。Zennは技術記事の配布面として強い。ただ、自前サービスのSEOを育てたいときに、同じ本文をそのまま全文クロスポストするのは危ない、という話です。
何が起きたか
Kotoniaという個人開発サービスを運用している。
週末に少し新規登録が出ていたのだが、2026-05-24(日)の朝以降、新規登録が止まった。
本番のadmin APIで、管理者・自分用のユーザーIDを除外して確認したところ、最後の新規登録は 2026-05-24 06:21 JST だった。
日別に見ると、直前までは登録があった。
| Date JST | New users |
|---|---|
| 2026-05-22 | 4 |
| 2026-05-23 | 2 |
| 2026-05-24 | 4 |
でも、そのあと止まった。
最初に疑ったのは登録処理だった。直近でGitHub上には登録まわりの変更があった。
- emailのunique制約
- Google OAuth
- Googleログイン時のアカウント統合
- ランディングページ追加
- 一部CTAの変更
ただ、analyticsを見ると、少し違う絵が出た。
登録フォームではなく、登録ページ到達が消えていた
/register/ の到達を見ると、こうなっていた。
| Date UTC | Register pageviews | Unique sessions | Signup conversions |
|---|---|---|---|
| 2026-05-22 | 3 | 3 | 2 |
| 2026-05-23 | 12 | 8 | 4 |
| 2026-05-24 | 0 | 0 | 0 |
| 2026-05-25 | 0 | 0 | 0 |
つまり、登録ページに来ているのに登録できていない、というより、そもそも登録ページまで来ていない。
この時点で、登録フォーム単体のバグよりも、上流の流入が変わった可能性の方が高くなった。
もちろん計測の穴はあった。Google OAuth経由の登録では、パスワード登録と同じ signup conversion がフロントで発火していなかった。なのでconversionは一部過小計測される。
ただ、新規ユーザー作成そのものも止まっていたので、今回のゼロ化の主因はそこではなさそうだった。
どこから来ていたのか
直近14日間の上位referrerを見ると、主にdirectとGoogleだった。
| Referrer | Pageviews | Unique sessions |
|---|---|---|
| direct | 149 | 61 |
| 113 | 21 | |
| zenn.dev | 4 | 1 |
| Bing | 3 | - |
| DuckDuckGo | 2 | - |
| ChatGPT | 2 | - |
Zennからの直接流入は、確認できた範囲では大きくなかった。
なので「Zennからのクリックが止まったから登録が止まった」とは言えない。
ただ、検索面で記事がどう扱われているかは別問題だ。Google検索からの流入が見えているなら、記事のcanonical、重複本文、外部クロスポストが効いてくる。
そこで、Zennとdev.toの扱いを確認した。
自分がやっていた記事運用
自分の想定では、こういう運用だった。
- Kotoniaに記事全文を公開する
- 同じMarkdownをZennにも置く
- Zenn側のfrontmatterに
canonicalを書く - GoogleにはKotonia側を正規URLとして見てもらう
- Zennは配布面として使う
Markdownにはこういうfrontmatterを書いていた。
canonical: "https://kotonia.ai/articles/<slug>/"
これで「Zennに全文を置いても、SEO上の正規URLはKotoniaになる」と思っていた。
でも、ここが間違っていた。
Zennではcanonicalが効いていなかった
実際に公開済みのZenn記事のHTMLを確認すると、link rel="canonical" はKotoniaではなくZenn自身を向いていた。
つまり、自分がMarkdownに書いていた canonical は、Zennの公開HTMLには外部canonicalとして反映されていなかった。Zennの公式ドキュメントやCLIのfrontmatter仕様、editorの型・validatorまで見たが、外部canonicalを指定する項目はどこにも無かった。
curlでHTMLを見た瞬間に、自分の運用前提が崩れた。「Zennにcanonicalを書いているから大丈夫」ではなかった。
(公開HTMLでの確認手順、Zenn frontmatterの正確な仕様、dev.toとの比較、参照した一次ソースは、技術面だけ切り出してZennに別記事としてまとめた → Zennのcanonicalは公開HTMLに効かない(技術検証))
dev.toはcanonicalが出ていた
一方で、dev.toに出していた英語記事では、HTML上でKotoniaへのcanonicalがちゃんと出ていた。同じ「frontmatterにcanonicalを書く」でも、サービスによって効く・効かないが分かれる、ということだ。
ただ、canonicalが効いたとしても、必ず検索で自前サイトが勝つとは限らない。ドメインの強さ、公開直後の鮮度、ユーザー行動で、外部ドメインの方が出ることはある。
弱い個人開発ドメインが育つ前に、強い外部ドメインへ全文を置くこと自体がリスクになる。
何がまずかったのか
一番まずかったのは、Zennを「配布面」と「全文ミラー」の両方として使っていたことだ。
Zennは強い。
強いドメインに、同じ本文を、同時期に、全文で置く。
しかも公開HTML上ではZenn自身がcanonicalになっている。
この状態だと、Googleから見てZenn記事の方が正規に近く見えてもおかしくない。
自分のサイトに記事を書いて、自分のサイトのSEO資産にしたかったはずなのに、実際には強い外部ドメイン側に本文と検索評価を渡していた可能性がある。
これは、かなり痛い。
登録数低下の原因だったのか
ここは断定しない。
今回の登録停止について、Zenn canonical問題が直接原因だったとは言い切れない。
実データ上、Zennからの直接流入は大きくなかった。登録ユーザーの入口も、トップページ、動画系ページ、英語ページ、チャットページなどが混ざっていた。
なので、今回起きたことはこう見るのが正確だと思う。
- 登録停止の直接原因はまだ特定できていない
- ただし、登録ページ到達が消えているので、登録処理より流入側の問題に見える
- 流入側を調べる過程で、Zenn全文クロスポスト運用の大きな穴が見つかった
- これは短期の登録数とは別に、長期SEO上かなり重要な問題
原因特定ではなく、重要なヒントを掴んだ、という感じだ。
これからの運用 — teaserで逃げるのではなく、別バージョンで戦う
最初に出した答えは「Zennにはteaserだけ置いて、全文はKotoniaに置く」だった。実際に一度、全記事を要約版に縮めた。
でも、これはやめた。元の全文に戻した。
理由は2つある。
ひとつは、Zennからは普通に恩恵を受けているということ。技術記事の配布面として強いし、読んでくれる人がいる。canonicalが効かないからといって、せっかく書いた本文を要約に痩せさせて逃げるのは、自分の中で筋が通らなかった。
もうひとつは、要約版は読者にとって中途半端だということ。「続きは自前サイトで」は書き手の都合であって、読者の体験ではない。
そこで方針を変えた。全文を両方に出す。ただし同じ本文にはしない。
Googleがcanonicalを無視して強いドメインを選ぶのは、2つのページが「ほぼ同じ本文」だから、どちらか一方でいいと判断されるからだ。だったら、near-duplicateにしなければいい。
- Zenn / dev.to: 技術コア版。純粋に技術クエリで探している人向け。再現手順、数字、コード。規約に収まる安全な範囲で全文を出す。
- Kotonia (
/articles): 情熱・戦略版。同じ題材でも、「なぜ個人開発者がこの残存領域を攻めるのか」「実需はどこにあるのか」という裏の文脈を主役にする。Zennとは狙うクエリも構成も違う。
同じ素材から、インテントの違う2本を書く。そして両者を有機的なリンクで繋ぐ。Zennの技術版から「この判断に至った戦略はこっち」と自前へ、自前から「技術の詳細はこっち」とZennへ。canonicalという裏技ではなく、中身が違うから両方indexされて両方読まれる、という状態を狙う。
イメージしているのは、OSSコミュニティがfine-tuningやモデル検証を率直に書いている記事のスタイルだ。手の内を全部見せて、過程も失敗も数字も出して、関連する記事へ自然にリンクを張っていく。あれはcanonicalで操作しているんじゃなくて、コンテンツの密度と相互リンクで読まれている。
露骨で製品ドメインに載せたくない作例は、別slugで noindex + sitemap除外のゲートの奥に置く。index対象を安全に保ったまま、「見たい人だけ」に届ける。これならSafeSearchが製品ドメイン全体を巻き込むリスクも避けられる。
計測も直す
今回の調査で、SEO運用だけでなく計測にも穴があった。
少なくとも次は保存する。
- ユーザー作成時のprovider
- password
- 初回流入path
- 初回referrer
- UTM
- session_id
- 登録前に踏んだCTA
これがないと、登録が増えたときも減ったときも、最後は推測になる。
今回も、本番データからかなり見えたが、最後の一段は推測になった。
個人開発でも、流入元と登録経路だけは最初から残しておいた方がいい。
エージェントに任せるほど、自分で違和感を見る
もう一つ、少し恥ずかしいが大きな反省がある。
最近の開発は、かなりエージェントに任せている。
自分で全部のコードを書くというより、ハーネスを書く、方針を出す、実装を任せる、レビューする、直す。いわゆるバイブコードに近い時間も増えた。
これは本当に助かっている。開発速度はかなり上がった。自分一人では手が回らなかった機能、調査、運用改善まで進められるようになった。
ただ、その一方で、エージェントに任せるほど「自分で見て違和感に気づく力」を手放してはいけないとも思った。
今回で言えば、Markdownに canonical と書いてあることと、公開HTMLに rel="canonical" がどう出ているかは別物だった。
エージェントは記事を作れる。投稿手順も整えられる。docsも書ける。けれど、検索結果でどちらのページが出ているか、公開HTMLが本当に意図通りか、流入が落ちたときに何が変なのかは、最後は自分が疑って見に行く必要がある。
特にSEOは、実装が正しいだけでは足りない。
- 公開HTMLでどう見えているか
- Googleにどう解釈されているか
- 強い外部ドメインに本文を渡していないか
- 登録数や流入に変な段差がないか
このあたりは、コードレビューだけでは拾いきれない。
エージェントで開発を加速すること自体は間違っていない。むしろ、これからも使う。
ただし、事業や流入に直結する場所では、実物を見る。ブラウザで見る。HTMLを見る。Search Consoleを見る。analyticsを見る。
「任せる」と「見なくなる」は違う。
今回の失敗は、それを思い出すには十分すぎた。
学び
今回の学びは、かなりシンプルだった。
canonicalは、書いたつもりではなく、公開HTMLで確認する。
Markdownに canonical と書いてあるかではなく、実際のページにどう出ているかを見る。
curl -s https://example.com/article \
| rg 'rel="canonical"'
そして、外部サービスのfrontmatterは、対応していないキーを書いても無視されることがある。
当たり前の話だけど、運用が流れていると見落とす。
特に個人開発サービスの初期SEOでは、記事一本一本が資産になる。強い外部ドメインに同じ本文を全文で置くなら、near-duplicateとして自分の検索評価を渡していないか、先に確認した方がいい。
自分はそれを怠った。
そして、canonicalが効かない面では、teaserで逃げるのではなく、別インテントの2本を書いて有機リンクで繋ぐ。同じ本文を2箇所に置かない。これが今の結論だ。
技術面の詳細はZennに分けた
公開HTMLでの確認手順(curl)、Zenn frontmatterの正確な仕様、dev.toとの比較、参照した一次ソースは、技術記事として切り出してZennに置いた。
→ Zennのcanonicalは公開HTMLに効かない(技術検証)
この記事(Kotonia版)は、その検証に至るまでの調査と、運用・思想をどう変えたかの記録。同じ題材を、技術リファレンス(Zenn)と意思決定の物語(こちら)に分けて、有機リンクで繋いでいる。canonicalに頼らず両方を読んでもらうための実験そのものでもある。
公開前メモ
- 公開時は、登録数などの具体値をそのまま出すか、少し丸めるか決める。
- Zennに投稿する場合は、この本文をそのまま転載せず、技術コア版として別構成で書き直す(near-duplicate回避)。自前版とは有機リンクで相互に繋ぐ。
- この記事自体のcanonicalはKotonia側に置く(自ドメイン内の自己canonicalは正しく効く。クロスドメインで強い外部に渡すのとは別の話)。