1. はじめに:本番運用で見えてきた構造的な課題
SvelteKit Blog Engine(SKBE)v1.0.0.10のリリース後、実際の運用環境で検証を進める中で、ローカル開発時には見落としていた構造的なボトルネックや改善点がいくつか浮き彫りになってきました。
本プロジェクトは、Cloudflareの無料枠(Pages、D1 SQLite、KV)を活用し、月額コスト0円($0)で稼働するサーバーレスアーキテクチャを前提に設計されています。そのため、トラフィックやデータ量が増加しても無料枠の制限内で安定してサービスを継続できるかどうかが、エンジニアリングにおける最大の焦点でした。
具体的に向き合うべき課題は次の3点でした:
- D1データベースのクエリ効率化:Cloudflare D1の1日あたりの無料書き込み上限(10万行/日)と、頻繁に発生する読み込みクエリをいかに削減するか?
- モバイル表示の安定性:JavaScriptのマウント後にレイアウトを判定していたことで生じる、モバイル画面の微小な累積レイアウトシフト(CLS)をどう解消するか?
- 検索エンジン(SEO)と次世代AIクローラーへの標準対応:多言語ルーティングにおけるcanonical URLとhreflangの矛盾を正し、ChatGPT SearchやPerplexityといった最新AIクローラーへ正しい規格を提供できているか?
これらの課題を解決するため、v1.0.0.11からv1.0.0.13にかけて段階的な改善を実施しました。本記事では、その過程で検討した設計と具体的な実装内容をまとめました。
2. [v1.0.0.11] 安定性の強化:AdSenseの描画崩れとランタイムエラー対応
最初のステップとして、収益化に関わる広告枠のレイアウト崩れと、特定パラメータの流入時に発生していたHTTP 500エラーの解消に着手しました。
デスクトップ環境におけるAdSenseブランク表示の防止
モバイルでは正常に表示されていたGoogle AdSenseのレスポンシブユニットが、デスクトップの特定画面幅において高さ0pxで描画され、空白として残ってしまう事象が確認されました。
- 原因:CSS Gridトラックのレスポンシブ計算タイミングと、AdSenseスクリプト(
adsbygoogle.push)が親コンテナの横幅を測定するタイミングの間にわずかな競合が発生していました。 - 対処:広告ラッパー要素に対して
min-height: 280pxとコンテナスタイルを指定し、スクリプト実行前でも安定した描画領域が確実に確保されるよう構造を補正しました。
特殊文字タグおよび多言語ルーティングの安定化
特殊文字やスペースを含むタグページへアクセスした際、SvelteKitのルートマッチャーとD1 Drizzle ORMのクエリバインディング間でエンコードの不一致が起き、500エラーが発生していた不具合を修正しました。
3. [v1.0.0.12] 構造の大幅改善:柔軟なデザイン管理とD1の負荷軽減
v1.0.0.12では、デザイン管理の自由度を高めつつ、Cloudflare D1の読み込み・書き込み負荷を大幅に引き下げるためのアーキテクチャ刷新を行いました。
1) 3スロット・マルチデザインシステム(Multi-Slot Design Architecture)
一般的なブログエンジン(WordPress、Ghostなど)でデザインを刷新する際、テーマを切り替えると既存の設定が上書きされてしまうため、バックアップの手間がかかるだけでなく、「稼働中の本番サイトで訪問者に崩れたレイアウトを見せることなく、リアルタイムに新デザインを検証する」ことが極めて困難でした。
SKBE v1.0.0.12では、この課題を根本から解決するために**「スナップショット型3スロットデザイン設計」**を採用しました。
3つの独立したデザインスナップショットを同時保持:
- 管理画面のデザインエディタにて、**スロット1(デフォルト・モダン)、スロット2(リデザイン検証用)、スロット3(ダーク/イベント用)**をそれぞれ完全に独立したスナップショットとして保存・管理できます。
- スロット2で新デザインを作成しても、現在公開中のスロット1には一切影響がありません。完成後はワンクリックで公開スロットを切り替えることができ、トラブル時も即座に元のデザインへロールバックが可能です。
運用方針に応じた3つの配信モード:
- 単一スロット固定:管理者が指定した特定のスロットのみを公開
- セッション別ランダムローテーション:訪問者のセッションごとにスロットをランダムに切り替えて配信(A/Bテストや訪問体験の刷新に有効)
- 訪問者によるリアルタイムテーマ切り替え:画面右下のフローティングボタンから、訪問者自身が好みのテーマを選択可能
リソースの無駄を排除した条件付きSSRペイロード設計:
- テーマを3つ保持しているからといって、ページ読み込みが重くなっては本末転倒です。
- 訪問者テーマ選択モードがオフの場合、
+layout.server.tsはアクティブな1スロット分のCSS/設定データのみをHTMLに同梱し、副スロットのデータは一切送信しません。 - 訪問者切り替え機能がオンの時だけ条件付きで副スロットデータを同梱することで、マルチテーマの柔軟性を備えながらも、単一テーマ運用と全く変わらない超軽量な初期表示速度を維持しています。

2) Cloudflare D1インメモリTTLキャッシュと閲覧数バッファリング
Cloudflare D1の無料枠を無駄なく活用するため、Worker内部でのインメモリキャッシュとバッチ処理を導入しました。
- インメモリTTLキャッシュ(
cache.ts):ブログ基本設定、レイアウト情報、タグ一覧など、更新頻度の低いデータに60秒TTLのインメモリキャッシュを適用しました。これにより、初回アクセス時に発生するD1の読み込みクエリ(rows_read)を98%以上削減しました。 - 閲覧数バッチバッファリング(
viewBuffer.ts):記事が閲覧されるたびにD1へ直接UPDATEクエリを発行すると、1日10万行の無料書き込み枠を急速に消費してしまいます。そこで、10件蓄積されるか30秒が経過した時点でまとめてD1に反映するインメモリバッファを構築しました。ユーザーの画面にはバッファ内の保留カウントを即座に加算して返すため、表示の遅延は一切ありません。 - 非正規化カウンターカラムの採用:記事一覧取得時の重い集計(COUNTやJOIN)を回避するため、カテゴリに
post_count、記事にview_countカラムを新設し、記事の作成・更新・削除時に自動同期される設計へと改めました。
3) モバイルCore Web Vitalsの最適化(CLS 0.000達成)
- モバイルでのレイアウトシフト完全防止(CLS 0.000):従来はJavaScriptのマウント後に画面幅を判定してビューを切り替えていましたが、これを純粋なCSSメディアクエリ(
@media (max-width: 768px))による制御へ全面移行しました。ブラウザが最初のフレーム(0.001秒)からモバイルレイアウトを確定するため、CLSスコアを1.0(不良)から0.000(満点)へと改善しました。 - 多言語辞書の平坦化(HTMLサイズ70%削減):フロントエンドには不要な約900個の管理画面専用翻訳キーを排除し、アクセス言語の単一文字列へと辞書データをフラット化しました。辞書サイズが179 KBから9.19 KBへと94.87%削減され、SSR HTML全体の容量も70%以上軽量化されました。
- Webフォントの非同期Preload:
<head>内のWebフォント読み込みをrel="preload"+onload方式に変更し、描画ブロック(Render-blocking)を解消してFCP(First Contentful Paint)の速度を半減(4.6秒 ➔ 2.0秒)させました。
4. [v1.0.0.13] Web標準の完備:SEO正規化とllms.txt
第3段階では、検索エンジンによるインデックスの整合性を整え、最新のAIクローラーに向けた標準エンドポイントを実装しました。
1) SEOカノニカル(Canonical)URLの正規化
多言語ルーティング([[lang=lang]])において、デフォルト言語でアクセスした際にcanonicalメタタグへデフォルト言語プレフィックス(例:/ko)が付与されてしまい、sitemap.xmlやhreflangタグと矛盾する問題が生じていました。
- デフォルト言語プレフィックスの自動除外:システムで規定されたデフォルト言語でアクセスした場合、トップページ(
/)、カテゴリ(/tech)、CMSページともにcanonical URLからデフォルト言語プレフィックスを自動除外するように改修し、サイトマップと完全に一致させました。 - 絶対パスフォールバックの保証:個別記事およびゲストブックにおいて、管理画面の
siteUrlが未設定の場合に相対パスが出力されてしまう例外を点検し、常にurl.originを参照して有効な絶対パスを出力するフォールバックを組み込みました。
2) 閲覧数の10分重複防止とRFC 6265準拠キー
- リロード乱発対策:ブラウザの
sessionStorageに加え、サーバー側でのCookie検証(10分ウィンドウ)を追加し、連続リロードやタブ複製による意図しないカウント増加を防止しました。 - 非ASCIIスラッグでの500エラー解消:日本語や韓国語などのマルチバイト文字を含むスラッグがCookie名に直接渡された際、HTTPヘッダー規格違反(
TypeError: argument name is invalid)で500エラーを吐く現象を解決しました。スラッグをRFC 6265準拠の短い英数字ハッシュ(skbe_v_${hash})に変換することで安全にキーを発行します。
3) 多言語対応の動的llms.txtエンドポイント構築
ChatGPT SearchやPerplexity、Claudeなど、大規模言語モデル(LLM)を活用した検索ボットのインデックスを支援するため、llmstxt.org規格のエンドポイントを実装しました。
- 言語別ルート分岐:
/llms.txt(デフォルト言語)、/en/llms.txt(英語)、/ja/llms.txt(日本語)を提供します。 - 標準Markdown出力:サイト概要、公開カテゴリ、最新記事30件(要約付き)、固定ページ、RSS/サイトマップリンクをMarkdown形式で動的生成します。
- Cloudflare CDNエッジキャッシュ:10分間のCDNキャッシュ(
s-maxage=600)を設定し、モバイルのGoogle PageSpeed Insights監査で発生していたタイムアウト警告(Fetch of llms.txt timed out)を解消しました。
5. 主要な改善指標まとめ
3つのバージョンを通じて達成された主なパフォーマンス指標の比較は以下の通りです:
| 項目 | 改善前 | 改善後 | 効果 |
|---|---|---|---|
| D1読み込みクエリ | ページアクセスごとに直接DB参照 | 60秒インメモリTTLキャッシュ適用 | 読み込み量98%以上削減 |
| D1書き込み頻度 | 記事閲覧ごとに即時DB Write | 10件 / 30秒バッチバッファリング | 1日の無料書き込み枠を保護 |
| モバイルCLS | 1.0(JSマウント後のレイアウトシフト) | 0.000(純粋なCSSメディアクエリ) | 初期表示の画面揺れゼロ |
| 翻訳辞書データサイズ | 179 KB(全言語キー同梱) | 9.19 KB(アクセス言語のみ平坦化) | データ量94.87%削減 |
| SSR HTMLサイズ | 約80〜100 KB | 約20〜25 KB | 70%以上の軽量化 |
| FCP(初回コンテンツ描画) | 4.6秒 | 2.0秒 | 50%以上の高速化 |
| SEO Canonical整合性 | 言語プレフィックスの不整合が存在 | サイトマップおよびhreflangと100%一致 | 重複インデックスのリスク排除 |
| AI検索対応 | 未対応(Lighthouse警告) | 多言語llms.txt標準完全対応 | 次世代AIボットの引用を最適化 |
6. おわりに:開発の舞台裏とHub機能の運用について
v1.0.0.11からv1.0.0.13までをまとめてお届けした理由
実を言いますと、v1.0.0.11は細かなバグ修正が中心であったため単独の記事にするには内容が軽く、メジャーな変更を含むv1.0.0.12と併せて公開する予定で準備を進めていました。
しかし、12の作業完了直後に、非ASCIIスラッグでの閲覧数500エラーや多言語canonical URLの不整合という重要な問題(v1.0.0.13)を発覚しました。実際にエンジンをご利用いただいている方々やクローンしてくださる開発者の方々にとっては、記事を書くことよりも**「コードの不具合を解消し、GitHubへ最優先でパッチを届けること」が急務**であると判断しました。
緊急修正とリリースを優先して行い、システムの安定性をしっかり確保してから執筆にあたった結果、11から13までを総括する形での開発ログとなりました。
Hub機能の一時停止と再オープンについて
現在、本ブログのHub(サブブログ/ハブ)機能は、Google AdSenseの審査対応に伴い一時的に非公開としております。審査プロセスにおけるサイト階層の整合性を維持し、クローラーの巡回ノイズを防ぐための対応です。
AdSense審査が完了次第、ハブ機能全体の点検とリファクタリングを終えたうえで再オープンいたしますので、楽しみにしてくださっている皆様には今しばらくお待ちいただけますと幸いです。
v1.0.0.11からv1.0.0.13にかけての改善は、単に機能数を増やすことではなく、Cloudflare Free Tierという制約の中で実用的なパフォーマンス、安定性、そしてWeb標準への適合性を極限まで高めるためのプロセスでした。
SKBEのソースコードはすべてGitHubにて公開されています。SvelteKitを使ったサーバーレスブログの構築やパフォーマンスチューニングに関心のある方々にとって、本記事が少しでも参考になれば幸いです。
コメント 0件
コメントを投稿するにはログインが必要です。