期限切れのSSL証明書が、48時間でオンラインショップをGoogleから消した話
水曜日の午前3時14分、証明書が期限切れになった。金曜の朝、オーガニック流入は崩壊していた。運が悪かったのではない。測定可能な怠慢だった。
ポルトのあるオフィス機器販売店が、水曜から金曜にかけてオーガニック流入の78%を失った。サイトには数週間、誰も手を触れていなかった。問題はログの1行に収まっていた。SSL証明書が水曜の午前3時14分に期限切れになっていたのだ。
その朝Googlebotが再びサイトを訪れたとき、返ってきたのはERR_CERT_DATE_INVALIDだった。何もインデックスされなかった。さらに悪いことに、まだキャッシュに残っていた検索結果からクリックしてきたユーザーに対し、このドメインを「安全ではない」と判定した。48時間後にはChromeが、トップページの前にあの赤い警告画面を表示するようになっていた。
なぜGoogleはここまで厳しく反応するのか
2014年からHTTPSはランキングシグナルだ。2018年からChromeはHTTPを「安全ではない」と表示している。期限切れの証明書はHTTPより悪い。壊れたHTTPSだからだ。ブラウザはこれをなりすましの試みと解釈する。実際にはacme.shの更新が失敗しただけであっても。
GooglebotはTLSハンドシェイクの失敗をクロールエラーとして扱う。再試行し、失敗し、また再試行し、また失敗する。数時間でインデックスから外し始める。ページが索引から即座に消えるわけではない。だが信頼シグナルがゼロに落ちたため、ランキングは崩れる。
現場で実際に起きたこと
- 水曜 3:14 — Let's Encryptの証明書が期限切れ。更新cronは過去30日のうちに3回失敗していた。誰も通知メールを見ていなかった。
- 水曜 8:00 — スマートフォンでアクセスした最初のユーザーが赤い警告を目にする。直帰率が急上昇。
- 水曜 11:30 — Googlebotが巡回。ハンドシェイクに失敗し、大量のsoft 404を記録。
- 木曜の朝 — Search Consoleから「カバレッジ」のアラートが届く。誰もSearch Consoleを開かない。
- 金曜 9:00 — 売上が止まる。クライアントが開発者に電話する。開発者は4分で原因を突き止めた。
更新作業そのものは90秒で終わった。順位の回復には23日かかった。その間に競合は主要キーワードで上位を奪い、その後も完全には手放さなかった。
なぜcronは失敗したのか
サーバーは1月にIPを変更していた。DNSは正しく向いていたが、Let's EncryptのHTTP-01チャレンジが、新しいファイアウォールに弾かれていた。そのファイアウォールはISRGのレンジからのリクエストをブロックしていたのだ。更新が試みられるたびに、誰も読まないログにエラーが残された。
これは典型的なパターンだ。証明書は更新が難しいから期限切れになるのではない。自動更新が静かに失敗し、適切なログを監視するアラートを誰も仕込んでいないから期限切れになる。
今日やるべきこと
- cronではなく、証明書そのものを外部から監視する。UptimeRobotやStatusCakeのようなサービスを使い、期限の14日前にアラートを出すよう設定する。
- 通知はメールではなくTelegramかSMS。Let's Encryptの警告メールは、誰も見ていないメールボックスに届いている。
- Search Consoleは毎週確認する。Googlebotが失敗していれば、そこに書いてある。
- 更新は四半期に一度、手動でテストする。本番でcronが動いていてもステージングで動かないなら、設定がドリフトしている証拠だ。
- 緊急時の更新手順を文書化する。金曜の18時にサイトが落ちているときに、acme.shの使い方を学んでいる場合ではない。
期限切れの証明書は、6ヶ月分のSEOを失う最も安価な方法だ。予防にはゼロ円、復旧には給料3ヶ月分かかる。— インシデント後の内部監査、ポルトの顧客
実際のコスト
この店舗の月間売上は、四半期平均より3万1千ユーロ少なかった。サーバーを構築した開発者は復旧作業に180ユーロを請求した。予防のコストはゼロだった。ヘルスチェック用のcronをもう1本と、Telegramへのwebhookを1本足すだけだった。
日本でオンライン販売をしていて、自社サーバーとは独立した証明書監視をまだ持っていないなら、更新が1回失敗するだけで四半期を失う距離にいる。脅しではない。算数だ。
ルールは単純だ。更新するcronを信用してはいけない。cronがちゃんと更新したことを確認する外部モニターを信用すべきだ。