HTTP コンテンツ圧縮でパフォーマンス改善
事業本部 プロダクト開発室のエンジニアの中畑です。
オンライン診療・服薬指導・クラウド診療支援システム「CLINICS」の開発・基盤周りを担当しております。
今回は、HTTP のコンテンツ圧縮について調査・対応する機会があったので、本ブログにて紹介したいと思います。
HTTP コンテンツの圧縮とは
HTTP コンテンツの圧縮とは、HTTP の通信において Web サーバー側が返すデータを、なんらかの形式で圧縮してクライアントに返すことです。圧縮されたレスポンスをクライアント側は解凍して利用します。
HTTP コンテンツの圧縮によって得られるメリット・デメリットは以下の通りです。
⤴ メリット
- 通信の帯域使用量を減らせる
- それによって通信にかかる時間を削減し、ページ表示速度を向上できる
⤵ デメリット
- 圧縮・解凍コストがかかる
- ただし、圧縮・解凍コストはほとんどの場合は小さいため、メリットを下回る
- 大容量ファイルやもともと圧縮されているファイル(画像や動画、PDF ファイルなど)を圧縮するのは、圧縮してもサイズがそれほど小さくならないため非効率である
- サイズがあまり削減できない割に、圧縮・解凍に CPU リソースを使い、数百 MB を超えるファイルになるとそれぞれ数秒かかることもある
HTTP コンテンツを圧縮するためには
HTTP コンテンツを圧縮するためには、クライアントが解凍可能な圧縮形式を指定する必要があります。解凍可能な圧縮形式を指定するには、リクエストヘッダにAccept-Encoding
ヘッダを指定します。
最近のブラウザでは、HTTP リクエスト時に自動的にAccept-Encoding
ヘッダを自動的に付加してアクセスしているので、ブラウザ経由の場合は特に明示的に指定する必要はありません。Chrome, Safari, Edge など、ほとんどのメジャーなブラウザではAccept-Encoding: gzip, deflate, br
が指定されています(※2021-01-23 時点)。
圧縮形式(gzip, deflate, br)
圧縮形式はいくつかありますが、ブラウザを利用する場合は以下のいずれかが選択肢になります。
- gzip: LZ77 と 32 ビット CR を用いた圧縮形式
- deflate: zlib 構造体と deflate 圧縮アルゴリズムを用いた圧縮形式
- br: Brotli アルゴリズムを用いた圧縮形式。gzip に近いが大容量の言語辞書を用いて、頻出するパターンの単語を圧縮して効率化。そのため文章的なテキストではgzip よりも圧縮率が高いと言われる
Brotliは比較的新しい形式で、ほとんどのサーバー、ブラウザで対応しています。
サーバーでの HTTP コンテンツの圧縮方法(gzip)
サーバーはクライアントのAccept-Encoding
リクエストヘッダを受け取り、その中から 1 つを選択して圧縮処理を行い、Content-Encoding
レスポンスヘッダを付加してクライアントに結果を知らせます。
CLINICS が利用しているそれぞれのアプリケーション・ミドルウェアに絞って、どのように HTTP コンテンツ圧縮を実現しているか解説したいと思います。いくつか圧縮形式はありますが、ここでは gzip 形式での圧縮方法について解説します。
NGINX
NGINX のngx_http_gzip_module
を利用することで gzip 圧縮することができます。
nginx.conf のgzip
ディレクティブをon
にすることで圧縮が有効になります。ただし、タイプを指定しないとContent-Type: text/html
のときにしか圧縮されません。他のタイプでも圧縮したいときはgzip_types
ディレクティブも合わせて指定する必要があります。gzip_types
に*
を指定することで、すべてのコンテンツを圧縮することもできます。
gzip: on;
gzip_types: text/css application/javascript application/json
また、CloudFront など Proxy を経由してのアクセスの場合はデフォルトでは行われません。Proxy 経由のアクセスかどうかは、リクエストヘッダにVia
ヘッダがあるかどうかで判定します。
CloudFront 経由でのアクセスの場合はVia: 1.1 xxxxx.cloudfront.net (CloudFront)
のようにVia
ヘッダが付加されているため、NGINX にて Proxy 経由であると判定します。Proxy 経由であっても何かしらの条件で圧縮したい場合はgzip_proxied
ディレクティブを指定する必要があります。
ref. https://nginx.org/en/docs/http/ngx_http_gzip_module.html
CloudFront
CloudFront の Behavior の設定にて設定します。Compress Objects Automatically を有効化することで、gzip 圧縮が有効になります。
上記を有効化すると、CloudFront では以下の条件で圧縮が行われます。
- ファイルサイズが 1,000(≒1KB) 〜 10,000,000(≒10MB) バイトの間
- よって、オリジンからファイルサイズを判定するための
Content-Length
ヘッダが付与されていない場合は、サイズ判別できないため圧縮されない
- よって、オリジンからファイルサイズを判定するための
- 特定の
Content-Type
のコンテンツを圧縮する- テキスト系のコンテンツは圧縮するが、画像や動画、PDF など、もともと圧縮されているものは対象外。詳しくはこちら
- オリジン側(NGINX や Rails など)から圧縮して返される場合は、再度圧縮は行わない
Content-Encoding
ヘッダの有無で判定している
ref. https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/ServingCompressedFiles.html
Rails
Rails はデフォルトでは HTTP コンテンツの圧縮は行いません。Rails でコンテンツ圧縮を行いたい場合は、Rails の Rack Middleware のRack::Deflaterを導入するのが簡単です。しかしながら、Rack::Deflater はすべてのContent-Type
のコンテンツでも圧縮するので、画像や動画・ PDF など圧縮するべきでないコンテンツまで圧縮してしまいます。NGINX や CloudFront など、Rails 外の他のサービスやミドルウェアに任せるのが良いと思います。
CLINICS での HTTP コンテンツ圧縮のシーケンス
前章で解説したアプリケーション・ミドルウェアは、CLINICS では以下のように連携しています。
AWS 上に Rails アプリケーションをデプロイしており、通常のアクセスはロードバランサーから NGINX を経由して Rails にアクセスし、静的ファイルなどキャッシュコンテンツは CloudFront 経由でアクセスしています。
CLINICS では用途に合わせた圧縮を行っています。3 つのケースを紹介します。
1. NGINX 経由で Rails にアクセスした時
API アクセスなどは上記シーケンスでアクセスしています。ほとんどがtext/html
やapplication/json
形式のコンテンツとなり、NGINX にて gzip 圧縮処理を行っています。Rails はアプリケーションの処理のみを行い、圧縮は行わないようにしています。
2. CloudFront 経由で S3 にアクセスした時
画像ファイルや PDF、静的な js、css ファイルなどはサービスのデプロイ時に S3 にアップロードしています。クライアントは CloudFront 経由でアクセスし、S3 から取得して、CloudFront で gzip に圧縮処理を行っています。また、一定期間 CloudFront 上にキャッシュされるので、効率よく圧縮コンテンツを返します。
3. CloudFront→NGINX→Rails 経由で S3 にアクセスした時
静的ファイルの中でもシグネチャをチェックしているものは、このフローでアクセスしています。NGINX でも圧縮設定を ON にしていますが、Via
ヘッダがあるため、NGINX では圧縮しないようになっています。
まとめ
HTTP コンテンツの圧縮を適切に行うことで、サービス全体のパフォーマンス向上が見込めます。更に CloudFront を活用することで、アプリケーションやミドルウェアでの圧縮処理をなくし、更なるパフォーマンス向上が見込めます。
今回は HTTP コンテンツの gzip 圧縮についてのみ触れましたが、Brotli 圧縮についても NGINX、CloudFront ともに可能なため、今後取り入れていきたいと考えています。もし HTTP コンテンツの圧縮設定を特に気にしたことがない方は一度確認してみてはいかがでしょうか?
最後に
メドレーでは、医療分野の社会課題を IT にて解決するために日々邁進しています。医療という分野においては、機微な情報を扱ったり診療を止めないようにするために、パフォーマンス・セキュリティ共に高いサービスレベルが求められます。興味を持った方がいらっしゃいましたら、まずは気軽に面談できればと思いますので、是非ご応募ください!
最後までお読みいただきありがとうございました。