はじめに
Railsのアップグレード後にAPIが突然破壊されました。
調査してみると CloudFront が「それまで圧縮していなかったレスポンスを gzip 圧縮して返すようになった」ということが発覚しました。
CloudFrontが圧縮する条件
CloudFront では、次の条件で自動圧縮されます1。
- リクエストに
Accept-Encoding: gzip(またはbr)が含まれている - オリジンの応答が未圧縮である(
Content-Encodingヘッダが付いていない) - Content-Type が
application/jsonやtext/htmlなど圧縮対象の型である - ステータスコードが
200・403・404のいずれかで、ボディが空でない
かてて加えて、 Content-Length が1,000〜10,000,000 バイトだと自動圧縮されます。1
RailsのアップデートでContent-Lengthが付与
Rails のアプリケーションサーバ puma は、アプリが Content-Length を指定していない場合に、ボディから計算して付与されるようになりました2。
このアップデートを反映させると、アプリのコードは全く変わっていないのにレスポンスヘッダーに Content-Length が自動で付与されます。すると、CloudFront が1,000バイト以上のレスポンスを自動で圧縮しはじめます。
クライアントの処理
HTTP クライアントは Accept-Encoding: gzip を指定して送ることがあります。しかし、これを送っておきながら返ってきた gzip データを展開する処理がないと、生の gzip バイト列(先頭が 0x1f 0x8b)がそのまま JSON パーサに渡り、パースエラーで失敗します。
まとめ
- CloudFrontは Content-Length が無いレスポンスは圧縮しない
- Content-Length はサーバやミドルウェアが決めるため、アプリコードを変えなくてもアップグレードで圧縮されうる
- 受け取る側が
Accept-Encoding: gzipを送るなら、Content-Encodingを見て展開する前提で設計する
参考文献
-
圧縮ファイルを供給する - Amazon CloudFront。圧縮の条件(Accept-Encoding、Content-Encoding、Content-Type、Content-Length、サイズ 1,000〜10,000,000 バイト、ステータス 200/403/404)。引用は「圧縮の条件」節より。参照日 2026-07-13 ↩ ↩2
-
puma/puma#2896: Response refactor, increase requests-per-second, body type handling。"An Array - Puma will determine the content-length if not provided"。puma 6.0.0(2022-09-13 マージ)に含まれる。参照日 2026-07-13 ↩