ラベル apache の投稿を表示しています。 すべての投稿を表示
ラベル apache の投稿を表示しています。 すべての投稿を表示

2011年2月6日日曜日

CGIの環境変数 SERVER_NAME と HTTP_HOST

ほかの誰かが作ったCGIを移行する仕事で、バグの原因になったので記録しておく。

症状

CGIで作成された、お問い合わせフォーム。このCGIが出力するHTMLは、head要素内のscript要素でJavaScriptファイルを参照し、ユーザーインターフェースを制御している。

www有りと無しの二通りのURLでアクセスが可能(http://www.example.com/form/ と http://example.com/form/)。しかし、www有りでアクセスした場合は、JavaScriptが正しく動作しない。

原因と対応

JavaScriptファイルのうち、動的に生成されるファイルにバグがあった。動的に生成される部分というのは XMLHTTPオブジェクトがアクセスするURLであり、環境変数SERVER_NAMEを使って生成されていた。次のように。

$url = "\/\/" . $ENV{'SERVER_NAME'} . $service_path . '?';
 $script =~ s/<url>/$url/g;  # $scriptの中身はJSのテンプレ。それを置換している。
 print "Pragma: no-cache\n";
 print "Cache-Control: no-cache\n";
 print "Content-type: text/plain; charset=UTF-8\n\n";
 print $script;

SERVER_NAMEの値はWebサーバーの設定に依存してしまうため、JavaScriptファイルを取得するときのリクエストが www.example.com なのに対して、XMLHTTPオブジェクトは example.com を参照してしまうという矛盾が生じた。これはクロスドメイン制限(cross-domain restrictions)に抵触、あるいは同一生成元ポリシー(same-origin policy)に反してしまう。よって、次のように修正すればOK。

$url = "\/\/" . $ENV{'HTTP_HOST'} . $service_path . '?';  # HTTP_HOST に変更
 $script =~ s/<url>/$url/g;
  (略)

SERVER_NAME と HTTP_HOST

CGIの環境変数のうち、SERVER_NAME と HTTP_HOST はよく似ていているのだが、意味が異なる。

これらの意味は、CGIに環境変数を渡しているApacheのドキュメントに書いてあるはずなのだが、いまいちみつからない。とりあえず、mod_rewriteの中に説明があったのでそこを引用しておく。

mod_rewrite - Apache HTTP Server
  • HTTP headers:
    • (略)
    • HTTP_HOST
    • (略)
  • server internals:
    • (略)
    • SERVER_NAME
    • (略)

These variables all correspond to the similarly named HTTP MIME-headers, C variables of the Apache server or struct tm fields of the Unix system. Most are documented elsewhere in the Manual or in the CGI specification.

SERVER_NAME and SERVER_PORT depend on the values of UseCanonicalName and UseCanonicalPhysicalPort respectively.

要するに、HTTP_HOSTのほうは"HTTP headers"、つまりブラウザからのリクエストヘッダに含まれるHOSTを表し、SERVER_NAMEのほうは"server interanals"、つまりApacheサーバー内の設定ファイル等に記述されているSERVER_NAMEを表すということ。したがって、HTTP_HOSTを扱うときはセキュリティ的に注意を要する、なんて話題も避けられない(変な文字列が送られてくる可能性があるから、内容をチェックしたりエスケープしたりする)。

このあたりの話は、Apacheから環境を引き継ぐプロセス全般に通用する話だから、PerlだけでなくPHPやRuby, Javaサーブレットなどを作るときにも頭に入っているとよいかもしれない。あと、mod_rewrite の条件を書く時にも。

2010年12月15日水曜日

Redirectディレクティブのパラメータの形式

ある日サーバーの乗り換えを実施したところ、一部のURLで404エラーが発生するようになってしまった。

原因は、乗り換え前のサーバーとApacheのバージョンが変わってしまってリダイレクトが効かなくなったため。根本的には、 .htaccess 内のRedirectディレクティブで形式エラーが発生したためであった。

エラーのログ(apache の error_log)

[Fri Dec 10 20:18:06 2010] [alert] [client xxx.xxx.xxx.xxx] /foo/bar/baz/.htaccess: Redirect to non-URL, referer: http://example.com/index.html

.htaccess で Redirect to non-URL ですよ、と。

.htacces の修正

修正前
Redirect permanent /foo.html /foo/index.html

最後のパラメータ(リダイレクト先)がURLの形式になっていなかった。乗り換え前のサーバーではこれで問題なかったのだが…

修正後
Redirect permanent /foo.html http://example.com/foo/index.html

httpスキームとホスト名を追加してURLの形式にしたらOK。ホスト名を指定してしまうとWebサイトのテスト工程などで不便になるのだが、やむをえず。

Redirectディレクティブの仕様を調べてみる

ApacheのドキュメントからRedirectのSyntaxを引用:

Redirect [status] URL-path URL

リダイレクト元を表すURL-pathとリダイレクト先を表すURLの形式については、バージョン1.3, 2.0, 2.2 ごとに仕様記述が変化している(mod_alias は比較的素朴で安定したモジュールだと思われるが、それでも変化している)。仕様を読む限り、バージョン2.2からリダイレクト先の記述でスキームやホスト名を省略できることになったようだ。

Apache 1.3 の場合
パラメータ URL-path, URL の形式については特に記述が無い。
Apache 2.0 の場合
URL について、「スキームとホスト名で始まる完全なURLであるべし」と記述されている。
The new URL should be an absolute URL beginning with a scheme and hostname
この文で、"an absolute URL" という英語に「絶対URL」という用語をあてる人もいるが、混乱を招くようだ。"absolute"は"complete"でもあるから「完全なURL」としておいたほうが適切と思われる。
Apache 2.2 の場合
URL について、さらに「しかし、スラッシュで始まるURL-pathも使用できる。その場合は現在のサーバーのスキームとホスト名が付加される」と追記されている。
, but a URL-path beginning with a slash may also be used, in which case the scheme and hostname of the current server will be added.

この文で"URL-path"の書体はイタリックではない。よって一般的な用語としての"URL-path"であり、SyntaxにおけるURL-pathではないと思われる。

2010年6月25日金曜日

クローラーの邪魔をしたい場合

.htaccess

BrowserMatchNoCase Googlebot     robot
BrowserMatchNoCase Slurp         robot
BrowserMatchNoCase msnbot        robot
BrowserMatchNoCase proodleBot    robot
BrowserMatchNoCase psbot         robot
BrowserMatchNoCase ScSpider      robot
BrowserMatchNoCase TutorGigBot   robot
BrowserMatchNoCase YottaShopping robot
BrowserMatchNoCase Faxobot       robot
BrowserMatchNoCase Gigabot       robot
BrowserMatchNoCase MJ12bot       robot
BrowserMatchNoCase Baidu         robot
deny from env=robot

確認(Perlモジュールを使う)

$ lwp-request -mHEAD -H 'User-Agent: slurp' http://sample.com/img/1.jpg
$ lwp-request -mHEAD -H 'User-Agent: slurp' http://user:password@testserver.com/img/1.jpg