22973


サイトに設置しているPHPにアクセスされた際に、アクセス解析用のPHPを非同期で実行させするのに、GET/POSTで引数を渡せるfsockopenを利用しています。

これを下記のよう実装するとapache+phpの環境だと問題なく動作しますが、nginx+php-fpmの環境だと動作しません。  
その時のnginxのアクセスログを見ると、解析用のPHPに499のステータスコードが表示されています。また、phpのエラーログには何も表示されません。  
（apache+phpの環境では200のステータスコードで、解析用PHPは実行されます。）

対応方法や問題点などありましたら、ありますでしょうか？

[code]

    $host = "192.168.18.10"; // 解析用のサーバ
    $port = "80";
    $script = "analytics.php";　// 解析用のPHP
    $param = "aflag=on";
    $url  = "http://".$host."/".$script."?".$param;
    $fp = @fsockopen( $host , $port , $errno , $errstr ,30);
    
    if( !$fp )
            { return false; }
    
    $string  = 'GET /'.$script."?".$param . ' HTTP/1.1' . "\r\n";
    $string .= 'HOST: ' . $host . "\r\n";
    $string .= 'User-Agent: PHP/' . phpversion() . "\r\n";
    $string .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n";
    $string .= 'Content-Length: ' . strlen( $param ) . "\r\n";
    $string .= 'Connection: Close' . "\r\n";
    $string .= "\r\n";
    $string .= $param . "\r\n";
    fwrite( $fp , $string );
    fclose( $fp );
    
[/code]


CC BY-SA 3.0
2016-03-10T04:52:58.617
22970
2016-03-10T05:49:35.433
2016-03-10T05:49:35.433
8000
9128
question
php nginx
nginx+php-fpmにfsockopenでリクエストを送信すると、Nginxのログにステータスコード499が記録される


Nginxがログに記録する499というステータスコードは、Nginxからクライアントにレスポンスを送信し始める前にクライアントから切断されたことを示します。Apacheにこのような仕様はありません。

  * [nginxのログに499(レスポンスコード)が記録される場合 - hiroi10の日記](http://d.hatena.ne.jp/hiroi10/20130306/1362591114)
  * [Re: 499 error - Nginx Mailing List](https://forum.nginx.org/read.php?2,219411,219421)

今回の場合、あなたは `fwrite()` でリクエストを送信した後、レスポンスを待つことなく `fclose()`
でコネクションを閉じているため、この仕様に引っかかっているものと思われます。ですから、

  * 上記MLの回答にあるように、Nginx側で `proxy_ignore_client_abort` もしくは `fastcgi_ignore_client_abort` オプションを指定して、このチェックを行わないようにする
  * きちんとレスポンスを受信してからコネクションを切断するようにする

のどちらかになるかと思います。非同期ということなら後者は難しいでしょうか。

* * *

なお相手のPHPの処理を待つことはないものの、 `fsockopen()` \+ `fwrite()`
自体は同期的に実行されるため、Nginxに問題があると時間がかかる可能性があります。例えば[`fsockopen()`](http://php.net/manual/ja/function.fsockopen.php)の第5引数に30を指定されているので、接続できるまで最大30秒待つことになります。

この辺も含めて非同期にしたいのなら、curlやwget、あるいは別のPHPスクリプトを別プロセスで実行するといったハックが必要になります。あるいは30秒もかかっている時点でおかしいと考えて、タイムアウトをもっと短くするというのもありかと思います。

[http - How do I make an asynchronous GET request in PHP? - Stack
Overflow](https://stackoverflow.com/a/963019/2818869)

また、質問に書かれたコードでは次のようなリクエストが送信されますが・・・

[code]

    GET /analytics?aflag=on HTTP/1.1
    HOST: 192.168.xx.xx
    User-Agent: PHP/5.4
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 8
    Connection: Close
    
    aflag=on
    
[/code]

GETリクエストではbody部は使いません。ですから、以下のようなリクエストで十分です。

[code]

    GET /analytics?aflag=on HTTP/1.1
    HOST: 192.168.xx.xx
    User-Agent: PHP/5.4
    Connection: Close
    
[/code]


CC BY-SA 3.0
2016-03-10T05:40:12.267
22973
2016-03-10T05:45:55.807
2017-05-23T12:38:56.083
-1
8000
22970
answer