Content-Encoding: gzip を付与してコンテンツを配信する
この記事では、パフォーマンス改善を試みた際に遭遇した問題をもとに、以下の流れに沿って解説していきます。
-
Content-Encoding: gzip
とは - Firebase の圧縮技術
- gzip圧縮をかけてS3にアップロードする
- S3を使ってたらCloudFrontを使うと良い
[問題]: react-scripts build を改善させたい
create-react-app
で作成したSPAのパフォーマンス改善をしていたのですが、ejectなどを行っていないので何から手を付けてよいか分からなかったというのが発端です。
とりあえず、公式でも推奨されてるように source-map-explorer
を使用してバンドルサイズを計測し、大きな割合を占めているライブラリ等がないかを見ていました。特に目立つものはありませんでした。
CircleCIの config.yml
を眺めていたら、CI上でビルドしてS3にアップロードしてCloud Frontで配信していることが分かりました。
気になったので、devtoolで見たところ Content-Encoding
が設定されていません...😲
ここからgzip圧縮を有効にするまでの冒険が始まりました。
Content-Encoding とは?
そもそもの Content-Encoding
についてですが
どの圧縮アルゴリズムを使用して対象のコンテンツを圧縮しているかを示すヘッダー情報です。
gzip
, deflate
など様々なアルゴリズムがあります。
よく見かける形式だと Content-Encoding: gzip
でしょうか。
HTML, CSS, JS などをレスポンスとして返す際、圧縮したファイルを返すことでコンテンツサイズを減らすことでパフォーマンスを改善するという目的があります。
詳しい解説は省きますが、
CDNでWEB高速化コンテンツ圧縮(gzip)の設定と注意点 という記事を参考として置いておきます。
Firebase と圧縮技術
gzip圧縮をしてない問題に直面した際、
管理している他のサイトやアプリケーションではどのようになっているのか気になり、まずは Firebase Hosting について調べてみました。
Content-Encoding: br
と書いてありました。
Googleが開発したBrotli圧縮のアルゴリズムを示しており、gzip圧縮よりも圧縮率が向上しているそうです。
firebase deploy
コマンドでデプロイしているのですが、ただbuild済みのファイルをアップロードして配信するのではなく、Brotliアルゴリズムを用いて圧縮した上で Content-Encoding: br
ヘッダーを付与して配信してくれてるそうです。ありがたい🙏
Chrome, Firefox, Safari, Edge でもサポートしているそうなので、IE11サポートをしていないプロダクトであれば導入しても良いかもしれませんね。
圧縮したファイルをS3にアップロードする
ここまでで Firebase Hosting では自動でコンテンツを圧縮してくれることが分かりました。
ですが、ビルドした.js
, .css
ファイルをS3やGCSなどにアップロードして配信している場合、普通にアップロードするだけだと Content-Encoding
ヘッダーは付与されません。
ここでは .js
, .css
ファイルにgzip圧縮をかけてヘッダーを付与してS3にアップロードする手順を示します。
gzip
コマンド .js.gz
, .css.gz
の拡張子がついたファイルを生成します。
gzip ./build/static/js/*.js ./build/static/css/*.css
圧縮したファイルの拡張子から .gz
を削除
ls ./build/static/js/*.gz | sed -e s/\.gz// | awk '{print $1 ".gz " $1}' | xargs -n 2 mv
ls ./build/static/css/*.gz | sed -e s/\.gz// | awk '{print $1 ".gz " $1}' | xargs -n 2 mv
S3へ Content-Encoding
, Content-Type
を付与してアップロード
aws s3 sync ./build/ s3://{BUCKET_NAME} --exclude "*.css" --exclude "*.js"
aws s3 sync ./build/ s3://{BUCKET_NAME} --exclude "*.css" --include "*.js" --content-encoding "gzip" --content-type "text/javascript"
aws s3 sync ./build/ s3://{BUCKET_NAME} --exclude "*.js" --include "*.css" --content-encoding "gzip" --content-type "text/css"
これらのステップで Content-Encoding: gzip
が付与され圧縮済みのコンテンツが配信されるようになります。
しかしJS, CSSだけでなくHTMLなども圧縮して配信したいとなると、コマンドを増やすのは手間です。
次のセクションでは、CloudFrontというCDNを利用した圧縮の設定について説明します。
CloudFrontで圧縮の設定をする
まずは先程のようにファイルをS3へアップロードします。
Content-Encoding
などは付与しなくて大丈夫です。
aws s3 sync ./build/ s3://{BUCKET_NAME} --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers
CloudFront側の設定をします。
以下の記事を参考に、S3のバケットと対応させました。
CloudFront の設定が完了したら設定されるドメインにアクセスします。
コンテンツの圧縮が設定されていない場合のdevtoolの表示です。
2.4777e926.chunk.js
, main.6b9f388c.chunk.js
に着目します。
この状態だとgzip圧縮がされてないファイルを配信しているので、CloudFrontで圧縮の設定をしていきます。
CloudFront Distributions
の画面にアクセスします。
コンテンツ圧縮の設定をしたい Distribution の ID をクリックして遷移します。
遷移したら、Behaviors
タブをクリックします。
Edit
画面に遷移し赤枠で示した Compress Objects Automatically
を有効にします。
これで完了です。
設定が完了したら実際に見てみると、Content-Encoding: gzip
が付与されていることが分かります。
比較のために、それぞれでのコンテンツサイズを表にまとめておきました。
2.4777e926.chunk.js | main.6b9f388c.chunk.js | |
---|---|---|
gzip無 | 132 kB | 1.4 kB |
gzip有 | 43.0 kB | 1.1 kB |
Firebase Hostingのようにコンテンツ圧縮と配信を自動で行ってくれます。ありがたい🙏
今回は検証していませんが、GCSを使用してる場合はCloud CDNを使うことになると思います。
(Compress Objects Automatically
と同じ機能はあるのかな...)
この記事では、CloudFrontを使用して手軽に Content-Encoding: gzip
の付与を行うことについて説明しました。
1Byteでも削ってコンテンツ配信をする助けになればと思います。
Discussion