net::ERR_INCOMPLETE_CHUNKED_ENCODING with Nginx
起こったこと
Rails/Unicorn/Nginxという構成で動いているアプリケーションを使っていたら、net::ERR_INCOMPLETE_CHUNKED_ENCODING
というエラーが発生し、32KB以上のページが途中で途切れるようになりました。
解決策 その1
Railsのログを追ってみたところ特に異常なし。Unicornも特に異常はなく、試しにNginxを介さずにUnicornに直接アクセスするとページが切れずに表示されました。したがって、犯人はNginx。 そこで、いろいろとググってみたところ、こんなものを発見。
git - Enabling nginx Chunked Transfer Encoding - Server Fault
というわけで、Nginxの設定ファイルに追記します。*1
location / { proxy_buffering off; }
細かいことを気にしないなら、これでおしまいです。
proxy_bufferingディレクティブとは何か
公式ドキュメントによると、
- Syntax: proxy_buffering on | off;
- Default: proxy_buffering on;
- Context: http, server, location
- Enables or disables buffering of responses from the proxied server.
簡単に言うと、
ONの場合
Nginxはプロキシ先のサーバからのレスポンスをメモリ上のバッファに溜め込みます。なお、バッファのサイズがproxy_buffer_sizeディレクティブ及びproxy_buffers directivesディレクティブで定義されているサイズより大きくなった場合にはディスク上の一時ファイルに書き出します。
OFFの場合
Nginxはプロキシ先のサーバからのレスポンスをクライアントに即時に送り返します。Nginxがプロキシ先のサーバから一度に受け取れるデータの大きさはproxy_buffer_sizeによて定義されます。
もう一歩踏み込む
ここで、proxy_buffersディレクティブについてもう少し踏み込んでみます。
同じく公式ドキュメントによると、
- Syntax: proxy_buffers number size;
- Default: proxy_buffers 8 4k|8k;
- Context: http, server, location
proxy_buffersディレクティブはプロキシバッファの数と大きさを定義します。そして、バッファ数のデフォルト値が8、バッファサイズのデフォルト値がプラットフォームのページサイズです。 今回不具合が起こったアプリケーションはAmazon Linuxで稼働していたため、ページサイズは
$ getconf PAGE_SIZE
4096
ということで、4KBでした。つまり、
4KB × 8 = 32KB
ということで、プロキシ用に32KBのバッファが用意されていることになります。
32KBしかブラウザに落ちてこない理由の一端はこのあたりにありそうです。
なぜ一時ファイルが使われなかったのか
しかし、先ほどproxy_bufferingディレクティブのところで説明したとおり、Nginxはバッファ用メモリが足りなくなると、ディスク上に一時ファイルを作成してそこにバッファリングします。まあ、当然の仕様だと思います。
では、なぜ一時ファイルが使用されなかったのか。答えは簡単です。
「Nginxが一時ファイルを置くディレクトリのパーミッションを持っていなかったから」
はい、死ぬべきですね。すみません。
解決策 その2
というわけで、より良い解決策は
- Nginxの作業用ディレクトリに適切なパーミッションを設定する
- 必要に応じてバッファメモリのサイズを大きくする
ということになると思います。つまり、Amazon Linuxの場合だと、
$ sudo chown -R nginx:nginx /var/lib/nginx
location / { proxy_buffers 64 4k; }
という感じですかね。
*1:locationの下じゃなく、serverやhttpの下でもOKです。