リバースプロキシとは
Reactを扱うAIのv0にはまりBlueSkyの画像検索サイトを作っていました。
気に入ったものはボタンを押すとすぐに画像がダウンロードできるという仕様にしたのですがBlueSkyの画像のURLをfetchするとどうにもエラーがでてしまいます。
function imagedownload(image_url) {
fetch(image_url)
.then(response => response.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.style.display = 'none'
a.href = url
a.download = "image.jpg"
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
})
.catch(() => console.error('download error'))
}
' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
例えば以下のURLでこの関数を使うとエラーになります。
https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:2et4xgrlwcqymiyjpvdv4yle/bafkreia7fdlsuaob47fjxy2umdnah6iuiqfy7wew6gg7ampwptwch6ughu@jpeg
React初心者殺しのCORS policyエラー。
私はCORSを理解してませんし、理解できる気がしません。(偉そうにいうことではないですが)
以前X版を作っていたときは問題なかったのでもうこれはcdn.bskyのせいと決めつけました。
このエラーの解消は諦め、サーバーを立ててそこ経由で取得ができるんじゃない?
と調べていたらそれがリバースプロキシでした。
浅知恵の猿知恵でしたが試したところ上手く行きましたので記念に紹介したいと思います。
OSはAlpine Linux(Cloud RunにデプロイするDocker内)
WebサーバアプリはNginxです。
Nginx Configファイル作成
最初にリバースプロキシを行うためのNginxのConfigファイルを用意しておきます。
なぜNginxかは触ったことがあったからでして、apacheでもできると思います。
設定方法はこちらを参考にしました。
今回はこのような設定にしました。※ファイル名は拡張子が.confならなんでもよいです
server {
listen 8080;
server_name _;
location / {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";
add_header Access-Control-Allow-Credentials true;
rewrite /(.*) /$1 break;
proxy_pass https://cdn.bsky.app/;
}
}
add_headerはリバースプロキシと関係ありません。(私のやりたかったCORSエラー回避のため追加しています。)関係あるのはrewriteとproxy_passだけです。
rewriteで正規表現を使いマッチしたURLを書き換えることができ、proxy_passは転送先となります。
今回は自分宛(リバースプロキシサーバ、以下代理サーバとします)に来たリクエストを
"https://(代理サーバのホスト名)/img/~~~.jpeg"
↓
"https://cdn.bsky.app/img/~~~.jpeg"
と変換することを目的としています。
rewrite /(.*)とすることで「/img/~~~.jpeg」がマッチ。それを$1で受け取ることができ、proxy_passに設定した https://cdn.bsky.app/ につなげて転送できるといった感じです。
今回はホスト名以下をまるごと使うのでこうしましたが、/img/以下だけを使いたい場合は「rewrite /img/(.*) /$1 break;」とすればマッチした部分だけ利用することができます。
意図した通り動くかはこの後のDockerで確認しながら作成した方がよさそうです。
※proxy_passに"/"を含むかどうかで変わるなど、かなり緻密な点があります
Cloud Run用 Dockerfile作成
この設定ファイルを基にどこかにNginxが動くサーバーが必要となります。
今回はCloud Runを利用することにしました。
なぜCloud Runのメリットはドメインの用意が不要なことです。Compute Engineなどの仮想サーバではドメイン設定をしないとIPむき出しの接続となり、それはそれで問題になるかと思います。
ドメイン不要がどういうことかはこの後見て頂けたらと思います。
Cloud Runを利用するまでは以下を参考にさせて頂きました。
流れとしましてはCloud Buildでコンテナイメージを登録しておき、それを基にCloud Runを作成するような感じとなります。
ということでまずDockerファイルをこしらえます。
FROM nginx:stable-alpine3.20
ADD proxy_test.conf /etc/nginx/conf.d/
RUN rm /etc/nginx/conf.d/default.conf
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]
ベースイメージはnginx(タグは無くても大丈夫だと思います。)にして、先ほど作成したconfigファイルを「/etc/nginx/conf.d/」下に置きデフォルトのconfigファイルを削除しています。※ベースイメージのLinuxの種類によって異なる可能性があります
ポートはCloud Runのデフォルトの8080にしておき、Dockerでnginxを動かす際のお約束CMD ["nginx", "-g", "daemon off;"] としておきます。
あとはこれをデプロイ。Cloud Runを使うまでにあるとおり最初にCloud Buildにイメージをビルドします。先ほどのDockerファイルがあるフォルダからpowershellで以下を実行します。
$PROJECT_ID="rational-oasis-XXXXXXXX" # 自身のプロジェクトID
gcloud config set project $PROJECT_ID
gcloud builds submit --tag gcr.io/$PROJECT_ID/proxy_test # proxy_testは適当です
あとはCloud Runにデプロイするだけです。このままコマンドでできるようですが、私はコンソールから操作します。
コンテナイメージのURLはgcr.io/(プロジェクト名)/(ビルド名)です。サービス名は適当です。
インスタンスの最小数が0の場合、起動に時間がかかります。ただこれを1以上にすると料金が変わるので注意してください。
成功しましたらエンドポイントURLが出てきます。これが最初に言っていたドメインが不要ということです。
https://cdn.bsky.app を発行された https://proxy-test-1043149056745.asia-northeast1.run.app に置き換えれば同じ画像を表示することができるようになります。
※わかりづらいですが下の画像を上のURLからも表示できるようになるということです
おわりに
やっていて 「これってもしや違法なんじゃ…」 と思い "リバースプロキシ 違法" で検索すると「漫画村」が出てきました😅
「こんなことできるんだ!」と無邪気に使っていたら違法になるかもしれないので注意してください笑