ViteでDevサーバーのProxyオプションを高度に操作する方法


※ 当ページには【広告/PR】を含む場合があります。
2024/12/26
開発中のSvelte/SvelteKitのDevサーバーのCORS対策
蛸壺の技術ブログ|ViteでDevサーバーのProxyオプションを高度に操作する方法

前回はViteのProxyサーバー設定から、基礎的なDevサーバーへのCORS対策を説明していました。

合同会社タコスキングダム|蛸壺の技術ブログ
開発中のSvelte/SvelteKitのDevサーバーのCORS対策

Svelte&ViteクライアントアプリのDevサーバーのCORS設定

そちらの記事では、
rewriteメソッドからURLパスを置き換えたり、targetはURL固定だったり、独自設定の自由度に制約があり、場合によってはアクセス先のサブドメイン名を柔軟に切り替えることが困難になります。

今回は、
rewriteメソッドよりももっと高度にCORS対応を実現するための、プラスアルファなテクニックを紹介します。


合同会社タコスキングダム|蛸壺の技術ブログJavascript(js)&Typescript(ts)プログラミング入門〜これから学ぶ人のためのおすすめ書籍&教材の手引き

超JavaScript 完全ガイド 2024

ProxyOptionsを使いこなす

ViteのProxyオプションの実装は以下のリンク先のコードで確認できます。

参照先|proxy.ts

この中では、いくつかの関数型が
HttpProxy.ServerOptionsから拡張できるように定義されています。

ちなみに大本のプロキシサーバーのオプションに関しては、拡張元の
HttpProxy.ServerOptionsを参照してください。

            
            //...

export interface ProxyOptions extends HttpProxy.ServerOptions {
  /**
   * rewrite path
   */
  rewrite?: (path: string) => string
  /**
   * configure the proxy server (e.g. listen to events)
   */
  configure?: (proxy: HttpProxy.Server, options: ProxyOptions) => void
  /**
   * webpack-dev-server style bypass function
   */
  bypass?: (
    req: http.IncomingMessage,
    /** undefined for WebSocket upgrade requests */
    res: http.ServerResponse | undefined,
    options: ProxyOptions,
  ) => void | null | undefined | false | string
  /**
   * rewrite the Origin header of a WebSocket request to match the target
   *
   * **Exercise caution as rewriting the Origin can leave the proxying open to [CSRF attacks](https://owasp.org/www-community/attacks/csrf).**
   */
  rewriteWsOrigin?: boolean | undefined
}
        
さて、rewriteに関してはViteのリファレンス公式でも利用方法がいくつか紹介されていますが、configurebypassはほとんど言及はされていません。

場合によっては開発環境で高度なhostの書き換えや、リクエスト/レスポンスの処理などを挟みたいことがあります。

configurebypassのどちらを使うかは迷うところではありますが、プロキシサーバーの内部処理を伴うのであれば、主にconfigureを、外部とのHTTPリクエスト・レスポンスに修正を加えたいのであればbypassを使うと良いでしょう。

configureメソッド

configureはプロキシサーバーの内部構成を再設定する場合に有効です。

最初の引数にて、プロキシサーバーインスタンス、2つ目の引数でそのプロキシオプションがそれぞれ参照されています。

例えばサーバーにエラーハンドリングを独自に挟みたいときなど、この
configureメソッドが利用できます。

            
            //...

// Listen for the `error` event on `proxy`.
proxy.on('error', function (err, req, res) {
    res.writeHead(500, {
      'Content-Type': 'text/plain'
    });
    res.end('Something went wrong. And we are reporting a custom error message.');
});


// Listen for the `proxyRes` event on `proxy`.
proxy.on('proxyRes', function (proxyRes, req, res) {
    console.log('RAW Response from the target', JSON.stringify(proxyRes.headers, true, 2));
});

// Listen for the `open` event on `proxy`.
proxy.on('open', function (proxySocket) {
    // listen for messages coming FROM the target here
    proxySocket.on('data', hybiParseAndLogMessage);
});

// Listen for the `close` event on `proxy`.
proxy.on('close', function (res, socket, head) {
    // view disconnected websocket connections
    console.log('Client disconnected');
});
        
のように利用できます。

Vite側への実装の一例として、

            
            import { defineConfig } from 'vite';

//...

export default defineConfig({
    //...
    server: {
        proxy: {
            '^/s3/.*': {
                changeOrigin: true,
                configure: (proxy, options) => {
                    //👇プロキシサーバーへイベントリスナー追加
                    proxy.on('proxyRes', (proxyRes, req, res) => {
                        console.log('RAW Response from the target:', JSON.stringify(proxyRes.headers));
                    });
                    //👇rewriteメソッドを再定義
                    options.rewrite = (path) => {
                        const _match = path.match(/^\/s3\/(.+)\/(.+)\/.*/m);
                        let _region = '', _bucket = '';
                        if (_match) {
                            _region = _match[1]??'';
                            _bucket = _match[2]??'';
                        }
                        //👇アクセス先となるターゲット自体を書き換え
                        options.target = `https://${_bucket}.s3.${_region}.amazonaws.com`;
                        return path.replace(`/s3/${_region}/${_bucket}`, '')
                    }
                }
            }
        }
    },
    //...
})
        
というように書き換えることで、例えばDevサーバーの/s3/hoge-piyo/us-east-1にアクセスした際に、https://hoge-piyo.s3.us-east-1.amazonaws.comへ置き換え、かつ、proxyResイベントにより返ってきたレスポンスに対して処理をおこなうことができます。

bypassメソッド

もう一つ
bypassについて考えてみましょう。

このメソッドは外部へのリクエスト、もしくは外部からのレスポンスに対して何らかの操作を行いたい場合に利用します。

例えばホスト名ならびに特定のHTTPヘッダーも書き換えたい場合、

            
            import { defineConfig } from 'vite';

//...

export default defineConfig({
    //...
    server: {
        proxy: {
            '^/s3/.*': {
                changeOrigin: true,
                bypass: (req, res, proxyOptions) => {
                    const _match = req.url.match(/^\/s3\/(.+)\/(.+)\/.*/m);
                    let _region = '', _bucket = '';
                    if (_match) {
                        _region = _match[1]??'';
                        _bucket = _match[2]??'';
                    }
                    proxyOptions.target = req.headers['host'] = `https://${_bucket}.s3.${_region}.amazonaws.com`;
                    req.headers['date'] = (new Date()).toUTCString();
                }
            }
        }
    },
    //...
})
        
とすると、Devサーバーの/s3/hoge-piyo/us-east-1にアクセスした際に、targetとHTTPリクエストのHostDateヘッダーを書き換えることができます。


合同会社タコスキングダム|蛸壺の技術ブログJavascript(js)&Typescript(ts)プログラミング入門〜これから学ぶ人のためのおすすめ書籍&教材の手引き

超JavaScript 完全ガイド 2024

まとめ

今回もViteのProxyサーバーのオプションについてちょっとだけ深堀した内容を説明してきました。

この辺はDevサーバーの話なので、プロダクトでの挙動とは直接関係しないものの、Viteによる快適な開発環境を整えるためにはしっかり理解しておきたいポイントになっています。

参考サイト

server.proxy - Vite

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。

合同会社タコスキングダム|蛸壺の技術ブログJavascript(js)&Typescript(ts)プログラミング入門〜これから学ぶ人のためのおすすめ書籍&教材の手引き