静的サイトで JS/CSS のキャッシュを防ぎつつレポジトリをキレイに保つ

ABOUT」のページをリニューアルしました。

1つ前のデザインのままだったものを、現在のデザインに沿って作りなおして、ついでに文章やレイアウトなども整えました。

公開したものの問題発生

意気揚々と公開してみたのですが、1つ問題が見つかりました。

ページを公開したあと、本番環境を表示するとデザインが崩れていました。そのあと、ページをリロードすると期待通りの結果になりました。

原因は CSS がキャッシュされていた ことでした。

たとえページが更新されていたとしても、JavaScript や CSS は古いキャッシュを使い続けてしまうことがあります。

で、この問題に対処しようとしたのですが、「静的生成」「生成結果を GitHub で管理」「キャッシュ問題への対策」の 3 つのいいとこ取りをしようとすると、意外に複雑でした。

その話をいまからします。

キャッシュ問題の一般的な解決策

よく見る解決索は、JS や CSS の URL の最後に、URL パラメーターとしてタイムスタンプを入れておく方法です。

<link href="/stylesheets/foo.css?1348129300" ... />
<script src="/javascripts/bar.js?1282104010"></script>

このようにすると、次のような効果が期待できます。

  • JavaScript や CSS が更新されたとき:
    • タイムスタンプが変更されていれば、必ずサーバーに取りに行く。
  • JavaScript や CSS が更新されないとき:
    • タイムスタンプが同じなので、キャッシュを使い続ける。

このサイトでも、同じ方法を使うことにしました。ただ、Jekyll で静的にサイト生成しているので、サーバー側で動的にタイムスタンプを埋め込むことはできません。

となれば、タイムスタンプを埋め込んだ状態で Jekyll で HTML を生成すれば万事解決しそうです。

しかし、もう一点、悩みどころがあります。

GitHub 上に生成結果の HTML をコミットしている

俺の最強ブログ システムが火を噴くぜ でも書きましたが、このサイトでは Jekyll でビルドした生成結果を GitHub 上に html ブランチとしてコミットしています。

その理由は次の通りです。

  • 生成結果の HTML についても差分を管理したい。
    • 何か修正をしたときに変な差分が発生していないかを git diff で確認できる。
    • 任意の時点のサイトの生成結果を (再ビルドを行うことなく) 知ることができる。
  • サーバー側では html ブランチに追従するだけでデプロイが完了する。
    • サーバー側にビルド環境を整えなくてよい。
    • 生成処理はけっこう重いので、(格安レンタルサーバーでは) 時間がかかるプロセスが殺される可能性がある。

さて、前述の方法で「キャッシュ問題」に対処するには、JavaScript や CSS を更新するたびに、HTML を書き換えることになります。

たとえば、「JavaScript の更新」をしたとすると、そのスクリプトを利用する「すべての HTML を更新」することになります。このサイトに限っていえば、数百ある過去記事のすべての HTML が一緒に更新されます。

論理的には「JavaScript を 1 回書き換えた」だけなのに、レポジトリ上で数百の HTML ファイルが一緒にコミットされます。HTML の更新履歴に「JavaScript を更新」といった記述が並ぶことになります。

これは美しくありません。

そこでアクロバティックに解決

最初は SSI を使おうかとも思ったのですが、「Jekyll の開発サーバー上での表示が困難」「静的生成という大原則が崩れる」ことを理由にパスしました。

いろいろ悩んだ結果、デプロイ時の処理を次のように変更することを思いつきました。

  1. html ブランチを git pull する (※いままではこの処理のみだった)。
  2. 全部の HTML について、JavaScript・CSS の URL に URL パラメータを付与する。

この付与処理を実現するのが、次のスクリプトです。

だいぶアクロバティックですが、静的生成の大原則を守りつつ、生成結果のレポジトリ上の diff が大きくなりすぎないように工夫してみました。

しばらくはこれでやってみようと思います。