GUiLZ Project Personal & Experimental Blog
このエントリーをはてなブックマークに追加
はてなブックマーク - Google App Engineで使える、CloudflareのSNI SSLを判定して振り分けるPHPスクリプトを書いた
Facebook にシェア
[`evernote` not found]

 

1年以上放置した末の更新というか、生存証明的なエントリーというか、まあ死にそうになりながらも生きてる俺・輝いてる☆ 的な話というか、結論としては、生きててすみません。(積極的に生存の許しを請うていくスタイル)

1年以上更新していないと、書き出しのスタイルすら迷走し混迷を深めていくものだなあと他人事のように語りつつ、暁の軌跡 引退記念としてこのエントリーを奉じる次第……って前置きはこのあたりにしておいて。

だいたいタイトルだけで説明になってますが、Google App Engine では .htaccess が使えないため、mod_rewrite を使っての SNI SSL 非対応ブラウザの振り分けができない。よってスクリプト書いて代替したよーという話。

1年ぶりの更新なので、このくらいのネタがリハビリとしてはいいんじゃないかなということで、以下続きにて。

スポンサードリンク

 

はじめに

これまで、SNI SSL非対応環境を振り分ける .htaccess については、SNI SSL(HTTPS)を使いたいけど Android 2.X とか古いブラウザもあるから SNI 対応ブラウザだけ SSL 接続させる正規表現 様のページを参考に振り分けしていたのだけど、その自前サイトはたいした規模でもないので、Google App Engine で動作させればサーバー維持費が無料になるんじゃね? と思い立って作業開始したものの、Google App Engine は Apache 環境じゃないので .htaccess での振り分けが使えない。

じゃあせっかくだしPHP経由で振り分けすればいけるかな、と思って試したら上手くいったので、そのスクリプトとか app.yaml の設定などをメモ的に残す感じで。

ただ、振り分けにPHPを使う=サイトへのアクセス毎にインスタンスが起動するという事でもあるので、静的HTMLベースのサイトでも、調整によってはインスタンスが無料範囲に収まらない可能性もあるため、その点だけはご注意を。

 

SNI SSL非対応環境の振り分けスクリプト

PHPのコードは下記の通り。一部環境の判定に不要なものもあるけれど、App Engine 以外の環境でも使えるように保険として。App Engineだけでしか使わないなら、ログを見て不要な判定部分は削ってもいいかもしれない。

処理としては、初めから https つきでアクセスされたら、SNI非対応の場合はその時点でエラーやら警告が出るので、SNI非対応かどうかは判定せずに https にジャンプさせている。http でアクセスされた場合のみ、User Agent を確認して振り分ける。処理の速さを優先して strpos で判定しているので冗長だけど、この程度の規模なら正規表現で判定させてもたいして速度や負荷は変わらなかったかも。

ファイル名 : snicheck.php

<?php

# ヘッダー情報を取得
$http_host = $_SERVER['HTTP_HOST'];
$http_cf_visitor = $_SERVER['HTTP_CF_VISITOR'];
$cf_visitor = $_SERVER['CF_VISITOR'];
$x_forwarded_proto = $_SERVER['HTTP:X-Forwarded-Proto'];
$https = $_SERVER['HTTPS'];
$user_agent = $_SERVER['HTTP_USER_AGENT'];

# ジャンプ先 URL
$nosnijump = 'http://www.example.com/index.html';
$snijump = 'https://www.example.com/index.html';


# httpsで接続してきているかの確認

# https で接続された場合、htmlにリダイレクトする
if($x_forwarded_proto === 'https' || $https === 'on' || strpos($http_cf_visitor, 'https') !== false || strpos($cf_visitor, 'https') !== false) {

	header("Location: ".$snijump);


# https 接続ではない場合の処理
} else {

	# SNI非対応かチェック
	if(strpos($user_agent, 'MSIE 4') !== false || strpos($user_agent, 'MSIE 5') !== false || strpos($user_agent, 'MSIE 6') !== false || strpos($user_agent, 'MSIE 7') !== false || strpos($user_agent, 'MSIE 8') !== false || strpos($user_agent, 'Windows NT 5') !== false || strpos($user_agent, 'UP.Browser') !== false || strpos($user_agent, 'DoCoMo/') !== false || strpos($user_agent, 'SoftBank/') !== false || strpos($user_agent, 'Android 1') !== false || strpos($user_agent, 'Android 2') !== false) {

		# SNI非対応ブラウザは http にジャンプ
		header("Location: ".$nosnijump);

	# SNI 対応ブラウザの処理
	} else {

		# SNI対応ブラウザは https にジャンプ
		header("Location: ".$snijump);

	}

}

?>

 

Google App Engine で判定させる app.yaml

この例では、インデックスファイルとして上記 snicheck.php を読み込ませるように設定している。スクリプト内で index.html にリダイレクトさせているので、その設定も handlers に追加している。

そのため、直接 index.html を指定してアクセスされると無意味になってしまうけど、自分の場合は特にそれでも支障ないのでこのやり方にした。

handlers:
- url: /
  script: snicheck.php

- url: /index.html
  static_files: index.html
  upload: index.html

 

あとがき

最近はSNI非対応環境も(Windows XP のサポート終了に伴って) 少なくなってきているだろうし、Android 2.x 環境も同様だとは思うけど、一部フィーチャーフォンに対応しないといけない場合などにはまだ使う事もあるかなと。

Google App Engine 前提で書いたスクリプトだけど、改造すれば Apache 以外の環境で条件判定するのに使えるかもしれないので、もしお役に立てれば幸い。

 

おまけ

別のサイトで、IE 4~8 環境を判定して別ページに飛ばす必要があったので、そのスクリプトもおまけとして公開。もうサポートも終了しているので切り捨ててもいいんだろうけど、ロングテールで細かくアクセスを稼ぐタイプのサイトなので、その辺りもフォローする必要があり・・・。

<?php

# ヘッダー情報を取得
$http_host = $_SERVER['HTTP_HOST'];
$http_cf_visitor = $_SERVER['HTTP_CF_VISITOR'];
$cf_visitor = $_SERVER['CF_VISITOR'];
$x_forwarded_proto = $_SERVER['HTTP:X-Forwarded-Proto'];
$https = $_SERVER['HTTPS'];
$user_agent = $_SERVER['HTTP_USER_AGENT'];

# ジャンプ先 URL
$iejump = 'http://www.example.com/ie4-8.html';
$nosnijump = 'http://www.example.com/index.html';
$snijump = 'https://www.example.com/index.html';


# httpsで接続してきているかの確認

# https で接続された場合、htmlにリダイレクトする
if($x_forwarded_proto === 'https' || $https === 'on' || strpos($http_cf_visitor, 'https') !== false || strpos($cf_visitor, 'https') !== false) {

	header("Location: ".$snijump);


# https 接続ではない場合の処理
} else {

	# IE 4~8かどうかチェック
	if(strpos($user_agent, 'MSIE 4') !== false || strpos($user_agent, 'MSIE 5') !== false || strpos($user_agent, 'MSIE 6') !== false || strpos($user_agent, 'MSIE 7') !== false || strpos($user_agent, 'MSIE 8') !== false) {

		# IE 4~8なら、SNI非対応かつIE専用ページにジャンプ
		header("Location: ".$iejump);


	# SNI 非対応かどうかチェック
	} elseif(strpos($user_agent, 'Windows NT 5') !== false || strpos($user_agent, 'UP.Browser') !== false || strpos($user_agent, 'DoCoMo/') !== false || strpos($user_agent, 'SoftBank/') !== false || strpos($user_agent, 'Android 1') !== false || strpos($user_agent, 'Android 2') !== false) {

		# SNI非対応ブラウザは http にジャンプ
		header("Location: ".$nosnijump);


	# SNI 対応ブラウザの処理
	} else {

		# SNI対応ブラウザは https にジャンプ
		header("Location: ".$snijump);

	}

}

?>

 

おまけ その2

Google App Engine でこのスクリプトを導入した際に、インスタンス節約のために調整した app.yaml ファイルもおまけで公開。アクセス数にもよるだろうけど、この調整でインスタンスの無料範囲を超えることなく運用できるようになった。

service: default
runtime: php55
api_version: 1
threadsafe: true

automatic_scaling:
  min_idle_instances: 0
  max_idle_instances: 1
  min_pending_latency: 3000ms
  max_pending_latency: 4000ms

handlers:
- url: /
  script: snicheck.php

- url: /index.html
  static_files: index.html
  upload: index.html

 

スポンサードリンク

コメントを残す

メールアドレスが公開されることはありません。

当サイトのコメント欄は承認制となっております。また、日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

スポンサードリンク

Twitter
利用中のサービス

GUiLZ Project では、以下のサービスを利用しています。


関連サイト
巡回先サイト様
アーカイブ