営業用サンプルLPが19MBでデプロイされていた話


副業のHP改修事業で、営業先に見せるサンプルLPを Claude Code とAIで作っている。

そのうち1つを Cloudflare Pages にデプロイしたあと、何気なくファイルサイズを確認して凍った。

19MB。

1ページのHTMLファイルが、19メガバイト。

19MBの実害

光回線で見ると、体感0.1秒未満で開く。重いという感覚すらない。

でも、営業先がスマホで開いたらどうなるか。

  • 4G の格安SIM、混雑時間帯:20〜60秒待ち
  • 3G エリアや電波の弱い場所:数分〜タイムアウト
  • ギガが残りわずかなSIM:開いた瞬間に容量を食い潰す

私の営業先は、地方の士業の先生(50〜60代)。外出先のスマホで開く可能性は低くない。

「ローディング待ちで閉じられる」が一番ありえる。営業の入口でこれは致命的。

私のミス

デプロイ前に du -sh を打っていなかった。

「AIが書き出してくれたHTMLをそのまま上げる」しかしていなかったので、サイズを気にする発想自体がなかった。

普段なら数十KBで済むものが、何かのはずみで19MBになっていたら、普通は気づく。でも今回は気づかなかった。

理由はもう1つあって、私がサンプルを確認するときの環境が、自宅の光回線+PCだったから。

光回線で見ると、19MBのページも体感0.1秒未満で開く。「ちょっと重いな」とすら感じない。

これが家の外で、スマホで開いていたら一発で気づけたはずだ。「いつまで経ってもロード終わらない」とイライラしながら待つことになるから。

作る側の環境と、見る側の環境が違う。これを忘れていた。

19MBの正体

不思議だったので分解してみた。HTMLの構造は3つに分かれていた。

  1. 18.5MB:JSONで書かれたアセット一覧
  2. 558KB:本体のHTML文字列
  3. 6KB:起動スクリプト

JSONの中身は何かというと、249個のファイルが入っていた。内訳は248個のフォントファイルと、1個のPNG画像。

それぞれが base64 という形式で、文字列に変換されて詰め込まれていた。

ブラウザでページを開くと、起動スクリプトが「JSONからフォントと画像を取り出して、HTMLに差し込む」という動作をする。つまり全部が1ファイルで完結している、オフライン動作する仕組みだった。

これは AI のArtifact機能(HTMLをパッと書き出してくれる機能)の標準的な書き出し形式らしい。便利な機能だけど、本番に上げるには重すぎる。

なぜフォントが248個もあるのか

これも調べた。

日本語フォント(Noto Sans JP・Noto Serif JP)は、文字数が多いのでファイルが大きい。Google Fonts は「漢字ブロックごとにファイルを分割しておいて、ページが使う文字に必要な分だけダウンロードする」という工夫をしている。

その分割数が248個。

普段は Google Fonts の CDN(配信サーバ)からブラウザが必要なものだけ取りに行くので、ユーザーが248個全部をダウンロードすることはない。

ところが Artifact の standalone 書き出しは、248個全部をHTMLに入れてしまう。「オフラインで動かしたい人」を想定した仕組みだから。

解体と最適化

3ステップで解体した。

  1. JSONからアセットを取り出して個別ファイルにする
  2. 248個のフォントは全削除して、Google Fonts CDN へのリンクに置き換える
  3. PNG画像(1.6MB)を WebP(42KB)に変換する

WebP は Google が作った画像形式で、JPEGより圧縮率が高い。Pillow(Pythonの画像ライブラリ)で1行で変換できる。

ヒーロー画像は1672×941ピクセルあったので、表示に必要な1400ピクセルに縮小もした。

結果は、19MB → 75KB。99.6%減。

営業に間に合った

このサンプルは、別の士業先生(こちらは初回営業中)への送付用ではなかったが、別案件の最適化作業にも応用が効いた。

すでに営業メールを送ってしまった案件のサンプルLPも、同じパターンで重かった。ヒーロー画像が1.88MB、トラック写真が1.30MB。両方ともAIが書き出したPNGをそのまま上げていた。

メール送信の数時間後に気づいて、急いで最適化+再デプロイ。営業先がアクセスする前にギリギリ間に合った。たぶん。

学んだこと

  • AIが書き出してくれたHTMLは、本番に上げる前に必ずサイズを確認する
  • du -sh . で1コマンド、これを儀式にする
  • 画像はPNGのままにしない。WebPに変換する1行で十分
  • 「便利機能」と「本番運用」は別物。間に1ステップ確認を入れる必要がある
  • 自分が見ている環境と、相手が見る環境を一致させる。光回線+PCで作ったものを、4Gスマホで見直す

AIが速いと、確認をスキップしたくなる。スキップした分のリスクは、本番に出してから返ってくる。

まとめ

  • 営業用サンプルLPが19MBになっていて、スマホで数十秒待ちだった
  • 自宅の光回線+PCで確認していたから気づかなかった。家の外でスマホで見ていたら一発で分かった
  • 正体はAIが書き出した standalone HTML。248個のフォントと画像が base64 でJSONに詰め込まれていた
  • 解体してCDN置換+WebP化で 19MB → 75KB(99.6%削減)
  • デプロイ前に du -sh を打つ儀式を作った

便利だからといって、確認まで省いていい理由にはならない、という当たり前を再確認した。