petitviolet blog

    Provisioning a custom domain on Cloud Run using Firebase Hosting

    2024-03-25

    GCPFirebase

    It used to be that using a load balancer is the most common way to provision a custom domain on Cloud Run. However, it's a bit complicated and costly because it requires a load balancer. In order to have lower running cost, using load balancer should not be the best option.

    For provisioning custom domains on Cloud Run, today we have some options as the official document describes.

    https://cloud.google.com/run/docs/mapping-custom-domains

    how to provision custom domains on cloud run

    This post describe the second option, using Cloud Run integrations feature for Firebase Hosting.

    System Architecture

    Firebase Hosting to serve front-end SPA application built with React, and Cloud Run to serve API.

    User --> Firebase Hosting(Frontend) --> Cloud Run(API)
    

    In terms of security including CORS, it's better to have the same origin for the frontend and the backend as long as possible.

    Domain Setup

    In order to setup a custom domain on Firebase Hosting as well as Cloud Run, you need to add a DNS record to your domain provider as Firebase Hosting requires.

    • "Add Custom Domain" on Firebase Hosting console Firebase Hosting
    • Give A and TXT records to your domain provider cloudflare setup
      • Note that Proxy status should be DNS only for A record to prevent Firebase Hosting fails issuing SSL certificates
    • Wait for a while to get DNS records and SSL certificate to be ready

    API

    Deploy an API to Cloud Run that serves an API under /api path so that Firebase Hosting, L7 load balancer, can proxy HTTP requests to the Run service.

    Use express for example:

    app.ts
    import express from 'express'
    
    const apiRouter = express.Router()
    apiRouter.get(`/`, (req: express.Request, res: express.Response) => {
        res.send('hello, world')
      },
    )
    
    const app: express.Express = express()
    app.use('/api', apiRouter)
    
    app.listen(port, () => {
        console.log(`server started`)
    })
    

    firebase.json

    Let's see firebase.json for how to proxy requests to /api to Cloud Run.

    firebase.json
    {
      "hosting": {
        "public": "build",
        "rewrites": [
          {
            "source": "/api/**",
            "run": {
              "serviceId": "api-service",
              "region": "asia-northeast1"
            }
          },
          {
            "source": "**",
            "destination": "/index.html"
          }
        ]
      }
    }
    

    As you can see, only requests to /api will get proxy-ed to Cloud Run, and other requests will be served by Firebase Hosting.

    That's it!

    Wrap up

    In my understanding, Firebase Hosting rewrite feature can be used to proxy requests to Cloud Run like L7 load balancers do. Thanks to this feature, we can serve api running on Cloud Run and front-end application on Firebase Hosting under a single domain without provisioning load balancers that we need to pay extra for.