昨日ブログを書いたのですが、その登場人物S氏から「俺の携帯だとブログ見られない」という連絡を頂きました。どうも400 Bad Request
が出ていたようです。
状況をまとめると……
- 去年までは見られていた
- 端末は DocomoのMEDIAS ES N-05D
- OSはAndroidの2.3系(バージョンアップはしていない様子)
- FacebookやTwitterでソーシャルデバッギングをしかけたところ、他のAndroid2.3のユーザーは見られているとのこと
というわけで調べてみました。S氏には「とりあえずドコモショップに言ってOSとファームウェアをアップデートしてもらって」と伝えたのですが、ドコモ店員に向かって「このサイトが見られない」というクレームを入れたようで、なんか僕のところに電話かかってきました……。
家族を人質にでも取られない限り、ドコモショップの店員はやるまいと思いましたね。
400 Bad Requestとは
直訳すると「悪い要求」なのでクライアント(見る側)の問題ではあるようです。ググると「クッキー消せ」という情報が出てきますね。
まあ、「クッキー消して」と頼んでなんとかなる規模ならばいいのですが、沢山のユーザーを抱えるサイトで同じことが起きたらやだなと思ったので、ちょっと調べてみました。
で、色々ググったところ、 Dealing with Nginx 400 Bad Request HTTP errors というブログ記事を発見。ここによると、400 Bad request
はCookieが大き過ぎるとなるよと書いてありました。
で、最後にはNginxのドキュメントに行き当たりました。large_client_header_buffers
の項目にこんなことが書いてあります。
このディレクティブはクライアントからのリクエストに含まれる大きなヘッダーを読みとるためのバッファーの数およびサイズを割り当てます。
リクエストの行は1つのバッファーのサイズより小さくないといけません。もしクライアントがそれよりも大きいヘッダーを送信した場合、nginxは ”Request URI too large(414)” を返します。
また、ヘッダーの一番長い行も1つのバッファーのサイズを超えてはいけません。超えた場合、クライアントは ”Bad request(400)”を受け取ります。
バッファーは必要に応じて分割されます。
初期値では1つのバッファーのサイズは8192バイトです。古いnginxではこの値は1ページあたりの容量と同じで、プラットフォームによって4kまたは8kとなり、動作中のリクエスト接続がkeep-aliveに移行するとバッファーは解放されます。
最後の文はちょっと翻訳怪しいですが、要するにここに設定された値を超えたヘッダーを受け取るとエラーになるよということですね。
CookieはHTTP Request Headerであり、RFCによれば(参考: Internet Explorer の cookie の数とサイズの制限)1行が4kb程度なはずなのですが、ブラウザによってはこれを超えるクッキーを許容するようです。
となると、少なくともCookieを含められる値を large_client_header_buffers
に設定すれば動くんじゃないかということになりますね。したがって、これを次のように設定します。
http{ # httpディレクティブに書きます large_client_header_buffers 4 256k; }
S氏に確認しながらやった結果、最終的に256kまで増加しないと見られるようになりませんでした。そんなにCookieに保存することあるの……
結局のところ、なにによってCookieが増加しているのかはわからなかったのですが、Androidで400 Bad Request
が出ることは多いみたいですね。Google Adsenseとか、Google Analyticsとか、そういうのが原因かもしれません。
ちなみに、デバッグ中にS氏はドコモショップに行ってOSを4.xに上げてもらったようですが、それでは解決しませんでした。
今回のことから得た教訓は……
- 端末によって起きたり起きなかったりすると、デバッグ大変。
- Androidはマジで辛い
おまけ: client_body_buffer_sizeも設定した
今回の件でログをあさったところ、大量のエラーが出ていました。
an upstream response is buffered to a temporary file /var/cache/nginx/fastcgi_temp/1/02/0000000021 while reading upstream
PHP-FPMと通信するときにバッファに乗り切らないとファイルI/Oが発生するたみたいです。よって、client_body_buffer_size
を大きめに設定。どうも、ページサイズの倍ぐらいないとダメっぽかったので、次のようにしました。
http{ client_body_buffer_size 512k; }
ここら辺はわりと適当なので、参考リンクなども熟読してください。終わり。
価格¥505
順位132,909位
著Dimitri Aivaliotis
翻訳高橋 基信
発行オライリージャパン
発売日2013 年 10 月 26 日
今これ読んでる。