ウェブサービスを作っていると、大きなファイルを認証付きでダウンロードさせたいけど、ダウンロード処理自体はアプリケーションサーバーを経由したくない場合があります。
それだけならアプリケーションサーバーから300系のリダイレクトを使えばいいんですが、クライアント側に実体のURLを知られたくない場合もあります。
例えばこんな状況。
- ECサイトでダウンロード商品を扱っていて、ダウンロードファイル自体はアプリケーションサーバーとは別のサーバーにある。
- 商品を購入済みの人にだけダウンロードを許可したいので、商品購入済みかどうかをダウンロード前にアプリケーション側でチェックしたい。
- でもダウンロード処理自体にはアプリケーションサーバーを経由したくない(例えば、ダウンロードのためにPHPのreadfile()とかで別サーバーのファイルを読みたくない)
- かといってアプリケーションサーバーから300系リダイレクトをするとクライアントにファイルの実体のURLが知られてしまうので、リダイレクトはやりたくない(S3の署名付きURLのような上等なものは使えない)
Nginxで解決
色々調べたらNginxでできることがわかりました。とは言ってもNginxに専用の機能があるわけではなく、設定を工夫すればできるという感じです。
前提として、アプリケーションサーバーの前段にNginxがリバースプロキシーとして置いてある状態を想定します。
stack overflowの回答例をどうぞ。
server { ... location / { proxy_pass http://backend; # You may need to uncomment the following line if your redirects are relative, e.g. /foo/bar #proxy_redirect / /; proxy_intercept_errors on; error_page 301 302 307 = @handle_redirects; } location @handle_redirects { set $saved_redirect_location '$upstream_http_location'; proxy_pass $saved_redirect_location; } }
ここで、 http://backend はアプリケーションサーバーを指しています。
アプリケーションサーバーからの300系リダイレクト(301, 302, 307)に対して、Locationヘッダーの値をプロキシー先とみなしてproxy_passに設定し、実体を取りに行きます。なるほどなるほど。Nginxがアプリケーションとクライアントの仲介をすることで、一見矛盾する要望を見事実現しています。頭いいー。
これのいいところは、ダウンロードファイルのあるサーバーはNginxからアクセスさえできればいいので、インターネットに晒す必要がないということです。今回のような要望のあるファイルは不用意にダウンロードされたら困ることが多いので、外部に晒さずに済むのは大きいですね。
あと、クライアントから見たダウンロード用のURL(実際はアプリケーションサーバーのURL)を任意に決められるというメリットもあります。いかにもワンタイムURLですと言わんばかりのゴチャゴチャした文字列がつかず、スッキリとしたURLにできます。だから何だとか言わない。
ちなみに・・・
そもそもNginxを使えるなら、ダウンロードファイルのあるサーバーでngx_http_secure_download_moduleを使えば署名付きURLのようなものを発行できます。標準で入っている機能なので、特別な設定は不要です。
この場合はもちろんサーバーをインターネットに晒す必要があるので、うっかりミスで署名付きURLを無効にしないように。
0 件のコメント:
コメントを投稿