📶

cURL コマンドで、gzip や br など圧縮された応答を解凍の上出力する

2025/01/24に公開

ことの発端

先日、とあるマイナーな JavaScript ライブラリを、非インターネット接続環境でも利用する都合が発生しました。そこで、その JavaScript ライブラリファイルをいったん自分の PC にダウンロードすることになりました。それまでは、その JavaScript ライブラリの配布サイトが Web ページからの直接参照を許していたことからそのように利用しており、ローカルにファイルとして保存したことはなかったのです。

ということで、ターミナルを開き、以下のような感じで cURL コマンドを実行しました。

curl {JavaScript 配布 URL} -o foo.js

これでカレントディレクトリに foo.js が保存されました。早速、内容を確認してみます。

cat ./foo.js

すると、どういうわけか、JavaScript コードのテキストが表示される代わりに、文字化けしたような出力が現れました。

保存された foo.js の内容を、cat コマンドで標準出力に表示したら、JavaScript コードのテキストではなく、なにやらバイナリ形式っぽい文字化けしたような出力が表示されたところ

最初は配布サイトが障害でも起こして、正しいコンテンツを返さなくなったのかと思いました。

しかしよくよく調べると、HTTP 応答の Content-Encoding ヘッダに gzip が設定されていることがわかりました。つまり、保存した foo.js は、GZip 圧縮された状態のファイルだったのです。試しに以下のように gzip コマンドを使って標準出力に解凍してみると、期待した JavaScript コードのテキストがターミナルに表示されました。

gzip -dc foo.js

cURL コマンドだけで解凍してみる

圧縮されていた、ということがわかってしまえば、あとは gzip コマンドなり brotli コマンドなりで、cURL コマンドで入手したファイルを解凍すればよいです。しかし実は、cURL コマンド自身で、受信したコンテンツを解凍した状態で出力できます。そのためには、 --compressed オプションを 指定します。

curl --compressed {JavaScript 配布 URL} -o foo.js

こうすることで、gzip 圧縮が解かれた状態で foo.js ファイルが保存されます。

--compressed オプションの本来の意味

なお、--compressed オプションは圧縮を解くことそれ自体はが本来の目的ではないようです。--compressed オプションを付けて cURL コマンドを実行すると、送信する要求ヘッダに Accept-Encoding: deflate, gzip, br, zstd が付け足されるようになります。つまり Web サーバーに対して「圧縮した状態でコンテンツを送ってくれてもいいよ」と意思表示するわけですね。その結果、Web サーバーからは圧縮された状態のコンテンツが応答として送信されてくる場合がありえます。cURL コマンドは「圧縮されてても構わない」と意思表示した手前、実際に受信したコンテンツが圧縮状態だったらその圧縮を解けないとならないわけで、実際、そのように動作します。言い換えると、--compressed オプションを付けることで、通信経路上は圧縮されたデータが流れることで、通信量や時間を削減できますよ、っていう機能らしいのです。

それが今回は、Accept-Encoding 要求ヘッダを指定していないにもかかわらず、問答無用で GZip 圧縮した状態でコンテンツを返す Web サイトが相手だったわけで、その場合にも、cURL コマンドに --compressed オプションを付けることで対処できますね、という話でした。

近代の Web ブラウザはだいたい常に Accept-Encoding 要求ヘッダを付けて HTTP 要求を送信するでしょうし、もちろん、コンテンツが圧縮されていれば展開して使用することでしょう。そのため、Web ページ内の <script> タグから直接その配布サイト上の JavaScript ファイルを参照するぶんには、支障を期していなかったのでしょう。

Discussion