移行サイトのSEO・AI-SEO改善を1日で回した — 画像altを480件、記事の要点を203本、OG画像を署名付きに

開発eurekapu

朝いちばんに積み残しを聞いたら、筆頭に出てきたのが移行サイトのSEO/AI-SEO改善だった。判断が要る4件はすでに回答済みで、実装だけが丸ごと残っている。それなら今日やってしまおうと思って、続きを頼んだ。

結論から言うと、この日のうちに Phase A から C まで通って本番に出た。ただし途中で一度、承認したつもりのメッセージが承認として届いていなかった。

承認が届かなかった30分

朝のセッションでは、技術基盤の是正(canonical・robots.txt・sitemap・404の静的化)と、タイトルと説明文の改訂を先に片づけた。ここまではローカル完結なので、勝手に進めてもらってよい。

止まったのはその先だった。Worker の再デプロイ、本番デプロイ、apex ドメインの転送先変更。この3つは押したら外に出る操作なので、実行前に確認を入れさせている。

そこで私が返した「順次進めてください」が、承認として認識されなかった。理由は自分側にあって、ターミナルの入力欄を間違えて日本語をそのままシェルに打ち込んでいた。コマンドとして解釈されて流れたので、前のメッセージと同じ文言だけが残った。判断がつかないと言われ、本番3件は保留のまま、承認の要らないタイトル改訂へ迂回されていた。

.env への OG_SECRET の受け渡しも同じ経路でつまずいた。別リポジトリの .env から該当の1行だけを抜いて移行先に書き出す形にして、値は画面に出さない。コマンドは提示してもらい、実行は自分のターミナルで打った。1回目は打ったつもりで打てておらず、.env が空のままだった。2回目でようやく通り、記事ごとの署名付きOG画像URLが生成されるようになった。

改めて「3件ともやっていい」と伝えたら、そこからは早かった。

著者名の括弧が豆腐になった

Worker をデプロイして、生成されたOG画像を開いた。著者名の全角括弧が □ になっている。

フォントのサブセットに括弧が入っていなかった。サブセット化したフォントを埋め込む以上、使う文字を漏らせば必ずこうなる。文字を足して再デプロイしたら、括弧が括弧として出た。

本番デプロイのあとは、sitemap の234 URL を全件叩いてもらった。全部200。canonical・OG画像・robots.txt・404の表示も実測で確認済み。

apex の転送先はダッシュボードの画面を操作してもらった。パス保持・クエリ保持・301の設定にして、旧サイトの ?p= 形式のURLがどこに着地するかを追う。すると www から記事、記事から末尾スラッシュ付きへと3回跳ねていた。転送先を最初から末尾スラッシュ付きにして、2ホップに縮めた。

200本のメタを、サブエージェントに書かせる

タイトルと説明文の改訂は、記事の中身を読まないと書けない。上位の9本は手で見たが、残り約175本は物量が違う。

サブエージェントには Opus 5 を指定して、147記事を12並列で流してもらった。1回目は引数の渡し方を間違えて空振り。作り直して再起動。

戻ってきた156件のうち17件が未改訂だった。あるバッチの担当が取り違えられていて、同じ記事を2度書いていた。取りこぼしは機械で検出できるので、リストを突き合わせて追加で回す。規律違反(文字数・重複・検索語の位置)は314件から282、32、そしてゼロになった。

同じやり方で、記事冒頭に置く「この記事の要点」を203本ぶん生成させた。ここでも16件が未生成で、追加で埋めた。

面白かったのはその副産物だ。本文を読んで要点を書かせた結果、朝のうちに自分で承認した説明文のほうに事実誤りが2件見つかった。所得控除の種類数と、簿記の起源の世紀が、どちらも本文と食い違っている。数字の食い違いを機械的に洗ったら6件挙がり、4件は別々の対象を数えていただけの誤検出、残り2件が本物だった。本文を正として説明文を直した。

先に説明文を承認し、あとから本文を読ませる順番にしたおかげで、自分の承認ミスが向こうから出てきた。この順番なら、判定だけしていればいい。

見出しの下線を全部外す

昼のセッションで、読んでいて気になっていたことを注文した。見出しに下線が入っていて、本文中のリンクの下線と見分けがつかない。

区別の基準は自分の中でははっきりしていた。同じサイト内の遷移と、外に出るリンクは別物でいい。外部リンクには下線を残す。内部の見出しには要らない。

外してもらうついでに、記事末の定型的なSNS訴求が固定ページ側に残っていることが分かった。前日の除去スクリプトが移行記事のディレクトリだけを見ていて、固定ページを対象に含めていなかった。同じ方針で外した。

直書きの「2026年」を、ビルドのたびに書き換える

プロフィールの記述に、年齢や年数が直書きされていた。WordPress の頃は手で書き換えるしかなかった名残だ。

コンポーネント化すれば済むかと思ったが、コンセプトページのほうは生のHTMLブロックの中に同じ直書きがあり、コンポーネントでは届かない。ビルド時にスクリプトで書き換える方式にしてもらった。来年の元日に自分が思い出す必要がなくなった。

「プロパティは0件です」は確認の仕方が間違っていた

改訂の優先順位を決めるのに、検索コンソールの実データが欲しかった。ところが「このChromeのGoogleアカウントにプロパティが無い」と報告が上がってきた。

そんなはずはない。自分で登録した覚えがある。取り直せるはずだと押し返した。

結果は押した側が正しかった。判断の根拠にされていた画面は、プロパティの有無にかかわらず「追加してください」を出すURLだった。ルートから辿り直したら、2つ目のGoogleアカウントの配下に該当プロパティが座っていた。全221ページぶんの実績を取得。

そこで分かったのが、それまで使っていた推測リストの外れ具合だった。上位だろうと見積もっていた記事のクリックが0で、逆に想定外の記事が400クリックを集めている。実データが来た時点で、以降の作業順はすべて並べ替えた。

画像altを480件、図を読ませて書く

alt が空の画像が947件、77本の記事にまたがっていた。移行元が長年の手作業だったので当然といえば当然だ。

上位20本は本文から内容を特定できたので先に埋めた。問題は残りで、画像を見ないと書けないものが大半になる。そこで Codex に画像を添付して読ませる方式を試し、まず実測した。

  • 1本(8件)で33.5秒
  • 重い10本・344件を並列10で流して7.6分。逐次なら35.7分の見込みで、4.7倍
  • 失敗0本

残り43本も3.5分で返ってきた。この日に付けたのは、Codex に書かせた480件、本文から特定できた上位20本の分、それにカードの見出しに書名が入っていた書影128件(機械的に付けた)。合わせて、alt 未設定は947件から21件まで落ちた。

最後に残った21件には、全部説明がついた。装飾用のストック写真が11件(空altが正しい扱い)、リンク切れで画像そのものが404になっているものが10件。ビルド後のHTMLで2,158件中21件まで確認して、ブラウザで実物の描画も見た。

alt を「埋めるべき欄」として扱うと全部埋めたくなるが、装飾写真に説明を付けると読み上げが冗長になるだけだ。埋めない判断も記録に残した。

TBDはTBDのまま残す

シリーズ目次のページで、17件の画像がすべて「TBD」という未作成のプレースホルダーだった。もう1本、セミナー資料をもとにした財務諸表の記事にも29件ある。

前者は作る、後者は現状維持と決めた。作る予定のないものに埋め草の画像を当てるくらいなら、TBD のまま置いておくほうがあとで自分が分かる。決めたことを台帳に書いてもらって、その場でコミットした。

記事の中で自分の説明が食い違っていた

入門記事の1本で、本文の記述どうしが矛盾していた。制度が改定される前の前提で書いた説明と、あとから追記した説明が同居している。

「検索すれば正しいほうが分かるはずだから、正しい側に統一してほしい」と投げた。事実を確認させたうえで、旧制度の記述は過去形として残し、改定後の説明を本筋にする形に直してもらった。消すのではなく、いつの話かを明示して両方残す。

ずんだもんが黙った理由は、私が窓を閉じたことだった

夕方、作業音声の通知が鳴っていないことに気づいた。セッションが終わるたびに読み上げるはずのものが、無言になっている。

調べてもらうと、読み上げは終了フックからローカルの再生サーバーを叩く仕組みで、そのサーバーのプロセスが消えていた。起動し直したら鳴った。

原因は自分だった。PowerShell の窓を×ボタンで閉じたときに、サーバーごと殺していた。別の用でPowerShellを立ち上げ直したので、窓はあるのにサーバーはいない状態になっていた。

ここで、そもそも常駐サーバーが要るのかという話になった。実測してもらったら、常駐サーバー経由が311ms、Python を直接叩くほうが194ms。しかも古い環境向けのホスト名を毎回引きに行って解決できず、171msを捨てて合計435ms かかっていた。速いと思って置いていたものが、いちばん遅かった。

リポジトリを探させたら、サーバーを使わない再生スクリプトがすでに実装済みだった。

夜のセッションでは、その切り替え計画をレビューさせた。実測値と前提はほぼ裏取りできたが、そのまま実装すると別ツールの完了音だけ確実に壊れることが分かったので、そこだけ設計を直す前提にした。今すぐ手を打たなくても、再起動すればサーバーは復活する。計画書は別リポジトリの memo に「保留」ステータスで移した。

なお、この日のコミットは学習ゲートに何度か止められた。クイズに答えて通したものもあれば、指示してスキップさせたものもある。スキップは理由をログに残す形にしてあるので、あとから見れば区別がつく。

この日の締め

  • 技術基盤の是正(canonical・robots.txt・sitemap・404の静的化)
  • 記事ごとの署名付きOG画像を本番で生成させる
  • 208記事のタイトル・説明文を改訂(規律違反ゼロ)
  • 203記事に「この記事の要点」を追加
  • llms.txt を生成(200記事・27カテゴリ)
  • カテゴリ28ページの説明文
  • 見出しの下線を外す/固定ページの定型訴求を除去
  • プロフィールの年表記をビルド時に自動更新
  • 画像alt 480件を適用(未設定21件まで、内訳は装飾11・デッドリンク10)
  • apex 転送先の変更と転送チェーンの短縮
  • 読み上げのサーバーレス化(設計を1点直してから)

学び

  • 承認は「同じ文言の再送」では届かない。 打ったつもりのコマンドが打てていなかったのと同じで、送った側の実感と、届いているかどうかは別物だった。本番に触る操作ほど、実行側が慎重に止まるのは正しい
  • 量を書かせると、先に書いたものの矛盾が出てくる。 本文を読ませて要点を作らせたら、自分が承認済みの説明文の誤りが2件落ちてきた。順番を工夫すると、自分は判定だけすればよくなる
  • 「無い」と言われたら、その根拠にした画面を疑う。 検索コンソールのプロパティは存在していた。押し返してよかったし、実データが来た瞬間に作業順が全部変わった
  • 速いと思って置いた仕組みが、いちばん遅いことがある。 常駐サーバーは、直接呼ぶより117ms遅く、解決できない名前を毎回引いていた。測るまで気づかなかった
  • 埋めない判断も記録に残す。 装飾写真の空altも、未作成画像のTBDも、決めたことを台帳に書いておけば次に見たときに迷わない