極光日記

nginxで事前にgzip圧縮されたHTMLを配信する

作成日

はじめに

nginxで事前にgzip圧縮されたファイルを配信する方法について調べた内容をまとめました。

HTMLやCSSなどの静的で内容が変わらないファイルは、nginx側でリアルタイムに圧縮せず、事前に圧縮しておくことが可能です。これは以下のようなメリットがあります:

  • 配信時にサーバーのCPU負荷を抑えられる
  • より高い圧縮率(gzip -9zopfliなどの高圧縮ツールが利用できる)
  • コンテンツのキャッシュ性が向上する

リアルタイム圧縮はgzip on;で有効になりますが、zopfliなどによる高圧縮は時間がかかるため、アクセスのたびに圧縮するのは非現実的です。したがって、可能であれば事前に圧縮しておくのが望ましいです。

詳しくは以下の参考リンクをご覧ください:

私はnginxの知識がないために躓いてしまったため、その内容をメモしておきます。

前提構成

以下のようなシンプルな構成を想定します。
test/ディレクトリ内にHTMLおよびCSSファイルがあり、HTMLからCSSを読み込む形です。

.
└── test
    ├── test.css
    └── test.html

今回の要件は以下のとおりです:

  • URLに拡張子なしでアクセスできるようにしたい(例: /test/test/test/test.html を返す)
  • 事前にgzip圧縮されたファイルのみを残し、元ファイルは削除(可能であれば)
  • 圧縮非対応のクライアントへの配慮はしない
    • 現代のほとんどのブラウザはgzipに対応しており、対応していないケースはほぼないはずです

拡張子を省略する理由として、以下のW3Cの記事も参考になります:

https://www.w3.org/Provider/Style/URI

“.cgi” や “.html” のような拡張子は将来的に変わりうるものであり、URIに含めるべきではない。

たとえば将来的にHTMLファイルをPHPなどで動的生成するようになった場合でも、.html に依存しないURIにしておけばリンクがそのまま有効である、という考え方です。

ファイルの最終構成

圧縮後は以下のような構成になります:

.
└── test
    ├── test.css.gz
    ├── test.html
    └── test.html.gz

ポイント:

  • test.html.gz が配信されるが、test.html もなぜか必要(後述)
  • 一方、test.css は削除してもOKだった(.gzだけで配信された)

nginxの設定例

以下は今回使用したnginxの設定例です。キャッシュ制御も含まれており、ある程度実用的な構成になっていると思います。
状況によってはさらにgzip_varygzip_proxiedなども必要かもしれません。詳細は前に掲載したリンクを確認してください。

events {
  worker_connections 1024;
}

http {
  include       mime.types;

  gzip on;
  gzip_static on;
  gzip_types text/css text/javascript;

  server {
    listen 80;
    root /usr/share/nginx/html;

    location ~* \.(js|css?)$ {
      add_header Cache-Control "public, max-age=31536000, immutable";
    }

    location / {
      add_header Cache-Control "no-cache, must-revalidate";
      try_files $uri.html $uri $uri/ =404;
    }
  }
}

補足:

  • gzip_static on; により .gz ファイルが存在すればそれを配信
  • 今回のように完全に事前にgz圧縮されたファイルしか配信しない場合はgzip onはなくても大丈夫でした
  • try_files により $uri.html が付いたファイルを探す(拡張子を省略できる)

ハマりどころメモ

HTMLだけは未圧縮ファイルも必要

.html.gz だけを残して .html を削除すると、404エラーになってしまいました。

そのため、以下のような構成が必要です:

  • test.html → 存在している必要あり(空でもよい?)
  • test.html.gz → 実際に配信されるコンテンツ

gzip_staticならgzファイルがあればそれを配信してくれるはずなのでgzファイルだけあれば大丈夫そうですが、そうではありませんでした。
HTMLファイルも必要ですが、きちんとgzファイルのほうが配信されているようではあり、test.htmlを書き換えてもtest.html.gz(書き換え前)の内容でサイトが表示されました。これならHTMLファイルのほうは空でも良いかもしれません。
try_files の評価対象として .html ファイル自体が存在している必要がある、ということでしょうか?
nginxに詳しくないためよくわかりませんでした……。