nginx プロキシでの複数の 302 応答の解決策 (nginx Follow 302)

nginx プロキシでの複数の 302 応答の解決策 (nginx Follow 302)

proxy_intercept_errors と recursive_error_pages を使用して複数の 302 をプロキシする

302 は、HTTP プロトコルで頻繁に使用されるステータス コードです。これは複数のリダイレクト方法の 1 つであり、その意味は「一時的に移動」と解釈されることがよくあります。ちなみに、302 は実際には誤用 (303 や 307 と混在) によりよく使用されます。HTTP/1.1 では、そのセマンティクスは「Found」です。

302 は、非常に明白な場合もあれば、非常に隠れている場合もあります。最も単純なケースは、ブラウザに URL A を入力すると、ブラウザのアドレス バーが自動的に B にジャンプし、Web ページが開く場合です。この状況は 302 である可能性が最も高くなります。

Web ページに埋め込まれたプレーヤーでは、より微妙な状況が発生することがよくあります。例えば、Youku の動画再生ページを開くと、パケットをキャプチャして観察すると、302 の影が見つかることが多いです。ただし、これらの URL はブラウザで直接開かれないため、ブラウザのアドレス バーには変更が表示されません。もちろん、これらの特定の URL を具体的に選択してブラウザのアドレス バーにコピーすると、引き続き確認できます。

Youkuについては前の段落で触れました。実際、ほとんどのオンライン ビデオ ウェブサイトは現在 302 を使用しています。その理由は非常に単純です。ビデオ ウェブサイトは一般にトラフィックが多く、CDN を使用します。唯一の違いは、自分で構築した CDN を使用するか、商用 CDN を使用するかです。また、302 のリダイレクト セマンティクス (繰り返しますが、302 のセマンティクスは広く誤用されています。302 を使用する場合は、おそらく 303 または 307 を使用する必要がありますが、これについては後で詳しく説明しません) により、CDN のスケジュールとうまく組み合わせることができます。

例を見てみましょう。NetEase のビデオ再生ページを開き、パッケージを取得して、302 ステータスの URL を見つけます。例えば:

http://flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4

これをブラウザのアドレス バーにコピーすると、アドレス バーがすぐに別の URL に変わります。この URL は不確かで、次のようなものである可能性があります。

http://14.18.140.83/f6c00af500000000-1408987545-236096587/data6/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4

curl ツールを使用すると、プロセス全体がより明確に表示されます。

カール -I "http://flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4" -L
HTTP/1.1 302 一時的に移動しました 
サーバー: nginx 
日付: 2014 年 8 月 25 日 (月) 14:49:43 GMT 
コンテンツタイプ: text/html 
コンテンツの長さ: 154 
接続: キープアライブ 
NG: CCN-SW-1-5L2 
X-Mod名: GSLB/3.1.0 
場所: http://119.134.254.9/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4 

HTTP/1.1 302 一時的に移動しました 
サーバー: nginx 
日付: 2014 年 8 月 25 日 (月) 14:49:41 GMT 
コンテンツタイプ: text/html 
コンテンツの長さ: 154 
接続: キープアライブ 
X-Mod-Name: Mvod-Server/4.3.3 
場所: http://119.134.254.7/cc89fdac00000000-1408983581-2095617481/data4/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4 
NG: CHN-SW-1-3Y1 

HTTP/1.1 200 OK 
サーバー: nginx 
日付: 2014 年 8 月 25 日 (月) 14:49:41 GMT 
コンテンツタイプ: video/mp4 
コンテンツの長さ: 3706468 
最終更新日: 2014年8月25日 (月) 00:23:50 GMT 
接続: キープアライブ 
キャッシュ制御: キャッシュなし 
ETag: "53fa8216-388e64" 
NG: CHN-SW-1-3g6 
X-Mod-Name: Mvod-Server/4.3.3 
受け入れ範囲: バイト

ご覧のとおり、プロセス中に 302 エラーが 2 つ発生しました。

この例は今は置いておいて、別の重要な用語である「プロキシ」について話しましょう。リーダーの中には 302 タイプのリーダーもいれば、プロキシ タイプのリーダーもいるとよく冗談で言われます。 302 タイプのリーダーは、タスクが自分の手に渡るとすぐにそれを他の人に引き継ぎますが、プロキシ タイプのリーダーはタスクに参加し、すべてを完了します。

上記の例に戻ると、URL にアクセスするときに複数の 302 がある場合、これらすべての 302 を途中で非表示にする Nginx を使用したプロキシを設計する必要がある場合はどうすればよいでしょうか。

1. オリジナルプロキシ

Nginx 自体は優れたプロキシ サーバーであることはわかっています。したがって、まず、サーバー IP が 192.168.109.128 (テスト仮想マシンの 1 つ) である Nginx フォワード プロキシを設定しましょう。

初期設定は次のように簡略化されます。

サーバー{
    聞く 80;
    位置 / {
        rewrite_by_lua '
            ngx.exec("/proxy-to" .. ngx.var.request_uri)
        ';
    }

    場所 ~ /proxy-to/([^/]+)(.*) {
        proxy_pass http://$1$2$is_args$クエリ文字列;

    }
}

達成される機能は、

http://192.168.109.128/xxxxxx

プロキシにアクセスすると、xxxxxx で表される実際のサーバーにプロキシされます。

テスト結果は次のとおりです。

カール -I "http://192.168.109.128/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4" -L
HTTP/1.1 302 一時的に移動しました 
サーバー: nginx/1.4.6 
日付: 2014 年 8 月 25 日 (月) 14:50:54 GMT 
コンテンツタイプ: text/html 
コンテンツの長さ: 154 
接続: キープアライブ 
NG: CCN-SW-1-5L2 
X-Mod名: GSLB/3.1.0 
場所: http://183.61.140.24/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4 

HTTP/1.1 302 一時的に移動しました 
サーバー: nginx 
日付: 2014 年 8 月 25 日 (月) 14:50:55 GMT 
コンテンツタイプ: text/html 
コンテンツの長さ: 154 
接続: キープアライブ 
X-Mod-Name: Mvod-Server/4.3.3 
場所: http://183.61.140.20/540966e500000000-1408983655-236096587/data1/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4 
NG: CHN-ZJ-4-3M4 

HTTP/1.1 200 OK 
サーバー: nginx 
日付: 2014 年 8 月 25 日 (月) 14:50:55 GMT 
コンテンツタイプ: video/mp4 
コンテンツの長さ: 3706468 
最終更新日: 2014年8月25日 (月) 00:31:03 GMT 
接続: キープアライブ 
キャッシュ制御: キャッシュなし 
ETag: "53fa83c7-388e64" 
NG: CHN-ZJ-4-3M4 
X-Mod-Name: Mvod-Server/4.3.3 
受け入れ範囲: バイト

プロキシが使用されているにもかかわらず、プロセスは元のアクセスと変わらないことがわかります。アクセスプロセスは次のとおりです。

http://192.168.109.128/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4

Nginxがリクエストをプロキシすると

http://flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4

後者はすぐに 302 を返すため、プロキシとしての Nginx は 302 をクライアントに返し、クライアントはリクエストを再開して、前の 302 を繰り返します。これは問題を示しています。Nginx のプロキシのバックエンドが 302 を返すと、クライアントは Nginx プロキシから切断され、Nginx は完全なプロキシの役割を果たすことができなくなります。

2. 最初の改訂

構成ファイルを次のように変更します。

サーバー{
    聞く 80;
    位置 / {
        rewrite_by_lua '
            ngx.exec("/proxy-to" .. ngx.var.request_uri)
        ';
    }

    場所 ~ /proxy-to/([^/]+)(.*) {
        proxy_pass http://$1$2$is_args$クエリ文字列;
        エラーページ 302 = @error_page_302;

    }
    場所 @error_page_302 {
        rewrite_by_lua '
            ローカル _, _, アップストリーム http_location = string.find(ngx.var.upstream_http_location, "^http:/(.*)$")
            ngx.header["zzzz"] = "/proxy-to" .. アップストリームhttpの場所
            ngx.exec("/proxy-to" ..upstream_http_location);
        ';

    }
}

上記との違いは、error_page が使用されていることです。その目的は、プロキシ バックエンドが 302 を返すことがわかった場合、この 302 の宛先場所をクライアントに直接返すのではなく、プロキシに継続することです。そして、このロジックには再帰の意味が含まれており、最終的に 200 のアドレスに戻るまで 302 を追跡します。テスト結果は次のとおりです。

カール -I "http://192.168.109.128/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4" -L
HTTP/1.1 302 一時的に移動しました 
サーバー: nginx/1.4.6 
日付: 2014 年 8 月 25 日 (月) 15:01:17 GMT 
コンテンツタイプ: text/html 
コンテンツの長さ: 154 
接続: キープアライブ 
NG: CCN-SW-1-5L2 
X-Mod名: GSLB/3.1.0 
場所: http://183.61.140.24/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4 

HTTP/1.1 302 一時的に移動しました 
サーバー: nginx 
日付: 2014 年 8 月 25 日 (月) 15:01:17 GMT 
コンテンツタイプ: text/html 
コンテンツの長さ: 154 
接続: キープアライブ 
X-Mod-Name: Mvod-Server/4.3.3 
場所: http://183.61.140.20/a90a952900000000-1408984277-236096587/data1/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4 
NG: CHN-ZJ-4-3M4 

HTTP/1.1 200 OK 
サーバー: nginx 
日付: 2014 年 8 月 25 日 (月) 15:01:17 GMT 
コンテンツタイプ: video/mp4 
コンテンツの長さ: 3706468 
最終更新日: 2014年8月25日 (月) 00:31:03 GMT 
接続: キープアライブ 
キャッシュ制御: キャッシュなし 
ETag: "53fa83c7-388e64" 
NG: CHN-ZJ-4-3M4 
X-Mod-Name: Mvod-Server/4.3.3 
受け入れ範囲: バイト

この変更はまだ成功していないことがわかります。

なぜ?分析の結果、@error_page_302 の場所にヘッダー印刷ステートメントを追加しましたが、テストではヘッダーが印刷されず、プロセスが @error_page_302 の場所に入っていないことがわかります。

その理由は

エラーページ 302 = @error_page_302;

デフォルトでは、error_page はこのプロセスの戻りコードです。プロキシとして、この処理では、上流サーバーの応答が正常に転送されている限り、ステータス コードは 200 になるはずです。つまり、実際に確認する必要があるのは、プロキシ自体によって返されるステータス コードではなく、プロキシのバックエンド サーバーによって返されるステータス コードです。 Nginx wiki を見ると、proxy_intercept_errors ディレクティブはまさにこれを実行します。

構文: proxy_intercept_errors on | off;
デフォルト:  
proxy_intercept_errors をオフ;
コンテキスト: http、サーバー、場所
300 以上のコードを持つプロキシ応答をクライアントに渡すか、error_page ディレクティブを使用して処理するために nginx にリダイレクトするかを決定します。

3. 2回目の改訂

サーバー{
    聞く 80;
    proxy_intercept_errors がオン;
    位置 / {
        rewrite_by_lua '
            ngx.exec("/proxy-to" .. ngx.var.request_uri)
        ';
    }
    場所 ~ /proxy-to/([^/]+)(.*) {
        proxy_pass http://$1$2$is_args$クエリ文字列;
        エラーページ 302 = @error_page_302;

    }
    場所 @error_page_302 {
        rewrite_by_lua '
            ローカル _, _, アップストリーム http_location = string.find(ngx.var.upstream_http_location, "^http:/(.*)$")
            ngx.header["zzzz"] = "/proxy-to" .. アップストリームhttpの場所
            ngx.exec("/proxy-to" ..upstream_http_location);
        ';
    }
}

以前の変更と比較すると、唯一の違いは proxy_intercept_errors ディレクティブが追加されたことです。テスト結果は次のとおりです。

カール -I "http://192.168.109.128/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4" -L 
HTTP/1.1 302 一時的に移動しました
サーバー: nginx/1.4.6
日付: 2014 年 8 月 25 日 (月) 15:05:54 GMT
コンテンツタイプ: text/html
コンテンツの長さ: 160
接続: キープアライブ
zzzz: /proxy-to/183.61.140.24/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4

今回はさらに魔法のようです。302 ステータスを直接返して、それ以上ジャンプしません。

問題は、最初の 302 リクエストは @error_page_302 に正常に入力されるものの、後続の error_page 命令が機能しないことです。つまり、error_page はバックエンドから最初に返されたステータス コードのみをチェックし、後続のバックエンド ステータス コードはチェックし続けます。

情報を確認します。このとき、別のコマンド recursive_error_pages が役立ちます。

4. 第三次改訂

サーバー{
    聞く 80;
    proxy_intercept_errors がオン;
    recursive_error_pages オン;
    位置 / {
        rewrite_by_lua '
            ngx.exec("/proxy-to" .. ngx.var.request_uri)
        ';
    }
    場所 ~ /proxy-to/([^/]+)(.*) {
        proxy_pass http://$1$2$is_args$クエリ文字列;
        エラーページ 302 = @error_page_302;

    }
    場所 @error_page_302 {
        rewrite_by_lua '
            ローカル _, _, アップストリーム http_location = string.find(ngx.var.upstream_http_location, "^http:/(.*)$")
            ngx.header["zzzz"] = "/proxy-to" .. アップストリームhttpの場所
            ngx.exec("/proxy-to" ..upstream_http_location);
        ';
    }
}

前回と比較すると、recursive_error_pages on 命令のみが追加されました。テスト結果は次のとおりです。

カール -I "http://192.168.109.128/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4" -L 
HTTP/1.1 200 OK 
サーバー: nginx/1.4.6 
日付: 2014 年 8 月 25 日 (月) 15:09:04 GMT 
コンテンツタイプ: video/mp4 
コンテンツの長さ: 3706468 
接続: キープアライブ 
zzzz: /proxy-to/14.18.140.83/f48bad0100000000-1408984745-236096587/data6/flv.bn.netease.com/tvmrepo/2014/8/5/P/EA3I1J05P/SD/EA3I1J05P-mobile.mp4 
最終更新日: 2014年8月25日 (月) 00:21:07 GMT 
キャッシュ制御: キャッシュなし 
ETag: "53fa8173-388e64" 
NG: CHN-MM-4-3FE 
X-Mod-Name: Mvod-Server/4.3.3 
受け入れ範囲: バイト

Nginx が最終的に 200 を正常に返したことがわかります。この時点で、Nginx は実際にプロキシの役割を果たし、リクエストの複数の 302 リンクを隠し、クライアントに 1 つの最終結果のみを返します。

5. まとめ

要約すると、proxy_pass、error_page、proxy_intercept_errors、および recursive_error_pages 命令を一緒に使用することで、クライアントからのリクエストのリダイレクトの詳細を隠し、ステータス コード 200 の最終結果をユーザーに直接返すことができます。

以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • Nginxリバースプロキシに基づく実IP取得の問題の詳細な説明
  • nginx サーバーの IP とトラフィック統計を取得する Python 実装例
  • CDN アクセラレーションを使用する際にユーザーの IP を取得するように Nginx を構成する方法の詳細な説明
  • Nginx 仮想ホスト (IP ベース) を構成する 3 つの方法の詳細な説明
  • nginx を使用して同じドメイン名で複数の Vue プロジェクトをデプロイし、リバース プロキシを使用する方法
  • nginxリバースプロキシサービスは、設定ファイルのエラーによりリソースにアクセスするときに404エラーを引き起こします。
  • nginx が複数のプロキシ層を通過して実際の送信元 IP を取得するプロセスの詳細な説明

<<:  JavaScript ステートメントの一般的な for ループの詳細な説明

>>:  シェルでパスワードなしでMySQLデータベースに素早くログインする方法

推薦する

4つのReactコンポーネントにおけるDOMスタイル設定の詳細な説明

1. インラインスタイル仮想DOMにインラインスタイルを追加するには、式を使用してスタイルオブジェク...

mysql5.6 の無効な utf8 設定の問題を解決する

mysql5.6 のグリーン バージョンを解凍すると、my-default.ini ファイルが作成さ...

数千万のデータを扱うMySQLのページングクエリのパフォーマンスを最適化する

MySQL のデータ量が多い場合、制限ページングが使用されます。ページ数が増えると、クエリの効率が低...

Element UI で自動サイズ調整テキストエリアの高さを設定する方法

Element UIのtextarea input自動サイズに設定すると、テキストボックスのデフォル...

Linux での MySQL のアンインストールとインストールのグラフィック チュートリアル

ブログを書くのは初めてです。開発に携わって2年になります。仕事の後に何か有意義なことを見つけたいと思...

曇り空のアイコン効果を実現する純粋な CSS

効果効果は以下のとおりです​実装のアイデアbox-shadow プロパティを使用して、複数の灰色の円...

divとspanの違いと使い方

目次1. DIVとSPANの違いと特徴2. スパンタグの概要3. 拡大と改善4. 凡例の効果の実証例...

k8s に ingress-nginx をデプロイする手順

目次序文1. Ingressの展開と構成2. httpsを使用する序文k8sクラスタサービスがデプロ...

WeChatアプレット学習ノート: ページ構成とルーティング

最近、小さなプログラムの開発を勉強して見直しており、学習結果のいくつかをメモしています。公式の We...

CocosCreator 入門チュートリアル: TS で初めてのゲームを作る

目次前提TypeScript と JavaScriptコードエディタの選択TypeScriptを学ぶ...

JS ES6 非同期ソリューション

目次最初にコールバック関数を使用するes6 非同期処理モデルこの非同期モデルに合わせたAPI: pr...

mysql ローカルログインでポート番号を使用してログインできない問題の解決策

最近、Linux を使用してローカルにログインしていたところ、正常にログインできず、次のエラー メッ...

Linux でのマルチスレッドおよびマルチプロセス クラッシュのシミュレーションに関する簡単な説明

結論:マルチスレッド環境では、スレッドの 1 つがクラッシュすると、他のスレッド (プロセス全体) ...

完全なMySQL学習ノート

目次MyISAM と InnoDBパフォーマンスの低下と SQL の速度低下の理由: MySQL 実...

Vue は、デスクトップから Web ページにファイルをドラッグするためのサンプル コードを実装します (画像/オーディオ/ビデオを表示できます)

効果使用する場合は、コードとスタイルを自分で最適化してください。画像を表示しない/ビデオとオーディオ...