なんでもノート

なんでも書くノートみたいなブログ

gzip圧縮について調べたまとめ

gzip圧縮について調べたのでまとめる。

概要

gzip - Wikipedia

gzipジー・ジップ)は、データ圧縮プログラムのひとつ、およびその圧縮データのフォーマットである。

圧縮を有効にする  |  PageSpeed Insights  |  Google Developers

最新のブラウザはすべて gzip 圧縮に対応しており、すべての HTTP リクエストで自動的に圧縮のネゴシエーションを実施します。gzip 圧縮を有効にすると、転送されるレスポンスのサイズが最大で 90% 削減されるため、リソースのダウンロード時間の大幅な短縮、クライアントのデータ使用量の削減、最初のページ レンダリング時間の改善といった効果があります

概要は上記の説明に任せる。

仕組み

gzip圧縮の恩恵を受けるためにはサーバーとクライアントの両方が圧縮アルゴリズムに対応している必要がある。

クライアントがgzip圧縮を要求する場合は、リクエストヘッダーで Accept-Encoding: gzip を送る。

Accept-Encoding - HTTP | MDN

そして、サーバーがレスポンスをgzip圧縮し、レスポンスヘッダーで Content-Encoding: gzip を返す。

Content-Encoding - HTTP | MDN

実装

サーバーサイド

Node.jsでサーバーサイドの実装をする場合、compressionというミドルウェアがよく使われる。

const compression = require('compression');
const express = require('express');
 
const app = express();
 
app.use(compression());

上記のように、app.use するだけでgzip圧縮されるので簡単。もちろん細かい設定もできる。

クライアント

https://github.com/sindresorhus/got#comparison によると、有名なHTTPクライアントライブラリはどれもgzip圧縮のハンドリングに対応している。

axios

axiosはブラウザからのリクエストにはXMLHttpRequestを利用している。XHRではAccept-Encodingをリクエストヘッダーに設定することが禁止されている。

https://fetch.spec.whatwg.org/#forbidden-header-name

axiosで以下のようなコードを書いた場合、

axios.get("/users", { headers: { "Accept-Encoding": "gzip" } })

ブラウザではコンソールにエラーが出力される。

Refused to set unsafe header "Accept-Encoding"

XHRでのAccept-Encodingヘッダーはユーザーエージェントに委ねられている。

よって、axiosでgzip圧縮されたレスポンスを受け取りたい場合は、Node.jsで動く箇所にだけ Accept-Encoding: gzip を設定する必要がある。また、decompressオプションをtrueにすることでレスポンスボディを解凍し、Content-Encodingヘッダーを削除する。Content-Encodingヘッダーを削除するのはaxiosで解凍したレスポンスをさらに解凍してしまうのを避けるためだと考えられる。

https://github.com/axios/axios/blob/b5a1a67b3c2b20f5d6e78e7e80297e71da4ab74c/lib/adapters/http.js#L218-L227

got

gotでgzip圧縮を利用したい場合はdecompressオプションにtrueを設定する。

https://github.com/sindresorhus/got/blob/main/documentation/2-options.md#decompress

decompressがtrueの場合はAccept-Encodingにgzipを設定している。

https://github.com/sindresorhus/got/blob/295979693bb179b32d11107367c57c31cb05bf78/source/core/index.ts#L1086-L1088

また、Content-Encodingがgzipの場合はレスポンスのボディをパースしている。

https://github.com/sindresorhus/got/blob/163ff0003736dbb35300b4cb13499b5177711c6d/source/as-promise/index.ts#L56-L64

superagent

https://github.com/visionmedia/superagent/blob/master/docs/index.md#compression

Nodeの場合は何もしなくても圧縮されたレスポンスをサポートしているとある。

HEADメソッドでない場合は、Accept-Encodingにgzipを設定している。

https://github.com/visionmedia/superagent/blob/3c9c0f7feef61328131ae43b06e628bce1c20c17/src/node/index.js#L792

Content-Encodingがgzipの場合はshouldUnzipでtrueが返り、unzipで解凍している。

https://github.com/visionmedia/superagent/blob/3c9c0f7feef61328131ae43b06e628bce1c20c17/src/node/index.js#L1254

https://github.com/visionmedia/superagent/blob/3c9c0f7feef61328131ae43b06e628bce1c20c17/src/node/index.js#L1037-L1039

雑感

  • gzip圧縮の仕組みと実装方法を調べた
  • サーバーサイドの実装方法は知っていたので、クライアント側の実装方法について重点的に調べた
  • 3つのライブラリを調べたが、それぞれgzip圧縮の対応方法が異なり面白かった
    • axiosは利用者がAccept-Encodingを渡す必要があり、decompressオプションで解凍するかどうかを制御している
    • gotはdecompressオプションでAccept-Encodingの設定と解凍の両方を制御している(headersでAccept-Encodingを渡すこともできそう)
    • superagentはデフォルトでAccept-Encodingの設定と解凍をサポートしている