HTTP通信とPHPのスーパーグローバル変数の関係 【ブラウザがPHP内のheader()関数のLocationヘッダを受けるとリダイレクト処理する】

Apache設定ファイルにディレクティブの値としてHTTP環境変数を設定

HTTP通信はクライアントからのリクエストに対してApacheサーバーがレスポンスを返すことで成立します。

HTTPサーバーとしての最大の機能はバーチャルホストであり、物理ディレクトリを仮想サーバーのDocumentRootに指定してURLを割り当てることで、1つのApacheサーバーで複数のドメインや複数のサブドメインの管理することができます。

Apacheサーバーのチューニングをするといういことは、Apache設定ファイルであるhttpd.confに、Apacheディレクティブと値(ON/OFF, 数字, HTTP環境変数, URLなど)を設定することであり、これを親子関係にある.htaccessに設定して、ディレクトリ単位に配置することもできます。

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://bahtera.jp/$1 [R=301,L]
</IfModule>

.htaccessに、Apacheのrewrite_modeモジュールが実装するディレクティブ(コマンド・命令)を記述し、任意のディレクトリに配置することによってhttpd.confによる設定を上書きします。

例えばRewriteCondというディレクティブは、%{xxxx}形式で環境変数やリクエストヘッダの情報を参照するので、%{HTTPS}であれば、サーバー変数であるHTTPSを参照し、SSL通信がOFFであるかどうかを判断しています。

ApacheのHTTP環境変数はサーバー変数とも呼ばれ、変数ごとに固有の情報を記憶する仕組みを実装しており、この情報はログ収集やアクセス制御に使われます。

なおサーバー変数はWindowsの環境変数とは別物です。

ディレクティブの適用範囲を限定して設定するにはコンテナタグ「<>」の中で<ディレクティブ 値>の形式で記述します。

  1. ディレクトリごとの設定は<Directory “D:/xammp/htdocs”>
  2. ファイルごとの設定は<Files “.ht*”>
  3. URLごとの設定は<Location>
  4. 仮想ホストごとの設定は<ViurtualHost>

.htaccessを有効にするためにhttpd.conf内のAllowOverrideをAllにする必要があります。

<Directory "D:/xammp/htdocs">
AllowOverride All
</Directory>

ちなみにApacheサーバーのディレクトリ単位にBasic認証をかけるための.htaccessファイルにはApacheのディレクティブを記載します。

AuthUserFile /home/users/1/○○○○○/web/△△△△△/admin/.htpasswd
AuthGroupFile /dev/null
AuthName "Input your ID and Password"
AuthType Basic
require valid-user



register_globalディレクティブの機能


WordPress起動時に読み込まれるPHPファイルを追っていくためには、php.ini、httpd.conf、.htaccessファイル、PHPスーパーグローバル変数、サーバー変数、HTTP通信など、WEB通信で登場するキーワードの相関関係を理解する必要があります。

WordPressを起動する際の初期設定モジュールであるwp-settings.phpから、load.phpのwp_unregister_GLOBALS()関数を呼び出して、php.iniのregister_globalディレクティブをOFFにします。

スーパーグローバル変数$_GETや$_POSTが取得するデータは

  • $変数1=$_GET[‘変数1’];
  • $変数2=$_POST[‘変数2’];

のように初期化(代入)することにで、コード中の$変数1と$変数2が利用できるようになりますが、これがregister_globalディレクティブがONのままだと、上記の代入なしで$変数1と$変数2が、デフォルトでグローバル変数として値を持ってしまいます。

またサーバーから返信されるレスポンスヘッダーに含まれる環境変数から、現在実行されているPHPスクリプトのパスを参照するとき、スーパーグローバル変数で$_SERVER[“PHP_SELF”]としなくても、$PHP_SELFだけで参照できます。

このようにGET、POST、環境変数などPHPの外部からくる値が$変数名というフォーマットで使用できてしまうと、仮にURLのクエリストリングで「index.php?wp_did_header=dummy」とかやられたら、コード上で$wp_did_header変数にdummyという値が格納されてしまい、WordPressが起動時にwp-blog-header.phpでエラーを起こす可能性があります。

スーパーグローバル変数が勝手にグローバル変数に展開されると手間が省ける反面、セキュリティ上よろしくないということで、register_globalディレクティブのデフォルトがPHP5.4になってからOFFになりました。

register_globalディレクティブと$GLOBALSの関係

$GLOBALSはグローバルスコープで定義済みのすべての変数への参照を持つ特殊なスーパーグローバル変数です。

<?php
function test() {
    $foo = "ローカル変数";

    echo '$foo in global scope: ' . $GLOBALS["foo"] . "\n";
    echo '$foo in current scope: ' . $foo . "\n";
}

$foo = "グローバル変数";
test();
?>

グローバルスコープの変数の値が、ローカルスコープで$GLOBALSによって参照できています。

$foo in global scope: グローバル変数
$foo in current scope: ローカル変数

仮にregister_globalディレクティブがONの場合、HTTP通信でクライアントからリクエストが送信されるたびに、連想配列であるスーパーグローバル変数$GLOBALSに対して、[キー=>値]として自動的に値がセットされます。

HTTP通信とスーパーグローバル変数の関係

WEBシステムはクライアント(ブラウザ)からのリクエストとサーバー(PHP)からのレスポンスをHTTP通信で行なうことを前提に構築されます。

開発したHTMLもCSSもJavaScriptもPHPも、すべてサーバー上に置かれ、クライアントが送信したリクエストに基づいて、サーバーはPHPで処理を行い、結果をHTML形式でクライアントに送信し、ブラウザが描画(rendering)することでWEBサイトが見られる仕組みです

このHTTP通信の際に、PHPではクライアントからのリクエストヘッダは$_SERVERで、フォームに入力したリクエスト本体は$_GETや$_POSTで参照でき、スーパーグローバル変数としてPHPのコード中で使用します。

リクエスト本体は$_GETや$_POSTで取得できるものが$_REQUESTでも取得できますが、キーが同じだと上書きされてしまうので使いません。

これはコンタクトフォームページへのアクセス時のヘッダ情報ですが、リンクからアクセスする場合でもアドレスバーに直接URLを打ち込む場合でも、指定したコンテンツを取得するためのリクエストGET通信で行なわれます。

(1) HTTPメソッド
GET /contact/ HTTP/1.1 ・・・通信メソッドとパスとプロトコル

(2) リクエストヘッダ $_SERVERで参照
Accept: text/html, application/xhtml+xml, */*
Referer: https://bahtera.jp/profile/  ・・・リンク元のURL
Accept-Language: en-US  ・・・ブラウザ対応言語
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko  ・・・ブラウザの種類
Accept-Encoding: gzip, deflate
Host: bahtera.jp
Connection: Keep-Alive
Cookie: _ga=GA1.2.836530435.1451540923; _gat=1

そしてサーバーから、処理結果としてヘッダー情報がレスポンスされます。

(1) HTTPステータス
HTTP/1.1 200 OK ・・・プロトコルとステータスコードとステータスメッセージ

(2) レスポンスヘッダ $_SERVERで参照
Date: Thu, 31 Dec 2015 05:49:40 GMT
Server: Apache
X-Powered-By: PHP/5.2.17
X-Pingback: https://bahtera.jp/xmlrpc.php
Link: <https://bahtera.jp/wp-json/>; rel="https://api.w.org/", <https://bahtera.jp/?p=7515>; rel=shortlink
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 8074
Content-Type: text/html; charset=UTF-8 ・・・コンテンツの種類
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive

コンタクトフォームからメッセージを送信する際はPOST通信で行なわれます。

(1) HTTPメソッド
POST /contact/ HTTP/1.1 ・・・通信メソッドとパスとプロトコル

(2) リクエストヘッダ $_SERVERで参照
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: application/x-www-form-urlencoded; charset=UTF-8 ・・・コンテンツのタイプ
X-Requested-With: XMLHttpRequest
Referer: https://bahtera.jp/contact/ ・・・リンク元のURL
Accept-Language: en-US ・・・ブラウザ対応言語
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko ・・・ブラウザの種類
Host: bahtera.jp
Content-Length: 282
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: _ga=GA1.2.836530435.1451540923; _gat=1

_wpcf7=6942&_wpcf7_version=4.3.1&_wpcf7_locale=&_wpcf7_unit_tag=wpcf7-f6942-p7515-o1&_wpnonce=231d554ea0&your-name=%E5%B1%B1%E6%9C%AC%E3%80%80%E5%95%93%E4%BA%8C&your-email=pokecart%40gmail.com&your-message=%E3%83%86%E3%82%B9%E3%83%88%E3%81%A7%E3%81%99%E3%80%82&_wpcf7_is_ajax_call=1

そしてサーバーから、処理結果のヘッダーがレスポンスされます。

(1) HTTPステータス
HTTP/1.1 200 OK ・・・プロトコルとステータスコードとステータスメッセージ

(2) レスポンスヘッダ $_SERVERで参照
Date: Thu, 31 Dec 2015 05:57:58 GMT
Server: Apache
X-Powered-By: PHP/5.2.17
Content-Type: application/json; charset=UTF-8 ・・・コンテンツの種類
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked

レスポンスヘッダにPHPコードからヘッダ情報を追加

PHPコード中から、header()関数にて、レスポンスヘッダにLocationヘッダを追加してクライアントに送信すると、それを受けたブラウザは他サイトにリダイレクトします。

//Locationヘッダにリダイレクト先のURLをセット
header('Location:https://bahtera.jp/blog/');

PHPコード中から、header()関数にて、HTTPステータス401を追加してクライアントに送信すると、それを受けたブラウザは認証ダイアログを表示します。

//HTTPステータスに401をセットして認証ダイアログを表示
header('HTTP/1.0 401 Unauthorized');
//WWW-Authenticationヘッダに
header('WWW-Authentication: Basic realm="INDONESIA-JAPAN"');

それ以外にもステータスコードは301(恒久的移動)、401(HTTP認証要求)、404(Not Found)など、目的に応じてヘッダ情報のHTTPステータスとしてクライアントに送信されます。

サーバー変数とApacheディレクティブの関係

連想配列であるスーパーグローバル変数$_SERVERで参照できるリクエストヘッダとレスポンスヘッダのプレフィックスはHTTP_です。

$_SERVER['HTTP_REFERER']; //リンク元のURL(リクエストヘッダ)
$_SERVER['HTTPS']; //SSL通信によるアクセスかどうか(サーバー変数)

それ以外にプレフィックスがHTTP_でないサーバー変数が参照でき、もしphp.ini上のregister_globalディレクティブがONであれば$HTTPSだけで参照できます。

サーバ変数とはサーバが生成する変数であり、ヘッダやパス・スクリプトの位置などにアクセスできる変数です。

.htaccessはApacheで使用されるディレクトリ単位に、Webサーバの動作を制御するために置かれるApacheのディレクティブを記述したファイルであり、ディレクトリにベーシック認証をかけたり、mod_rewriteモジュールが実装するディレクティブでhttpd.confの設定を上書きすることができます。

AuthUserFile /home/users/1/○○○○○/web/△△△△△/admin/.htpasswd
AuthGroupFile /dev/null
AuthName "Input your ID and Password"
AuthType Basic
require valid-user

Apacheのrewrite_modeモジュールが実装するディレクティブで.htaccessによってhttpd.confを上書きしますが、ここでサーバー変数であるHTTPSを見て、SSL通信の有無を判断しています。

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://bahtera.jp/$1 [R=301,L]
</IfModule>

サイトを常時SSL化した後のリダイレクト処理時にHTTPステータスコードを302(一時的な移動)にしてしまうと、クローラーがリダイレクト先をインデックス化してくれませんので、SEOの観点から必ず301(恒久的な移動)にする必要があります。