Mazzarolo MatteoMazzarolo Matteo

Creating and deploying a tiny proxy server on Vercel in 10 minutes

By Mazzarolo Matteo


I recently created a tiny proxy server to edit responses of a public API on the fly, and I was impressed by how easy it is to build and deploy such things on Vercel.
In my case, the goal was to allow all origins to fetch the Pinboard API by adding an "Access-Control-Allow-Origin": "*" header to the API response, but there are plenty of other cases where a proxy server may come in handy.

So, here's how you can create and deploy a tiny but flexible Node.js proxy server on Vercel in 10 minutes.

TL;DR

We'll create a Node.js serverless function that uses http-proxy-middleware to proxy any /api request to (e.g.) https://example.org. Within the serverless function code, we can intercept requests/responses and manipulate them on the fly.
The serverless function will be deployed on Vercel.

Project setup

Create a project directory, cd into it, and initialize an npm project:

mkdir my-proxy && cd my-proxy
npm init
mkdir my-proxy && cd my-proxy
npm init

Install vercel as a dependency:

npm install -D vercel
npm install -D vercel

Update the start script of your package.json to "start": "vercel dev" in order to run your serverless function locally:

my-proxy/package.json
 {
   "name": "my-proxy",
   "version": "1.0.0",
   "scripts": {
+    "start": "vercel dev"
   },
my-proxy/package.json
 {
   "name": "my-proxy",
   "version": "1.0.0",
   "scripts": {
+    "start": "vercel dev"
   },

Create an api directory and an index.js file inside of it.

mkdir api && touch api/index.js
mkdir api && touch api/index.js

Vercel serverless functions use a file-system-based convention. So the api/index.js file you just created will automatically handle all requests of the /api endpoint:

my-proxy/api/index.js
// In Vercel, any file inside the "api" directory is exposed on an "/api" endpoint.
 
// For an API route to work, you need to export a function as default (a.k.a request handler),
// which then receives the following parameters:
// - req: The request object.
// - res: The response object.
// See https://vercel.com/docs/serverless-functions/supported-languages#node.js for details.
export default async function handler(req, res) {
  res.status(200).send(`Hello world!`);
}
my-proxy/api/index.js
// In Vercel, any file inside the "api" directory is exposed on an "/api" endpoint.
 
// For an API route to work, you need to export a function as default (a.k.a request handler),
// which then receives the following parameters:
// - req: The request object.
// - res: The response object.
// See https://vercel.com/docs/serverless-functions/supported-languages#node.js for details.
export default async function handler(req, res) {
  res.status(200).send(`Hello world!`);
}

By default, on Vercel, the /api/index.js file would strictly match only requests made to the /api endpoint, ignoring sub-paths like /api/hello.
To make /api/index.js handle the entire path, we can configure a Vercel rewrite to redirect all /api/* calls to the /api/index.js file (by specifying the rewrite rule in a vercel.json file at the root of the project):

touch vercel.json
touch vercel.json
my-proxy/vercel.json
{
  "rewrites": [{ "source": "/api/(.*)", "destination": "/api" }]
}
my-proxy/vercel.json
{
  "rewrites": [{ "source": "/api/(.*)", "destination": "/api" }]
}

You should now be able to deploy your code to Vercel (of course, we haven't added any "real" logic in api/index.js, so it won't do anything for now).
My go-to approach on these occasions is to create a GitHub repo and connect it to Vercel to automatically deploy the project on each commit. But you can also follow the automated setup by running npm start.

Proxy logic setup

Install http-proxy-middleware, an easy-to-use proxy middleware compatible with Vercel:

npm i http-proxy-middleware
npm i http-proxy-middleware

In /api/index.js, use http-proxy-middleware to create a new proxy and expose it on the route handler:

my-proxy/api/index.js
// Create a proxy to redirect requests of the "/api/*" path to "https://example.org".
//
// Examples:
// GET /api/hello → GET https://example.org/hello
// POST /api/test?color=red → POST https://example.org/test?color=red
//
// Additionally, the proxy will:
// - Add an "x-added" header
// - Remove the "x-removed" header
// From the proxied response.
//
// You can/should update the proxy to suit your needs.
// See https://github.com/chimurai/http-proxy-middleware for more details.
const { createProxyMiddleware } = require("http-proxy-middleware");
 
const apiProxy = createProxyMiddleware({
  target: "https://example.org",
  changeOrigin: true,
  pathRewrite: {
    "^/api": "", // strip "/api" from the URL
  },
  onProxyRes(proxyRes) {
    proxyRes.headers["x-added"] = "foobar"; // add new header to response
    delete proxyRes.headers["x-removed"]; // remove header from response
  },
});
 
// Expose the proxy on the "/api/*" endpoint.
export default function (req, res) {
  return apiProxy(req, res);
}
my-proxy/api/index.js
// Create a proxy to redirect requests of the "/api/*" path to "https://example.org".
//
// Examples:
// GET /api/hello → GET https://example.org/hello
// POST /api/test?color=red → POST https://example.org/test?color=red
//
// Additionally, the proxy will:
// - Add an "x-added" header
// - Remove the "x-removed" header
// From the proxied response.
//
// You can/should update the proxy to suit your needs.
// See https://github.com/chimurai/http-proxy-middleware for more details.
const { createProxyMiddleware } = require("http-proxy-middleware");
 
const apiProxy = createProxyMiddleware({
  target: "https://example.org",
  changeOrigin: true,
  pathRewrite: {
    "^/api": "", // strip "/api" from the URL
  },
  onProxyRes(proxyRes) {
    proxyRes.headers["x-added"] = "foobar"; // add new header to response
    delete proxyRes.headers["x-removed"]; // remove header from response
  },
});
 
// Expose the proxy on the "/api/*" endpoint.
export default function (req, res) {
  return apiProxy(req, res);
}

Et voilà!

By deploying your code (or running it locally with npm start) any call made to the /api endpoint will be proxied to https://example.org.

Check the documentation of the http-proxy-middleware library (and of the node-http-proxy library, used under-the-hood) to learn how you can manipulate the proxied request & response.