Serve Dynamic Content with Cloud Functions

Firebase Hosting allows you to use Cloud Functions to perform server-side processing. This means that you can support dynamic generation of content for your Firebase Hosting site. Here are some examples of what this allows you to do:

  • Serve dynamic content. Instead of only serving static content, you can perform server-side logic through a function to return a dynamically generated response. For example, you can have a URL like /blog/<id-for-blog-post>. This URL pattern can be pointed to a function that dynamically uses the URL blog post ID parameter to retrieve content dynamically from your Firebase Realtime Database.
  • Prerendering for single page apps to improve SEO. This allows you to create dynamic meta tags for sharing across various social networks.
  • Keep your web app lightweight. You can create an API through Cloud Functions for your Firebase Hosting site to asynchronously retrieve content. This allows you to reduce initial load times for your web app by keeping the client code lightweight and loading content asynchronously through a function.

Connecting Cloud Functions to Firebase Hosting

To connect a function to Firebase Hosting, you'll need to set up Cloud Functions, write your function, create rewrite rules, and deploy your changes. To improve the performance of your dynamic content, you can optionally tune your caching settings. Here's an example to demonstrate how to do this.

Setup Cloud Functions for Firebase Hosting

If you've already set up Cloud Functions for Firebase you can skip this step and proceed to Create an HTTPS function. This will set up Cloud Functions for your Firebase project.

First make sure you have the latest version of the Firebase CLI, which requires Node.js version 6.3.1 or greater. You can install Node by following the instructions on https://nodejs.org/. Installing Node.js will also install npm.

To check what version of Node.js version you're running, run this command in your terminal:

node --version

You can install the latest version of the Firebase CLI by running the following command in your terminal:

npm install -g firebase-tools

After you've updated the Firebase CLI, you'll have to initialize functions. If you have not initialized your Hosting project, run the following command and choose to initialize Hosting and Cloud Functions within your project directory when prompted.

firebase init

If you have an existing project with Hosting, run the following command within your project directory to only initialize Cloud Functions.

firebase init functions

Create an HTTP function to your Hosting site

Open /functions/index.js in your favorite editor and replace its contents with the following code. This will create a simple HTTPS function named bigben.

const functions = require('firebase-functions');

exports.bigben = functions.https.onRequest((req, res) => {
  const hours = (new Date().getHours() % 12) + 1 // london is UTC + 1hr;
  res.status(200).send(`<!doctype html>
    <head>
      <title>Time</title>
    </head>
    <body>
      ${'BONG '.repeat(hours)}
    </body>
  </html>`);
});

Directing Hosting requests to your function

With rewrite rules you can direct requests that match specific patterns to a single destination. For example, to direct all requests from the page /bigben on your Hosting site to execute the bigben function, you would open firebase.json and add the following rewrite configuration under the hosting section.

{
  "hosting": {
    "public": "public",

    // Add the following rewrites section *within* "hosting"
   "rewrites": [ {
      "source": "/bigben", "function": "bigben"
    } ]

  }
}

You can learn more about rewrite rules.

Deploying

After creating a function and setting your rewrite rules, you'll need to deploy your Firebase project. Run the following command in the terminal.

firebase deploy

After deployment, you'll notice that your function is normally accessible through a URL like the following:

https://us-central1-<your-project-id>.cloudfunctions.net/bigben

All traffic on your Firebase Hosting site that matches the path specified in the rewrite rules will be proxied to the appropriate function.

Trying it out

Once everything is deployed you can go to /bigben on your Firebase Hosting site to see it in action.

https://<your-project-id>.firebaseapp.com/bigben

Using Cookies

When using Firebase Hosting together with Cloud Functions, cookies are generally stripped from incoming requests. This is necessary to allow for efficient CDN cache behavior. Only the specially-named __session cookie is permitted to pass through to the function execution.

When present, the __session cookie is automatically made a part of the cache key, meaning that it is impossible for two users with different cookies to receive the other's cached response. You should only use the __session cookie if your function serves different content depending on user authorization.

Managing Cache Behavior

Firebase Hosting uses a powerful global content delivery network (CDN) to make your site as fast as possible. Static content on Firebase Hosting is cached until a new version is deployed. Because functions generate content dynamically, requests that are handled by a function do not cache in the CDN by default. You can configure caching behavior yourself to speed up your app and reduce function execution costs.

Setting Cache-Control

The main tool you'll use to manage cache is the Cache-Control header. By setting it, you can communicate both to the browser and the CDN how long your content should be cached. In your function, you set Cache-Control like so:

res.set('Cache-Control', 'public, max-age=300, s-maxage=600');

The above header does three things:

  • Marks the cache as public. This means that this content is okay to be cached by intermediate servers (in our case, the CDN). By default Cache-Control is set to private which means that only the user's browser is allowed to cache it.
  • Tells the browser for how many seconds it can cache the content with max-age. In the above example, we're telling the browser to cache for five minutes.
  • Tells the CDN for how many seconds it can cache the content with s-maxage. In the above example, we're telling the CDN to cache for ten minutes.

When setting max age, you should set it to the largest amount of time with which you're comfortable having users receive stale content. If a page changes every few seconds, this will be a small number. Other content, however, can safely be cached for hours, days, or even months.

You can learn more about the Cache-Control header on the Mozilla Developer Network.

When is cached content served?

If you have set a public Cache-Control header, your content will be cached in the CDN based on:

  • The hostname
  • The path
  • The query string
  • The content of the headers specified in Vary

The Vary header is how you can signal to which parts of the request were important in determining your response. Most of the time, you don't need to worry about this. Firebase Hosting will automatically ensure that an appropriate Vary header is set on your response for common situations. This includes making sure that any session cookie or authorization header you're using is made part of the cache key, preventing accidental leaks of content.

In some advanced use cases, you may have other headers that you need to affect the cache. When that's the case, you can simply set the Vary header on your response:

res.set('Vary', 'Accept-Encoding, X-My-Custom-Header');

Now two otherwise identical requests with different X-My-Custom-Header headers would be cached separately.

Send feedback about...

Need help? Visit our support page.