All Articles

Google Cloud Functions reverse proxy

Google Cloud Load balancer supports url rewrite only as a prefix to the url, so you can transform a request to host/url to host/prefix/url.

For my usecase I’m using a storage bucket as a backend, but if a request comes in to host/urlI would need it to proxy it to host/host/url so that it adds the host as a dynamic prefix to the url.

This is not supported out of the box so as fix I’ve changed the backend to a cloud function which proxies the request to the target bucket.

Embedded from https://gist.github.com/ecyshor/0138ea1f8b0b16b1b7357a936d060b66#proxy.ts

import * as functions from 'firebase-functions';

import * as http from 'http';

const BUCKET_NAME = "target-bucket"

exports.rewriteGCSRequest = functions.https.onRequest((oreq: any, ores: any) => {
    const redirectPath = oreq.originalUrl === '/' ? '/index.html' : oreq.originalUrl
    const options = {
        // host to forward to
        host: 'storage.googleapis.com',
        // port to forward to
        port: 80,
        // path to forward to
        path: `${BUCKET_NAME}/${oreq.hostname}${redirectPath}`,
        // request method
        method: 'GET',
        // headers to send
        headers: oreq.headers,
        agent: httpAgent
    };

    const creq = http
        .request(options, pres => {

            // set http status code based on proxied response
            ores.writeHead(pres.statusCode, pres.headers);

            // wait for data
            pres.on('data', chunk => {
                ores.write(chunk);
            });

            pres.on('close', () => {
                // closed, let's end client request as well
                ores.end();
            });

            pres.on('end', () => {
                // finished, let's finish client request as well
                ores.end();
            });
        })
        .on('error', e => {
            // we got an error
            console.error(e);
            try {
                // attempt to set error message and http status
                ores.writeHead(500);
                ores.write(e.message);
            } catch (error) {
                // ignore
            }
            ores.end();
        });

    creq.end();
})

Check the original code here

The function also proxies all the requests to the root / to index.html, that is optional and can actually be configured directly in the load balancer.

Published Mar 18, 2021

Software engineer.