header()の前には出力はできない(はず)
phpのマニュアルのheader()関数の説明に<html>とあります。たしかにおぼろげに
<?php
/* これはエラーとなります。この上に出力があることに注目してください。
* それはheader()のコールより前であるということになります */
header('Location: http://www.example.com/');
?>
Cannot modify header information - headers already sent by ...
こんなワーニングを見た記憶があります。
このワーニングを再現しようと思い上記のコードをテストサーバに配置しアクセスしてみたのですが、ワーニングも出ずにきちんとリダイレクトされてしまいました。なぜ!? 納得がいくまで調べていたら数日が経過してしまいました…
調査
サーバからのレスポンスをヘッダを含めて確認したかったので、いろいろ手段はありますが今回は Burp Suite を使いました。レスポンスはこのようになっていました。
HTTP/1.1 302 Found Date: Mon, 29 Aug 2011 **:**:** GMT Server: Apache/2.2.16 (Ubuntu) X-Powered-By: PHP/5.3.3-1ubuntu9.5 Location: http://www.example.com/ Vary: Accept-Encoding Content-Length: 7 Content-Type: text/html <html><html>を先に出力しているのに、きちんとheader()関数が有効で、Location:ヘッダが出力されていました。これは明らかに、
出力バッファリング
ですよね。でも、出力制御関数は一切使っていないのに…となると、答えはもう、php.iniしかありません。テストした環境(ubuntu server 10.10, php 5.3.3)のphp.iniを見てみると…
output_buffering = 4096見つけました。この設定があるおかげで、header()の前に出力があってもheader()が有効でした。ためしに、
<html> <?php echo str_repeat(" ", 4096); header('Location: http://www.example.com/'); ?>このように余計な文字列を出力し Content-Length: が4096以上になると、その時点でバッファに溜め込んだ出力がヘッダとともに出力されてしまうので、次の header("Location:"... でワーニングが発生し、リダイレクトもされません。
以上をふまえて注意
header()を使うときは- header()とコンテンツの出力の順序に気をつける。これはMVCの分離ができていれば容易にできるはずです。
- output_bufferingの設定を明示的に行う。php.ini以外に.htaccessやhttpd.confでも可能です。