Skip to content

Instantly share code, notes, and snippets.

@xfournet
Last active April 4, 2025 19:40
Show Gist options
  • Save xfournet/068592b3d1ddd488427b874b23f707bf to your computer and use it in GitHub Desktop.
Save xfournet/068592b3d1ddd488427b874b23f707bf to your computer and use it in GitHub Desktop.
Vite support for HTTP2 and proxy
import proxy from 'http2-proxy';
import type { Plugin, ProxyOptions } from 'vite';
export const pluginHttp2Proxy = (): Plugin => {
let routes: Record<string, string | ProxyOptions>;
return {
name: 'vite-plugin-http2-proxy',
config: (config) => {
const { server } = config;
routes = server?.proxy ?? {};
if (server) {
server.proxy = undefined;
}
return config;
},
configureServer: ({ config: { logger }, middlewares }) => {
Object.entries(routes).forEach(([route, target]) => {
if (typeof target !== 'string') {
throw new Error('ProxyOptions target are not supported yet, only string target are currently supported');
}
const { protocol, hostname, port } = new URL(target);
const options = {
protocol: protocol as 'http' | 'https',
hostname,
port: Number(port),
proxyTimeout: 60000,
};
middlewares.use(route, (req, res) => {
proxy.web(req, res, { ...options, path: req.originalUrl }, (err) => {
if (err) {
logger.error(`[http2-proxy] Error when proxying request on '${req.originalUrl}'`, { timestamp: true, error: err });
}
});
});
});
},
};
};
@xfournet
Copy link
Author

Tested with Vite 5 (should probably work with Vite 4 too ?)
ProxyOptions not implemented.

Configure the Vite proxy as usual and just add the plugin in the vite configuration eg:

export default defineConfig({
  plugins: [
    ....
    pluginHttp2Proxy(),
  ],
  ....
  server: {
    ....
    proxy: {
      '/api': 'https://localhost:8080/api',
    },
  },
});

@sebinsua
Copy link

Thank you for this! ❤️

@zybieku
Copy link

zybieku commented Mar 5, 2024

Thank you for this! ❤️

@A2E9
Copy link

A2E9 commented Apr 4, 2024

i get :
error when starting dev server:
Error: Not supported yet
/vite.config.js.timestamp-1712238134948-ef04cf446917a.mjs:114:17
at Array.forEach ()
with:
"http2-proxy": "^5.0.53",
"vite": "^5.2.0"

@xfournet
Copy link
Author

xfournet commented Apr 4, 2024

@A2E9 yes, ProxyOptions are not implemented. Can you share your proxy config ?

@nik-prostok
Copy link

nik-prostok commented May 7, 2024

@A2E9 yes, ProxyOptions are not implemented. Can you share your proxy config ?

here is my proxy config:

'/123': { target: '/456', secure: false, rewrite: (e) => e.replace(/\/123/, '/'), configure: (proxy) => proxy.on('proxyReq', (proxyReq) => { proxyReq.setHeader('Cookie', proxyReq.getHeader('auth') || ''); }), },

@c1aphas
Copy link

c1aphas commented May 8, 2024

Here is my version with rewrite and configure support.
Note that you should update your proxy config in configure() sections

proxy.on('proxyRes', (proxyRes) => {...} => proxy.onRes = async (req, res, proxyRes) => {...}

import type { Http2ServerRequest } from 'http2';
import type { Plugin, ProxyOptions } from 'vite';
import http2Proxy, { type http2WebOptions as Http2WebOptions } from 'http2-proxy';

const getOptions = (proxyOptions: string | ProxyOptions) => {
    if (typeof proxyOptions !== 'string') {
        const { protocol, hostname, port } = new URL(proxyOptions.target);
        const { proxyTimeout } = proxyOptions;

        return {
            protocol: protocol as 'https',
            hostname,
            port: Number(port),
            proxyTimeout,
            rejectUnauthorized: false,
        };
    }
    const { protocol, hostname, port } = new URL(proxyOptions);

    return {
        protocol: protocol as 'https',
        hostname,
        port: Number(port),
        proxyTimeout: 60000,
        rejectUnauthorized: false,
    };
};

export default (): Plugin => {
    let routes: Record<string, string | ProxyOptions>;

    return {
        name: 'vite-plugin-http2-proxy',
        config: (config) => {
            const { server } = config;
            routes = server?.proxy ?? {};

            if (server) {
                server.proxy = undefined;
            }

            return config;
        },
        configureServer: ({ config: { logger }, middlewares }) => {
            Object.entries(routes).forEach(([route, proxyOptions]) => {
                const options = getOptions(proxyOptions);
                middlewares.use(route, (req, res) => {
                    const http2Options: Http2WebOptions = { ...options };

                    if (typeof proxyOptions !== 'string') {
                        if (proxyOptions.rewrite) {
                            http2Options.path = proxyOptions.rewrite(req.originalUrl);
                        } else {
                            http2Options.path = req.originalUrl;
                        }

                        if (proxyOptions.configure) {
                            proxyOptions.configure(http2Options, proxyOptions);
                        }
                    }

                    http2Proxy.web(req as Http2ServerRequest, res, http2Options, (err) => {
                        if (err) {
                            logger.error(`[http2-proxy] Error when proxying request on '${req.originalUrl}'`, {
                                timestamp: true,
                                error: err,
                            });
                        }
                    });
                });
            });
        },
    };
};

@nik-prostok
Copy link

nik-prostok commented May 8, 2024

@c1aphas I didn’t quite understand what configure should look like...

[vite] Internal server error: proxy.on is not a function

@c1aphas
Copy link

c1aphas commented May 8, 2024

@nik-prostok

configure(proxy) {
  // before
  // proxy.on('proxyRes', (proxyRes) => {...} 

  // now
  proxy.onRes = async (req, res, proxyRes) => {...}
}

@mjgerace
Copy link

mjgerace commented Apr 4, 2025

Here is my version with rewrite and configure support. Note that you should update your proxy config in configure() sections

proxy.on('proxyRes', (proxyRes) => {...} => proxy.onRes = async (req, res, proxyRes) => {...}

import type { Http2ServerRequest } from 'http2';
import type { Plugin, ProxyOptions } from 'vite';
import http2Proxy, { type http2WebOptions as Http2WebOptions } from 'http2-proxy';
....

This doesn't seem to work well with graphql subscriptions (stream types). Any insight as to why?

@sebinsua
Copy link

sebinsua commented Apr 4, 2025

^ Are there any error messages or strange behaviours that you've noticed?

@mjgerace
Copy link

mjgerace commented Apr 4, 2025

^ Are there any error messages or strange behaviours that you've noticed?

Hi Seb,

It says "proxy timeout". I tried to increase the timeout limit with no success. We are using server-side events, so its an http stream call that is open for a long time. It only does this on our http streams.

Appreciate the help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment