Deploy a static site to Firebase Hosting: step-by-step

A no-nonsense 2026 walkthrough for shipping a static site (Astro, Vite, plain HTML) to Firebase Hosting in about 10 minutes — including SSL, custom domain, and cache headers.

You have a built static site sitting in dist/. You want it on a real URL with HTTPS, in 10 minutes, without reading the full Firebase docs. This walkthrough does exactly that — and tells you which steps you can safely skip.

Background

Firebase Hosting is one of the fastest ways to put a static site on the open internet with free SSL and a global CDN. The official quickstart works, but skips a few details (cache headers, trailing slash, preview channels) that you will eventually hit. This article covers them up front so you do not have to redo the setup later.

Before you start

  • A Google account and access to the Firebase console.
  • A static site repo that can build locally, for example Astro / Vite / Hugo / plain HTML.
  • Node.js and npm available in your terminal; node -v and npm -v should both print versions.
  • A build command and output directory you understand, usually npm run build and dist.
  • Optional but recommended: a domain you control and access to its DNS settings.

Step by step

  1. Create the Firebase project first: open the Firebase console, click “Add project”, enter a project name, keep or disable Google Analytics based on whether you need it, then finish project creation.
  2. Open the new project, go to Build > Hosting, click “Get started”, and leave that browser tab open. You do not need to copy the whole quickstart, but it confirms Hosting is enabled for the project.
  3. In your local repo, build the site: npm run build. Verify the output folder exists and contains HTML files, usually dist/index.html for Astro / Vite or build/index.html for some other tools.
  4. Install and sign in to the Firebase CLI: npm install -g firebase-tools, then firebase login. If you manage multiple Google accounts, run firebase login:list afterward and confirm the right account is active.
  5. Connect this repo to the Firebase project: run firebase init hosting, choose “Use an existing project”, select the project you just created, set the public directory to your build output (dist, build, or out), and choose “No” for GitHub deploys unless you want CI now.
  6. Answer the SPA question carefully: choose “No” for Astro / Hugo / multi-page static sites; choose “Yes” only for a client-side router app where every route should serve /index.html.
  7. Check the generated files. .firebaserc should contain your Firebase project ID, and firebase.json should point hosting.public at the build folder you verified.
  8. Add production-safe Hosting settings in firebase.json: use cleanUrls: true for extensionless URLs, keep HTML cache short (no-cache or a few minutes), and give hashed assets a long cache (Cache-Control: public,max-age=31536000,immutable).
  9. Preview locally with Firebase, not just your framework dev server: run firebase emulators:start --only hosting or firebase serve --only hosting, then test /, an article page, /404, /sitemap.xml, and /robots.txt.
  10. Deploy: run npm run build again, then firebase deploy --only hosting. Open the printed https://<project-id>.web.app URL and click through the same pages you tested locally.
  11. If the *.web.app site is correct, add your custom domain in Firebase console > Hosting > Add custom domain. Add the TXT verification record first, then add the A or CNAME records Firebase gives you.
  12. Wait for DNS and SSL. Most domains connect within minutes, but give it a few hours before changing unrelated settings. Test both apex and www, plus http:// to https:// redirect behavior.
  13. Finish the launch: update canonical URLs and sitemap base URL, submit the sitemap in Google Search Console, and save a note with the project ID, deploy command, public directory, and DNS records.

Reference firebase.json

A production-safe config for an Astro / Vite / Hugo static site:

{
  "hosting": {
    "public": "dist",
    "cleanUrls": true,
    "trailingSlash": false,
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "headers": [
      {
        "source": "**/*.@(js|css|woff2|svg|webp|png|jpg)",
        "headers": [{ "key": "Cache-Control", "value": "public,max-age=31536000,immutable" }]
      },
      {
        "source": "**/*.html",
        "headers": [{ "key": "Cache-Control", "value": "no-cache" }]
      }
    ]
  }
}

Common CLI commands you will use repeatedly:

npm run build                           # produce dist/
firebase emulators:start --only hosting # local preview at :5000
firebase deploy --only hosting          # push live
firebase hosting:channel:deploy preview --expires 7d  # share a preview link
firebase hosting:sites:list             # confirm which site got the deploy

After-launch verification

  • Open the *.web.app URL and the custom domain in an incognito window; test homepage, deep article URL, 404, sitemap, robots, and mobile layout.
  • Run firebase hosting:sites:list and confirm you deployed to the intended project, especially if you have multiple Firebase projects.
  • In the Firebase console, check Hosting > Release history and confirm the latest release time matches your deploy.
  • Use Search Console URL Inspection after DNS settles to confirm Google can fetch the page and sees the canonical URL you expect.

Common pitfalls

  • Skipping Firebase project creation and trying to run firebase init against an account with no project selected.
  • Choosing the wrong Google account in the CLI, then deploying to a personal test project instead of the production project.
  • Setting public to the source folder instead of the build output — your site will be live but blank.
  • Choosing “configure as a single-page app” by accident, which rewrites every URL to /index.html and breaks SEO for multi-page sites.
  • Forgetting to set no-cache on HTML — visitors keep seeing the old page after each deploy.
  • Skipping the DNS verification step and getting stuck on “needs setup” for hours.
  • Deploying from a dirty working tree without checking what files Firebase will upload.

Who this is for

Anyone shipping a static site (Astro, Vite, Hugo, plain HTML) who wants HTTPS, a CDN, and a custom domain on the free tier.

When to skip this

Apps that need full server-side rendering on every request, real-time websockets, or region-pinned routing — those belong on a framework-native host.

FAQ

  • Do I need to pay anything to deploy?: No. Spark (free) tier covers the deploy, the *.web.app URL, custom domain, and SSL.
  • How long until my custom domain works?: Usually 15 minutes to a few hours after the DNS records are set. SSL provisioning happens in parallel.
  • Can I roll back a bad deploy?: Yes. In Firebase console, Hosting > Release history > pick a previous release > Rollback. It is one click.
  • Where do I put environment variables?: Static sites bake env vars at build time. Put them in your build pipeline, not in firebase.json.
  • What about preview branches?: Use firebase hosting:channel:deploy <name> to get a temporary preview URL with auto-expiry.

Tags: #Indie dev #Firebase #Hosting #Getting started #Workflow