6

ヘッダーが存在せず、誰かがにアクセスしようとした/場合にリダイレクトする書き換えルールがあります。返されるヘッダーを除いて、正常に動作します。応答から欠落しています。Accept-Language?lang=enVary: Accept-Language

RewriteCond %{HTTP:Accept-Language} ^$  
RewriteCond %{QUERY_STRING}         ^lang=en  
RewriteRule ^$                      http://www.example.com/?     [R=301,L]

Apacheのドキュメントでは、次のように指定されています。

条件でHTTPヘッダーが使用されている場合、条件が要求に対してtrueと評価された場合に備えて、このヘッダーが応答のVaryヘッダーに追加されます。要求に対して条件がfalseと評価された場合は追加されません。

条件は間違いなく一致してリダイレクトされているので、Apacheが言語を追加しない理由がわかりません。プロキシがそれをキャッシュし、送信されたAccept-Languageヘッダーに関係なく?lang=en常にリダイレクトする場合、これが実際の問題になる理由がわかります。/

4

3 に答える 3

11

Apacheのリクエスト処理システムの怪しげな下腹を覗いてみると、ドキュメントはやや誤解を招くことがわかりました...しかし、説明に入る前に、私が言えることから、これについてはApacheに翻弄されています。

クライアントの問題

まず、ヘッダー名は、クライアントから送信されない場合、 Vary応答ヘッダーに追加されません。これはmod_rewrite、そのヘッダーの値を内部で構成する方法によるものです。

apr_table_get()、リクエストのヘッダーテーブル、および指定した名前を使用して、名前でヘッダーを検索します。

const char *val = apr_table_get(ctx->r->headers_in, name);

がテーブル内のキーでない場合name、この関数はを返しNULLます。これは問題です。これは、直後に次のチェックが行われるためvalです。

if (val) {
   // Set the structure member ctx->vary_this
}

ctx->vary_this最終的なVaryヘッダー*RewriteCondにアセンブルする必要のあるヘッダー名を累積するために使用されます。値がない場合、割り当てや追加は行われないため、参照されている(送信されていない)ヘッダーがに表示されることはありません。ドキュメントにはこれが明示的に記載されていないため、期待したものである場合とそうでない場合があります。Vary

*余談ですが、NV(変更なし)フラグと失敗時無視機能は、に設定ctx->vary_thisすることで実装されNULL、応答ヘッダーへの追加を防ぎます。

ただし、 Accept-Languageを送信した可能性はありますが、空白でした。この場合、空の文字列は上記のチェックに合格し、ヘッダー名は上記の内容からVaryに追加されます。mod_rewriteこれを念頭に置いて、私は次のリクエストを使用して何が起こっているのかを診断しました。

ユーザーエージェント:フィドラー
受け入れる:text / html、application / xhtml + xml、application / xml; q = 0.9、* / *; q = 0.8
受け入れる-言語:
Accept-Encoding:gzip、deflate
Accept-Charset:ISO-8859-1、utf-8; q = 0.7、*; q = 0.7
キープアライブ:115
接続:キープアライブ
ホスト:129.168.0.123

これも機能しませんが、なぜですか?mod_rewriteルールと条件が一致したときにヘッダーを確実に設定します(チェックされたすべての条件ctx->varyの集計です)。ctx->vary_this

if (ctx->vary) {
    apr_table_merge(r->headers_out, "Vary", ctx->vary);
}

これはlogステートメントで確認でき、応答ヘッダーを生成するときに使用される変数ですr->headers_out しかし、何かが間違いなくうまくいかないことを考えると、ルールが実行された後に問題が発生するはずです。

.htaccessの問題

.htaccess現在、または<Directory>セクションでルールを定義しているようです。これはmod_rewrite、Apacheの修正フェーズで動作していることを意味し、ここで実際に書き換えを実行するために使用するメカニズムは非常に厄介です。外部リダイレクトがなくても問題が発生したため、少しの間、外部リダイレクトがないと仮定しましょう(リダイレクトの問題については後で説明します)。

リライトを実行した後、モジュールが実際にファイルにマップするには、要求処理が遅すぎます。代わりに、それ自体をリクエストの「コンテンツ」ハンドラとして割り当て、リクエストがそのポイントに到達すると、への呼び出しを実行しますap_internal_redirect()。これにより、元のテーブルのテーブルを含まない新しいリクエストオブジェクトが作成されますheaders_out

それ以上のリダイレクトが発生しないと仮定するとmod_rewrite、応答は新しいリクエストオブジェクトから生成され、適切な(元の)ヘッダーが割り当てられることはありません。サーバーごとのコンテキスト(メイン構成または)で作業することでこれを回避することは可能です<VirtualHost>が、...

リダイレクトの問題

mod_rewrite残念ながら、サーバーコンテキストで使用する場合でも、リダイレクトが発生した場合に応答がたどるパスによって、モジュールが設定したヘッダーが破棄されるため、とにかくほとんど関係がないことがわかります。

リクエストがApacheによって受信されると、一連の関数呼び出しを介してになりap_process_request()ます。これにより、が呼び出されap_process_request_internal()、重要なリクエスト解析ステップの大部分が発生します(の呼び出しを含むmod_rewrite)。整数のステータスコードを返します。リダイレクトの場合は、たまたま301に設定されています。

ほとんどのリクエストOKは(値が0の)戻り、すぐに。になりap_finalize_request_protocol()ます。ただし、ここではそうではありません

if (access_status == OK) {
    ap_finalize_request_protocol(r);
}
else {
    r->status = HTTP_OK;
    ap_die(access_status, r);
}

ap_die()追加の操作(応答コードを301に戻すなど)を行い、この特定のケースでは、の呼び出しで終了しますap_send_error_response()

幸いなことに、これが最終的に問題の根源になります。見た目はそうかもしれませんが、物事は「逆行」ではなく、これにより元のヘッダーが破壊されます。ソースにはそれについてのコメントさえあります:

if (!r->assbackwards) {
    apr_table_t *tmp = r->headers_out;

    /* For all HTTP/1.x responses for which we generate the message,
     * we need to avoid inheriting the "normal status" header fields
     * that may have been set by the request handler before the
     * error or redirect, except for Location on external redirects.
     */
    r->headers_out = r->err_headers_out;
    r->err_headers_out = tmp;
    apr_table_clear(r->err_headers_out);

    if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
        if ((location != NULL) && *location) {
            apr_table_setn(r->headers_out, "Location", location);
        }
        //...
    }
//...
}

r->headers_outが置き換えられ、元のテーブルがクリアされることに注意してください。そのテーブルには、応答に表示されると予想されていたすべての情報が含まれていたため、現在は失われています。

結論

リダイレクトせず、サーバーごとのコンテキストでルールを定義すると、すべてが正しく機能しているように見えます。しかし、これはあなたが望むものではありません。潜在的な回避策はわかりますが、サーバーを再コンパイルする必要があることは言うまでもなく、それが受け入れられるかどうかはわかりません。

についてはVary: Accept-Encoding、ヘッダーがこっそり通過できるように動作する別のモジュールからのものであると推測できます。また、ガンボを試してみても問題がなかった理由もわかりません。

参考までに、2.2.14と2.2のトランクソースコードを見ていて、Apache2.2.15を変更して実行していました。関連するコードセクションのバージョン間には、大きな違いはないようです。

于 2010-09-14T16:55:13.377 に答える
1

回避策として、次のようなことを試してください。

<LocationMatch "^.*lang\=">
    Header onsuccess merge Vary "Accept-Language"
</LocationMatch>
于 2012-02-06T10:37:43.760 に答える