- 締切済み
相対URLによるリダイレクト
Locationヘッダに転送先のURLを指定する際に、相対URLをフルパスで指定すると内部リダイレクトになりますが、絶対URLを指定せずにブラウザにリダイレクトさせる方法はないでしょうか。 具体的にはこうです。 URL http://www.xxx.com/dir/from.cgi という位置にスクリプトがあるとして、ドキュメントルートにある「to.html」にリダイレクトさせたい場合、 Location: http://www.xxx.com/to.html と出力するのではなく、 Location: /to.html と出力すると、内部リダイレクトによってブラウザに返されることなく、つまりブラウザの「アドレス」欄はhttp://www.xxx.com/dir/from.cgiのまま「to.html」の内容が表示されるわけですが、そうではなく前者のように動作させたいのです(ブラウザに返してリダイレクトさせたい)。 目的は、ドメインなど絶対URLが変更になっても汎用的にスクリプトを移行できるようにしたい(絶対URLを書かなくて良いようにしたい)のですが、内部リダイレクトでは表示されるHTMLの内容によって、たとえば<img src="./image.gif">など相対パス指定の参照が正しく表示されないためです。 どうぞよろしくお願いいたします。
- みんなの回答 (5)
- 専門家の回答
みんなの回答
- 神崎 渉瑠(@taloo)
- ベストアンサー率44% (1016/2280)
> あるいは「内部リダイレクト」と「外部リダイレクト」の違いなど、もしや誤解なさっているのでは…。 「内部リダイレクト」や「外部リダイレクト」の言い方そのものは初めてですが、理解している意味との関係は間違っていないと思います。 > ブラウザのアドレス欄は http://www.example.com/to.html で、コンテンツボディも http://www.example.com/to.html のものを表示させたいわけです。 それならますますStatus:301か302で良いのでは、と思ったのですが、 > リダイレクトが返されない、なのでリクエストも発生しない、その結果、クライアント、サーバー、そして回線も含め、負担が少なくてすむ、というものです。 なるほど、これでわかりました。 ブラウザが、from.cgiとto.htmlの2回リクエストを送るのは仕方がないと思います。 HTMLファイルを書き出して<META>で再読込してもらうのよりは、Statusヘッダの方が通信量は少ないと思いますが、、、 > ANo.2回答 > また、ブラウザにリダイレクトが返された(ターミナルソフトにて確認)にも関わらず、なぜかリクエストを発生させず(というより発生させていないように見えるが実際は発生している)動作する場合があり、しかしHTMLファイルに含まれる相対URIの基点は正しいようで、言わば「ユーザーに隠したリダイレクト」のような動作で、なんとも良くわからない点もあります(ただこれはおそらくブラウザ特有の、あるいはバグかもしれません)。 Status:301の場合、 Firefox、IE6、Safari1.0、Lynxで、リダイレクト先のファイルがキャッシュに残っている場合に、リダイレクト先のファイルを読みに行かずに表示することを確認しました。 が、キャッシュがない場合は、常にto.htmlを読みに行っているようです。 TCPおよびHTTP通信ログから確認しました。 逆に考えると、キャッシュが残っているであろうファイルにリダイレクト(外部リダイレクト)させると、通信負荷は減らせると思います。 蛇足ながら、BBSなどの投稿完了画面の目的は 投稿後、すぐに閲覧画面を出すと、ブラウザがキャッシュを読んだ場合は、自分の投稿が画面に反映されませんから、再読込ボタンを押さなければなりません。 そうすると、再投稿することになってしまいますので、一度投稿完了画面を出すことで予防していると思います。 さらに蛇足 CGIスクリプトの出力を Status: 301 Location: /to.html とすると、CGIの仕様上はOKですが、Apache->クライアント(HTTPの仕様)の段階でNGになりますね。 この場合、ApacheはLocationヘッダを書き換えないようです。 Lynxではエラーが表示されます。 CGIスクリプトでStatus:200を出す場合は、Locationの内容によってApacheから出されるデータは大幅に変わるようですが、 Status:3xxなどを出す場合は、Locationヘッダは絶対URLにすべきかも知れません。 (CGIの仕様を知らずに絶対URLしか使わなかったのは、結果オーライなのかしら) > いずれにしても、この紛らわしい話にお付き合いいただき、誠にありがとうございます。 こちらこそ、誤解が多く申し訳ないです。 改めてテストすることで、新しく勉強になったこと(特にStatus:200とLocationヘッダの組み合わせによるApacheの動作)もありました。
- 神崎 渉瑠(@taloo)
- ベストアンサー率44% (1016/2280)
質問の投稿の > (ブラウザに返してリダイレクトさせたい)。 というところを読んで、ANo.1やANo.2の様に答えていたわけですが、、、 今の時間、少々寝ぼけてますが、、、 何となく理解出来ました。 Locationヘッダを使ってリダイレクトさせた時に、 ブラウザのアドレス欄はhttp://www.example.com/dir/from.cgiのままで、 ファイルの内容はhttp://www.example.com/to.htmlを表示したい。 その上で、<img>などで指定するファイル名の基準は/to.htmlと同じ位置、 つまり、to.htmlに<img src="./image.gif">と書かれているとhttp://www.example.com/image.gifを表示したい。 ということでよろしいでしょうか。 余程手の込んだことをしない限り、出来ないんじゃないかと思います。 アドレス(アドレス欄に表示されているURI)が http://www.example.com/dir/from.cgi ですから、このファイル(パス)が基準になるべきだとも思いますし。 で、from.cgiですが、 むしろ200を返さないと行けないんじゃないか、とも思えます。 Statusヘッダを出さない場合や、.htaccessとmod_rewriteを使った場合でも、200が返っていますし。 Locationヘッダで何とかするのではなく、 to.htmlの中で、絶対パスを指定するというのはダメなのでしょうか? <img src="/image.gif"> となっていると、from.cgiを使って表示した内容でも、to.htmlにアクセスした場合でもhttp://www.example.com/image.gifが表示されるようになります。 *余程手の込んだこと 無料ホスティングサービスのサーバーが、HTMLファイルに自動的に広告を入れるのと同じように、 HTMLを書き換える自作モジュールを組み込むか、Apacheそのものを作り替えれば出来るんじゃないかと。 StatusヘッダなしにLocationヘッダが返ってきた場合に、 src=""やhref=""が相対パスの場合、Locationで指定されたファイルのパスを書き込む to.html の内容 <img src="./image.gif"> Location: /to.html --> "/"を挿入 --> <img src="/./image.gif"> Location: /new/to.html --> "/new/"を挿入 --> <img src="/new/./image.gif"> またはfrom.cgiが行って、Content-type: text/htmlとともに出力。 ん~口で言うのは簡単(´・ω・`) 質問の解釈の仕方がまた間違ってたらすみません。
お礼
で、「あえて内部リダイレクトを発生させない方法は?」が、今回の質問でした。 ですから、投稿いただいた、 > ブラウザのアドレス欄は http://www.example.com/dir/from.cgi のままで、 > ファイルの内容は http://www.example.com/to.html を表示したい。 上記は普通の内部リダイレクトで(特別なことは何もせずに実現可能です)、やりたいのはその逆、ブラウザのアドレス欄は http://www.example.com/to.html で、コンテンツボディも http://www.example.com/to.html のものを表示させたいわけです。 例を挙げますと、たとえば何かの登録するようなページや、掲示板の類では多いですが、掲示板に記事を投稿した後、「投稿完了」のメッセージが数秒だけ出てすぐに記事の表示画面にリダイレクトでジャンプさせる、というパターンは多いですよね。 また他でもない、今ご覧になっているこのサイト(OKWave)でも、投稿後の画面では「投稿した記事を見る」というようなリンクをわざわざクリックさせますよね。 なぜリダイレクトヘッダを発行してそのまま移動させないのかというと、外部リダイレクトを発生させるためです。 ※OKWaveがこのCGIの仕様のためなのかは不明ですが、遷移が同じという意味で例に挙げました(おそらくmodでしょうけど…)。 それで、いろいろ調べては見たのですが、結局のところ、多くのスクリプトが外部リダイレクトを発生させるためにHTMLのMETAタグを出力していることから、やはり今のところこれしかなさそうです…。とりあえずこれなら絶対パスによる相対URLでも外部リダイレクトが発生します。 むしろ一番確実ではありますが、わざわざ内容の無いリダイレクト用のHTMLを吐かなきゃいけないのと、それこそブラウザに依存してしまうのが難点。ただまぁアンカーも併用するなど、対処法はあるので、今のところよしとするかな、という気がしてきています(眠いし)。 いずれにしても、この紛らわしい話にお付き合いいただき、誠にありがとうございます。
補足
※長文のため「補足」→「お礼」欄に分けました。 本当に何度もお付き合いいただき、ありがとうございます。 私もかなり眠い中いろいろ調べ、どうやらとりあえずの結論に至りました。 で、その前に、 > Locationヘッダを使ってリダイレクトさせた時に ~(中略) > ということでよろしいでしょうか。 その逆です…。 > 余程手の込んだことをしない限り、出来ない 質問文に書いたように、おっしゃることなら何もせずに(広告挿入のパースフィルターなども無しにApacheに手を加えることも無く)実現可能です。 誠に失礼だとは思うのですが、もしやと思うのですが「内部リダイレクト」をご存知でしょうか?… あるいは「内部リダイレクト」と「外部リダイレクト」の違いなど、もしや誤解なさっているのでは…。 ご存知でしたら失礼をお許しください。なんだかかみ合っていない様な?気がしてしまいまして。 もしご存じないなら、一応簡単に説明すると、 Locationヘッダに「絶対パス」による「相対URL」を指定すると、内部リダイレクトが発生します。 ※ 「パス」と「URL」の違いなど紛らわしいので注意。「絶対パス」を「フルパス」と書いた文書もありますが厳密には正しくないという見方もあるそうです。 下の2つは両方とも同じファイルを指していますが、サーバーの動作が異なります。 Location: http://www.xxx.com/to.html ← 絶対URL=外部リダイレクトが発生する Location: /to.html ← 絶対パスによる相対URL=内部リダイレクトが発生する どこが違うかというと、内部リダイレクトは名前のとおりサーバー内部で処理され、リダイレクトがブラウザに返されません。なのでブラウザに依存しませんし、転送先のコンテンツボディが200 OKとともに返されます。ブラウザはリダイレクトしたことを「知らない」ため、アドレス欄は元のままです。 リダイレクトが返されない、なのでリクエストも発生しない、その結果、クライアント、サーバー、そして回線も含め、負担が少なくてすむ、というものです。 これはCGIの仕様です。
- 神崎 渉瑠(@taloo)
- ベストアンサー率44% (1016/2280)
RFCよりコピペ 300 Multiple Choices 301 Moved Permanently 302 Found 303 See Other 304 Not Modified 305 Use Proxy 306 (Unused) 307 Temporary Redirect 3xxのステータスコードはそれぞれ、上記の通りです。 ステータスコードは何でも言いわけではなく、それぞれに意味があり、そのコードに従って、ブラウザは動作を変更します。 (HTMLのTABLEは表を作る物でレイアウトに使うべきじゃない、というのと同じような内容になってしまいますが) こういう動作をしたからOKというのは、あまり好ましい物ではありません。 Locationヘッダを出す目的がわからないので何とも言えませんが、 おそらくこの場合は303は使うべきではないと思います。 (おそらく、サーバーの負荷などにより代替文書を見てもらう場合などに使うと思います。 RFCにも注意書きがあるように、303を理解しないブラウザが多いようです。) メンテナンスなどで一時的に移動してもらうなら307になります。 画像ファイルの場所などを示すなら301(恒久的な移動)か302(一時的な移動)で良いのではないでしょうか? > やはりhttpdの仕様にかなり依存しているようです。 ステータスコードによる動作の違いは、HTTP(通信規格)の仕様、およびそれに準じたブラウザの実装(仕様)による物です。 ApacheはCGIスクリプトから受け取ったデータを、クライアントに丸投げしてるにすぎないと思います。 (ある程度HTTPの規格に合うように調整はするでしょうが) たとえばフォームでデータを送ったときにCGIスクリプトが307を返すと IEとFirefoxでの挙動が違うのが確認出来ると思います。 > (ただこれはおそらくブラウザ特有の、あるいはバグかもしれません)。 可能性がありますね。 RFCの注意書きにも、RFCと違う動きをするブラウザがあると、特にリクエストメソッドに関する部分で何度も出てきます。 > Note: When automatically redirecting a POST request after receiving a 301 status code, some existing HTTP/1.0 user agents will erroneously change it into a GET request. Windows版Firefox1.5.0.7でGETで送り直すことを確認しました。 > なぜかリクエストを発生させず(というより発生させていないように見えるが実際は発生している)動作する場合があり、 一応、IE6とFirefox1.5.7(ともにWindowsXP)で見てみましたが、 ステータスコード301、302、303、307を受け取った後で、 リダイレクト先のファイルにリクエストを送っているようですが、、、 キャッシュが残ってませんか? 301の場合など、リダイレクト先のファイルをキャッシュに残すことが許されています。 Apacheのログはどうでしょうか? もしよろしければ、クライアントのOS、ブラウザの種類とステータスコードを教えてもらえますか? ----- プレビューを見て、、、長っΣ(´Д ` ) すみません、読みづらくて、、、、
お礼
ご回答ありがとうございます。 まずは、せっかくご指摘いただいたのに申し訳ないのですが、ステータスコードにつきましては、「~は使うべきではない」や「こういう動作をしたからOK」というような趣旨で行ったのではないのです。 当然ながらこれらの意味は承知していますし、目的を果たすためではありません。 下にも書きましたが、今回の目的である「内部リダイレクトを発生させない」ための検証中に挙動がどうも明確ではなく、エンティティなどヘッダの内容、単にステータスコードによる違いを試してみただけのことです。どうぞご理解ください。 ご指摘の内容自体はごもっともですし、感謝いたします。 で、本題なのですが、とりあえずブラウザのバグなどはさておき、なにしろ今回は内部リダイレクトが発生する条件について調べています。 ですから、 > ApacheはCGIスクリプトから受け取ったデータを、クライアントに丸投げしてるにすぎないと思います。 「丸投げしない」のが内部リダイレクトであり、これの発生の有無ということです。 話をステータスコードに戻すと、どうも直接関係ないのかもしれません。コードの意味としては301、302、303、307あたりで良さそうなものなのですが(ちなみに303は、そのURIが別のURIにある、という意味です)、301(Moved Permanently)や302(Moved Temporarily)のようにRedirectionつまりLocation指定して良いかどうかに関わらず、やはりリダイレクトが返るかどうかとは違うようです(そういう意味でステータスコードというよりレスポンスコード)。 考えてみれば当然かもしれません。内部リダイレクトの場合、レスポンスコードは元のリクエストのステータスコードではなくリダイレクト先のステータスすなわち(成功すれば)200なわけで、リクエストに対するステータスコードが必ずしもレスポンスコードではないことから、むしろ指定できるのはおかしい、あるいは指定は無意味ということかもしれません。 ちなみにApacheのログはデフォルトで元リクエストのステータスが記録されます。 加えてブラウザのバグの可能性などもあるので、下にも書きましたがターミナルソフトで直接アクセスして調べてみました。ようするに直接GETなど送ってリダイレクトが返るかどうかを見る、というような方法で、最も確実と考えました。
- 神崎 渉瑠(@taloo)
- ベストアンサー率44% (1016/2280)
> CGIの仕様上は、URLは絶対パスでも相対パスでも構いません。相対URLをフルパスで指定すると、内部的なリダイレクトになります。 CGIの仕様を確認しましたが、"/"から始まる絶対パスでも構わないようですね。 失礼しました。 --------------- Location = "Location" ":" ( fragment-URI | rel-URL-abs-path ) NL fragment-URI = URI [ # fragmentid ] URI = scheme ":" *qchar fragmentid = *qchar rel-URL-abs-path = "/" [ hpath ] [ "?" query-string ] hpath = fpsegment *( "/" psegment ) fpsegment = 1*hchar psegment = *hchar hchar = alpha | digit | safe | extra | ":" | "@" | "& | "=" ------------ > CGI1.2 Spec. > If the Location value is a path, then the server will generate the response that it would have produced in response to a request containing the URL となっていますので、ウェブサーバーが変換してくれているようです。 スクリプトから絶対URIが返されたときはステータスコード302を出さなければならないとありますが、 絶対パスが返されたときのステータスコードは定義されていませんから、ウェブサーバーの実装次第になっているのかも知れません。 長々と書いてしまいましたが、要するに、 スクリプトからステータスコード302と、絶対パスでのlocationヘッダを返せば、絶対URLの場合と同じ動作をすると思います。 from.cgi #!/usr/bin/perl print "Status: 302 this page was moved\n"; print "Location: /to.html\n"; print "\n"; exit();
お礼
たびたびご回答ありがとうございます。 こちらこそ、解りにくい書き方ですみません。「相対URLをフルパス」だとか「絶対URL」など紛らわしかったかもしれません…。 で、下に書いた「検証中ですが、レスポンスコードとの組み合わせで~」というのが、まさしく今回ご提示いただいた方法です。 しかし、とりあえずApache 2.0.46にて試しているのですが、どうも明確な挙動がわからない部分もあります。 301を返せばいいのか、あるいは303、307、そしてHTTPヘッダのエンティティ有無なのか、どういうわけか内部リダイレクトが生成されるパターンがあり(条件が絞れていない)、やはりhttpdの仕様にかなり依存しているようです。 また、ブラウザにリダイレクトが返された(ターミナルソフトにて確認)にも関わらず、なぜかリクエストを発生させず(というより発生させていないように見えるが実際は発生している)動作する場合があり、しかしHTMLファイルに含まれる相対URIの基点は正しいようで、言わば「ユーザーに隠したリダイレクト」のような動作で、なんとも良くわからない点もあります(ただこれはおそらくブラウザ特有の、あるいはバグかもしれません)。 いずれにしても、もう少し情報を探してみます。 ありがとうございました。
- 神崎 渉瑠(@taloo)
- ベストアンサー率44% (1016/2280)
返信がないようなので、、、 > Locationヘッダに転送先のURLを指定する際に、(中略)、絶対URLを指定せずにブラウザにリダイレクトさせる方法はないでしょうか。 ありません。 Locationヘッダによるリダイレクトは、プロトコル名から始まる絶対URLで指定しなければなりません。 絶対URLでない指定をどの様に扱うかは、ブラウザ依存です。 (リダイレクトされなくても、それは正しい動作といえます)
お礼
ご回答ありがとうございます。 すみません、検証中ですが、レスポンスコードとの組み合わせで、ほぼ解決できそうです(しかしこれが最良な方法かどうかはわかりません)。 一応補足させていただきますと、 > Locationヘッダによるリダイレクトは、プロトコル名から始まる絶対URLで指定しなければなりません。 CGIの仕様上は、URLは絶対パスでも相対パスでも構いません。相対URLをフルパスで指定すると、内部的なリダイレクトになります。 > 絶対URLでない指定をどの様に扱うかは、ブラウザ依存です。 相対URLによる内部リダイレクトは、サーバー側の処理です。 新しいリクエストは発生しないため、ブラウザには依存しません。 今回はあくまでもCGIに関して質問させていただきましたが、これらはHTTPと紛らわしい内容で、解りにくいかもしれませんね…。 ご回答いただきありがとうございます。
補足
ご回答ありがとうございます。 以下補足させていただきます。 > ブラウザが、from.cgiとto.htmlの2回リクエストを送るのは仕方がないと思います。 むしろ逆で、2回リクエストを送らせたいのです。 (外部リダイレクト=リクエストは2回) > HTMLファイルを書き出して<META>で再読込してもらうのよりは、Statusヘッダの方が通信量は少ないと思いますが、、、 上記はようするにLocationヘッダによって外部リダイレクトさせるということですよね? もちろんそれを今回、何度も試行錯誤しているわけですが、やはり絶対パスによる相対URL&レスポンスコードで制御できないようです。 > Status:301の場合、 (中略) > が、キャッシュがない場合は、常にto.htmlを読みに行っているようです。 それが外部リダイレクトになります。また、別にキャッシュを読んでも構いません。 内部リダイレクトではリクエストは発生しない、ということになります(しつこいようですが、これを避けたいわけです)。 > 逆に考えると、キャッシュが残っているであろうファイルにリダイレクト(外部リダイレクト)させると、通信負荷は減らせると思います。 あくまでも今回の目的は、通信負荷を減らすことではなくて、言わばその逆、外部リダイレクト=リクエストを発生させたいのです。 解りにくくすみませんでした。 > 投稿後、すぐに閲覧画面を出すと、ブラウザがキャッシュを読んだ場合は、自分の投稿が画面に反映されませんから、再読込ボタンを押さなければなりません。 > そうすると、再投稿することになってしまいますので、一度投稿完了画面を出すことで予防していると思います。 厳密にはちょっと違いまして、 これらはフォーラム型など投稿と閲覧が独立しているようなタイプの話です(独立していないタイプでは、投稿完了画面が出るものは少ない気がします)。なので二重投稿になることはないですが、ポイントはそこではなくて、画面遷移にリダイレクトを使う点です。このとき内部リダイレクトが発生すると表示が壊れることがあります。 ちなみにちょっと話はずれますがキャッシュについては、別の画面を間に挟んだところでキャッシュを読ませないことにはならなくて、むしろ逆、内部リダイレクトさせることでキャッシュを読みません(コンテンツボディが返されるので)。 話を戻しますが、そもそもリダイレクトさせるなら、なぜサーバー側でやらないのか?遷移ステップも少なく負荷も軽く見栄えも良い、ついでにブラウザがキャッシュを読むこともないのに、という点が重要で、外部リダイレクトさせる工夫が必要になります。 ※もちろん私は世のすべてのスクリプトを知っているわけでもなく、目的が違うようなものも中にはあるかと思います。 > CGIスクリプトの出力を > Status: 301 > Location: /to.html > とすると、CGIの仕様上はOKですが、Apache->クライアント(HTTPの仕様)の段階でNGになりますね。 > この場合、ApacheはLocationヘッダを書き換えないようです。 > Lynxではエラーが表示されます。 そうなんですよね…。これがやっかいで… (ちなみに内部リダイレクトはLocationヘッダを「書き換える」というよりLocationヘッダを「返さない」) つまり目的どおり外部リダイレクトが発生していても、肝心要のクライアント側が絶対パスの相対URLを解釈してくれない。IEなどは問題ないんですが…。 これはMETAタグによるリダイレクトでも同じような問題を孕んでいるみたいです。 そんなわけで、下にも書きましたがMETAタグ&アンカーで対応しようと考えています。大多数のブラウザは問題なく動作し、動作しない一部のブラウザでは、アンカーをクリックしてもらう、ということです。 > CGIスクリプトでStatus:200を出す場合は、Locationの内容によってApacheから出されるデータは大幅に変わるようですが、 > Status:3xxなどを出す場合は、Locationヘッダは絶対URLにすべきかも知れません。 Status:200を出力しなくても内部リダイレクトの場合はLocationヘッダ自体がブラウザに送られませんから、「リダイレクトをブラウザに送る場合は…」と考えると解りやすいかもしれません(言い換えると「外部リダイレクトの場合は…」)。