Home Blog How to Deploy a Jekyll Site to Firebase Hosting (2026 Guide)
Tutorial

How to Deploy a Jekyll Site to Firebase Hosting (2026 Guide)

Deploy Jekyll to Firebase Hosting — complete setup with the Firebase CLI, firebase.json config, custom domains, URL rewrites, and GitHub Actions automation.

How to Deploy a Jekyll Site to Firebase Hosting (2026 Guide)

Firebase Hosting is Google’s static hosting platform — part of the Firebase suite alongside Firestore, Auth, and Cloud Functions. For a Jekyll site, it offers a fast global CDN, free SSL, a generous free tier, and seamless integration if you are already using other Firebase services. Here is how to set it up.

Why Firebase Hosting for Jekyll

  • Free tier — 10GB storage, 10GB/month transfer (Spark plan), unlimited for pay-as-you-go
  • Fast global CDN — Google’s infrastructure, same network used by Google’s own products
  • Instant rollbacks — every deployment is versioned; roll back to any previous version in one click
  • Preview channels — deploy to temporary preview URLs before going live
  • Firebase integration — pair your Jekyll site with Firestore, Auth, or Cloud Functions if needed
  • Custom domain + SSL — free certificate provisioning

Prerequisites

  • A Jekyll site with a Gemfile and committed Gemfile.lock
  • Node.js installed (required for the Firebase CLI)
  • A Firebase account (free at firebase.google.com, uses your Google account)
  • A Firebase project created in the Firebase console

Step 1: Create a Firebase project

  1. Go to console.firebase.google.com
  2. Click Add project
  3. Enter a project name (e.g. jekyllhub)
  4. Disable Google Analytics if you do not need it (you can add it later)
  5. Click Create project

Step 2: Install the Firebase CLI

npm install -g firebase-tools

Verify the installation:

firebase --version

Log in:

firebase login

This opens a browser for Google OAuth authentication.

Step 3: Initialise Firebase in your Jekyll project

In your Jekyll project root:

firebase init hosting

The CLI walks you through setup:

? Which Firebase project do you want to associate with this directory?
  > Use an existing project
  > jekyllhub (jekyllhub)

? What do you want to use as your public directory?
  > _site

? Configure as a single-page app (rewrite all urls to /index.html)?
  > No

? Set up automatic builds and deploys with GitHub?
  > No (we will set this up manually later)

? File _site/404.html already exists. Overwrite?
  > No

This creates two files: firebase.json and .firebaserc.

Step 4: Configure firebase.json

The generated firebase.json is a starting point. Customise it:

{
  "hosting": {
    "public": "_site",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "cleanUrls": true,
    "trailingSlash": true,
    "headers": [
      {
        "source": "/assets/**",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "public, max-age=31536000, immutable"
          }
        ]
      },
      {
        "source": "**/*.html",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "no-cache"
          },
          {
            "key": "X-Content-Type-Options",
            "value": "nosniff"
          },
          {
            "key": "X-Frame-Options",
            "value": "DENY"
          }
        ]
      }
    ],
    "redirects": [
      {
        "source": "/old-post/",
        "destination": "/new-post/",
        "type": 301
      }
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/404.html"
      }
    ]
  }
}

Key settings explained:

  • cleanUrls: true — serves /about/index.html when someone visits /about or /about/
  • trailingSlash: true — adds trailing slashes to URLs (matches Jekyll’s default URL structure)
  • headers — sets HTTP headers per file pattern (long cache for assets, no-cache for HTML)
  • redirects — 301 or 302 redirects processed at the CDN
  • rewrites — the catch-all to 404.html handles unknown URLs

Step 5: Build and deploy

Build your Jekyll site:

JEKYLL_ENV=production bundle exec jekyll build

Deploy to Firebase:

firebase deploy --only hosting

Firebase uploads _site/ to its CDN and gives you a live URL:

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/jekyllhub/overview
Hosting URL: https://jekyllhub.web.app

Your site is immediately live at your-project.web.app and your-project.firebaseapp.com.

Step 6: Add a custom domain

  1. In the Firebase console, go to HostingAdd custom domain
  2. Enter your domain (e.g. jekyllhub.com)
  3. Firebase shows DNS records to add:
    • Two A records for the root domain pointing to Firebase’s IP addresses
    • A CNAME for www pointing to your-project.web.app
  4. Add these records at your domain registrar
  5. Firebase provisions an SSL certificate automatically once DNS propagates

You can add multiple custom domains at no charge on both the Spark (free) and Blaze (pay-as-you-go) plans.

Step 7: Use preview channels

Firebase preview channels let you deploy to a temporary URL for review before going live — similar to Netlify’s deploy previews:

# Deploy to a named preview channel
firebase hosting:channel:deploy staging

# Output:
# ✔  hosting:jekyllhub:staging: Channel URL (expires 7 days): https://jekyllhub--staging-abc123.web.app

Preview channels expire after 7 days by default. Useful for reviewing design changes before merging to main.

# List active channels
firebase hosting:channel:list

# Delete a channel when done
firebase hosting:channel:delete staging

Step 8: Instant rollback

Every Firebase deployment is stored as a versioned release. Roll back to a previous version instantly:

  1. Firebase console → HostingRelease history
  2. Find the release you want to restore
  3. Click Rollback to this release

The rollback is instant — Firebase just updates its CDN pointers to the previous build.

Or via CLI:

firebase hosting:clone jekyllhub:live jekyllhub:live --version=VERSION_ID

Automate with GitHub Actions

# .github/workflows/deploy.yml
name: Deploy Jekyll to Firebase

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.2"
          bundler-cache: true

      - name: Build Jekyll
        run: JEKYLL_ENV=production bundle exec jekyll build

      - name: Deploy to Firebase (production)
        if: github.ref == 'refs/heads/main'
        uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: "$"
          firebaseServiceAccount: "$"
          channelId: live
          projectId: jekyllhub

      - name: Deploy PR preview
        if: github.event_name == 'pull_request'
        uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: "$"
          firebaseServiceAccount: "$"
          projectId: jekyllhub
          # No channelId = creates a preview channel automatically

Generate a Firebase service account

  1. Firebase console → Project settingsService accounts
  2. Click Generate new private key → Download the JSON file
  3. Add the entire JSON as a GitHub secret named FIREBASE_SERVICE_ACCOUNT

The GitHub Actions workflow then posts a comment on each PR with the preview URL — the same experience as Netlify deploy previews.

Using rewrites for dynamic routes

If you want to add Firebase Cloud Functions alongside your Jekyll static site (for example, a contact form handler or a newsletter API endpoint), use rewrites:

{
  "hosting": {
    "public": "_site",
    "rewrites": [
      {
        "source": "/api/subscribe",
        "function": "newsletterSubscribe"
      },
      {
        "source": "/api/contact",
        "function": "contactForm"
      },
      {
        "source": "**",
        "destination": "/404.html"
      }
    ]
  }
}

This routes /api/* requests to Cloud Functions while serving everything else as static Jekyll output — a clean way to add dynamic behaviour to a static site without reaching for a full backend.

Firebase Hosting free tier limits

Resource Spark (Free) Blaze (Pay-as-you-go)
Storage 10 GB $0.026/GB
Transfer/month 10 GB $0.15/GB
Custom domains Multiple Multiple
SSL certificates Free Free
Preview channels Yes Yes
Cloud Functions rewrites No Yes

For a personal blog or small project, the free Spark plan is likely sufficient. For a high-traffic site, switch to Blaze (pay-as-you-go) — there is no monthly fee, you only pay for what you use above the free limits.

Troubleshooting

firebase: command not found Run npm install -g firebase-tools and ensure your npm global bin directory is in your PATH.

Deploy fails with Error: HTTP Error: 400 Run firebase login --reauth to refresh your authentication token.

Clean URLs not working Ensure "cleanUrls": true is in firebase.json and you have run firebase deploy after making the change.

CSS/JS returning 404 Verify that _site/assets/ was generated by Jekyll before deploying. Check _config.yml does not exclude your assets directory.

Custom domain shows Firebase default page DNS propagation can take up to 48 hours. Use dig yourdomain.com A to check if the records have propagated. The Firebase console also shows domain verification status.

Firebase Hosting is a solid choice for Jekyll if you are already in the Google/Firebase ecosystem or want built-in preview channels and instant rollbacks. For pure static site hosting without Firebase integration, Cloudflare Pages or Netlify are simpler to set up. But if you need to pair Jekyll with Firebase services — Auth, Firestore, Functions — Firebase Hosting is the natural fit.

Share LinkedIn