以前CGI 版で404 レポートを出力するプログラムを作ったのだが、今回はPHP 版を作った。何故つくろうかと思ったかというと、WordPress などにはプラグインで機能するものがあるが、これらを利用しないサイトでも移転した際の案内を付けたかったから。
ところで、疑問に思うのは同様のプログラムを見つけることができないのは何故だろう。見つけ方が悪いのか、それとももっと高次元(Apache のログを解析するなど)で行われているのか。あるいはGoogle のウェブマスターツールなどでも404 がどこで発生しているかを見つけることはできるから、そういったものを使っているのだろうか。
WordPress などのCMS には404 時の動作を定義したり、改造したり、プラグインを導入したりすることで発見から対策までを容易にしている。今回作るプログラムは単体で動作するように設計しているので、どのサイトにもPHP が利用できる環境であれば使えると思う。
スポンサードリンク
期待する動作
- .htaccess で404 時に本プログラムを指定する
- 本プログラムはユーザ、Bot 等に対してHTTP ステータスコード 404 を返す
- 同時に検索語が解析でき、移転先へクエリを転送できる場合は案内する
- 同時に管理者に対して404 の発生を知らせる
- 管理者は必要に応じて .htaccess を更新し、適切なページにリダイレクトさせ404 を軽減する
バージョンアップ
公開までに少し運用して改良したところ
- Ver.0.04- favicon やrobots などを報告しないようにする$reject 配列を追加し、合致した場合は報告しない(完全一致のみ)。
※Ver.0.03 でも搭載したが、ハードコーディングだったのを改善した - Ver.0.05- 攻撃を目的としたアクセスの多くがReferer を持っていないことから、Referer を持っていない場合に報告しないようにする設定を追加した。$need_referer をfalse にするとreject に合致しなければ全件報告するので、どんな攻撃を受けたか見ることができる。ちなみにレポートが来たということは404 で弾いたということになる。その他Ver.0.05 ではいろいろ弄ったので新たなバグを内包したかも。
動作例
コード
<?php /* =================================================== * 404 Report and Suggest for mb-PHP * --------------------------------------------------- * DESCRIPTION: * - output 404 message for user * To display a polite message to visitors * * - guide for new site by searched strings * The destination guides to parse the search term. * * - site administrator get error mail * To send a message to the site administrator. * * USGE: * write .htaccess file * ErrorDocument 404 /err404.php * * LICENSE : GNU GPL * CODE : UTF-8 * LANGUAGE: PHP, ja-JP * * AUTHER * COPYRIGHT (C) CASEY 2011 * https://pc.casey.jp/ * * VERSION * ver.0.01 2011.09.18 first version * ver.0.02 2011.09.19 change algorithm * ver.0.03 2011.09.19 add reject function * ver.0.04 2011.09.19 change reject algorithm * ver.0.05 2011.09.29 add need referer * =================================================== */ // ===[ settings ]==================================== // --- suggest --- $new_url_suggest = true; $new_url_extract = true; $new_url_base = 'http://newexample.com'; $new_url_querystr = '/?s='; // --- mail ------ $mail = true; $mail_subject = "404 error report"; $mail_body = "an error occurred"; $mail_addr_from = "root@localhost"; $mail_addr_to = "[email protected]"; // --- rejects --- $need_referer = true; $rejects = array( '/robots.txt', '/favicon.ico' ); // ===[ settings end ]================================ // do not change! $debug = false; $sTime = time(); $VER = "0.05"; $program_name = "404 error report Ver."; $program_name .= $VER . " on "; $program_name .= $_SERVER['SERVER_NAME']; // =================================================== // MAIN // =================================================== // 2011.09.19 ver.0.27 // init if($debug){ _setDebugvalue(); } // start output outputHeader(); echo getHeaderHtml(); // add ver.0.05 if(!isset($_SERVER["HTTP_REFERER"])){ if($need_referer){ $mail = false; } }else{ $keywords = getKeywords(); // return suggest if($keywords && $new_url_suggest){ getSuggest(); } } // mail send $mail_body = getMailbody(); if(!$debug && $mail){ echo "<p>この問題は管理者に通知されました。</p>"; mailSend(); } // debug info if($debug){ _debuginfo(); } // print footer echo getFooterHtml(); // end of script exit(); // =================================================== // FUNCTIONS // =================================================== // ==================================== // output footer html // ==================================== // 2011.09.19 ver.0.03 function getFooterHtml(){ $host = $_SERVER['HTTP_HOST']; return <<< EOH <div align="right"> <a id="site-link" href="http://$host" target="_blank">$host</a> </div> </body> </html> EOH; } // ==================================== // output header html // ==================================== // 2011.09.19 ver.0.03 function getHeaderHtml(){ return <<< EOH <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>404 Not Found</title> <link rel="stylesheet" href="/err.css" type="text/css" /> </head> <body id="error-page"> <h1>404 Not Found</h1> <h2>ページが見つかりません</h2> <p>リクエストされたページが見つかりませんでした。</p> EOH; } // ==================================== // mail send // ==================================== // 2011.09.19 ver.0.03 function mailSend(){ // init global $mail_addr_to, $mail_subject, $mail_body, $mail_addr_from; // set japanese mb_language("japanese"); mb_internal_encoding("UTF-8"); // mail send mb_send_mail( $mail_addr_to, $mail_subject, $mail_body, "From:" . $mail_addr_from ); } // ==================================== // debug information // ==================================== // 2011.09.19 ver.0.03 function _debuginfo(){ // init global $mail_body; echo "[ DEBUG MODE ]<br />\n"; $debug_html = $mail_body; echo preg_replace('/\n/', '<br />', $debug_html); } // ==================================== // suggest links // ==================================== // 2011.09.19 ver.0.03 function getSuggest(){ // init global $keywords, $new_url_base, $new_url_querystr; // from search engine echo "<p>もしかしたら以下に移動したかもしれません:</p>"; echo "<ul>"; $new_url = $new_url_base . $new_url_querystr; foreach($keywords as $word){ echo '<li><a href="' . $new_url . urlencode($word) .'" target="_blank">移転先(' . $new_url_base . ')を「' . $word . "」で再検索する</a></li>"; } echo "</ul>"; } // ==================================== // set debug mode value // ==================================== // 2011.09.19 ver.0.03 function _setDebugvalue(){ // add ver.0.05 global $need_referer, $mail; if(!isset($_SERVER["HTTP_REFERER"])){ $_SERVER["HTTP_REFERER"] = ''; } } // ==================================== // make mail body // ==================================== // 2011.09.19 ver.0.05 function getMailbody(){ // init global $keywords, $rejects, $program_name, $sTime, $mail; $mail_body = "ErrorID: " . $_SERVER["REQUEST_TIME"] . "\n"; //$mail_body .= "HTTP_HOST: " . $_SERVER["HTTP_HOST"] . "\n"; //$mail_body .= "REQUEST_URI: " . $_SERVER["REQUEST_URI"] . "\n"; $mail_body .= "URL: http://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] . " \n"; if($keywords){ if(is_array($keywords)){ $mail_body .= "QUERY:" . implode(",", $keywords) . "\n"; }else{ $mail_body .= "QUERY:" . $keywords . "\n"; } }else{ $mail_body .= "QUERY: Unknown" . "\n"; } $mail_body .= "HTTP_REFERER: " . $_SERVER["HTTP_REFERER"]. "\n"; $mail_body .= "REMOTE_ADDR: " . gethostbyaddr($_SERVER['REMOTE_ADDR']) . " (".$_SERVER['REMOTE_ADDR'].")" . "\n"; $mail_body .= "REQUEST_TIME: " . date('Y/m/d H:i:s', $_SERVER['REQUEST_TIME']) . "\n"; $mail_body .= "\n" . $program_name . " (C) https://pc.casey.jp 2011 "; $eTime = time() - $sTime; $mail_body .= " (" . $eTime . "Sec+)"; // 2011.09.19 add reject // 2011.09.19 remove // favicon.ico //$pos = strpos($_SERVER["REQUEST_URI"], 'favicon.ico'); //if($pos !== false){ $mail = false; } // 2011.09.19 add reject new if(in_array($_SERVER["REQUEST_URI"], $rejects)){ $mail = false; }; return $mail_body; } // ==================================== // start output // ==================================== // 2011.09.19 ver.0.02 function outputHeader(){ // init global $debug; if($debug){ header( 'HTTP/1.1 200 OK' ); }else{ header( 'HTTP/1.1 404 Not Found' ); } header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' ); header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' ); header( 'Cache-Control: no-cache, must-revalidate, max-age=0' ); header( 'Pragma: no-cache' ); header( 'Content-Type: text/html; charset=utf-8' ); } // ==================================== // check referer // ==================================== // 2011.09.19 ver.0.07 function getKeywords(){ // init global $new_url_extract; $keywords = null; // check ref if(isset($_SERVER["HTTP_REFERER"])){ // decode referer $_SERVER["HTTP_REFERER"] = urldecode($_SERVER["HTTP_REFERER"]); // check domain $U = @parse_url($_SERVER["HTTP_REFERER"]); // check search strings $pos = strpos($U["host"], 'google'); if($pos !== false){ //Google # q= preg_match('/q\=([^\&]+)/', $_SERVER["HTTP_REFERER"], $Qstr); }else{ //Yahoo ? p= preg_match('/p\=([^\&]+)/', $U["query"], $Qstr); } // split keywords by space to keyword array if($Qstr[1]){ // keywords extract if($new_url_extract){ $keywords = preg_split('/[\s ]+/', $Qstr[1]); }else{ $keywords[] = $Qstr[1]; } } } return $keywords; }
ダウンロード
調整中
参考文献
- SEO:404 Not Found エラーは検索順位に影響するか? ::SEM R
検索語解析
- PHP入門 – 検索サイトからの検索ワードを取得 | PHPサンプル
- PHPリファレンス(urlencode())
- 検索エンジンのキーワードを表示する
- 高機能アクセス解析CGI Professional版 – futomi’s CGI Cafe
- Array to String – PHP
メール送信
- mb_internal_encoding
- PHPで日本語メールを送る – 基本編 – EC studio 技術ブログ
PHP マニュアル
- PHP: preg_match – Manual
- PHP: preg_replace – Manual
- PHP: strpos – Manual
- PHP: explode – Manual
- PHP: vsprintf – Manual
- PHP: implode – Manual
- PHP: gethostbyaddr – Manual
- PHP: preg_replace – Manual
- PHP: stripos – Manual
- PHP: parse_url – Manual
# PHP には何故、変数宣言の強制がないのだろう・・・?
コメント