Vue 及び Vercel でプロキシを設定してCORSを回避する方法 | Vue3
概要
WebアプリケーションにZennの記事を掲載しようと、アプリケーション上からZenn非公式のAPIにアクセスしたところ、CORS制約に引っ掛かり、データを取得できなかった。そこで、Vueとデプロイ環境であるVercelにプロキシを設定し、この制約を回避した。この記事では、プロキシの設定方法と、なぜプロキシでCORSを回避できるのかを説明する。
ファイル構成
root/
├ src/
│ └ App.vue # 今回は関係ない
├ package.json # 今回は関係ない
├ vercel.json # なければ作成する(Vercelの設定ファイル)
└ vue.config.js # なければ作成する(Vueの設定ファイル)
Vue の設定
設定ファイル
const { defineConfig } = require('@vue/cli-service');
module.exports = defineConfig({
devServer: {
proxy: {
'/zenn': {
target: 'https://zenn.dev',
changeOrigin: true,
pathRewrite: { '^/zenn': '' },
},
},
},
});
プログラムコード
const response = await fetch('/zenn/api/articles?username=bbsfish');
const result = await response.json();
このように、通常https://zenn.dev/api/articles?username={username}とするところを、/zenn/api/articles?username={username}に置き換える。
Vercel の設定
設定ファイル
{
"rewrites": [
{
"source": "/zenn/:path*",
"destination": "https://zenn.dev/:path*"
}
]
}
なぜプロキシでCORSを回避できるのか
devServer.proxyを設定すると、ブラウザとAPIサーバーの間の通信フローが劇的に変化し、CORSの問題が回避されます。その仕組みは以下の通りです。
-
Vueコンポーネントからのリクエスト変更:
APIを呼び出すコードを、絶対パスからプロキシのトリガーとなる相対パスに変更します。
変更前: axios.get('https://zenn.dev/articles')
変更後: axios.get('/api/articles') -
ブラウザの挙動:
ブラウザは、/api/articlesへのリクエストを、現在表示しているページと同じオリジン(http://localhost:8080)へのリクエストだと解釈します。これは同一オリジンへのリクエストであるため、同一オリジンポリシーには違反せず、Originヘッダーを付与することなく、通常のリクエストとして送信されます。CORSのチェック機構自体が発動しません。 -
開発サーバー(プロキシ)の役割:
localhost:8080で待機しているwebpack-dev-serverがこのリクエストを受け取ります。サーバーはリクエストパスが/apiで始まることを認識し、vue.config.jsで設定されたプロキシのルールを適用します。 -
サーバー間通信の実行:
開発サーバーは、クライアントであるブラウザに代わって、新しいHTTPリクエストを生成し、targetに指定されたZennのサーバー(https://zenn.dev/articles)に直接送信します。 -
SOPの適用外:
ここで最も重要な点は、この開発サーバーからZennサーバーへのリクエストはサーバー間通信であるということです。ブラウザの同一オリジンポリシーは、ブラウザ内で実行されるスクリプトに課せられる制約であり、サーバー間の通信には適用されません。 -
レスポンスの中継:
Zennのサーバーは開発サーバーからのリクエストに応答します。開発サーバーはそのレスポンスを受け取り、そのまま元のリクエスト元であるVueアプリケーション(ブラウザ)へと中継します。
この一連の流れにより、Vueアプリケーションはあたかも同一オリジンのエンドポイントと通信しているかのように振る舞い、CORSエラーに遭遇することなくAPIからデータを取得できるのです。
Discussion