Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Set Caching Rules in Firebase Hosting

To set caching rules in Firebase Hosting, add a headers block in your firebase.json configuration that matches file patterns with Cache-Control directives. Use long max-age values with immutable for fingerprinted assets like JavaScript bundles, short max-age for HTML files that change frequently, and no-cache for dynamic content. Firebase Hosting automatically applies CDN caching, so your headers control both browser and edge cache behavior.

What you'll learn

  • How to configure Cache-Control headers in firebase.json
  • How to set different caching policies for static assets vs HTML
  • How to use immutable caching for fingerprinted build output
  • How Firebase Hosting CDN interacts with your caching headers
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate7 min read10-15 minFirebase Hosting (Spark and Blaze plans), Firebase CLI v13+March 2026RapidDev Engineering Team
TL;DR

To set caching rules in Firebase Hosting, add a headers block in your firebase.json configuration that matches file patterns with Cache-Control directives. Use long max-age values with immutable for fingerprinted assets like JavaScript bundles, short max-age for HTML files that change frequently, and no-cache for dynamic content. Firebase Hosting automatically applies CDN caching, so your headers control both browser and edge cache behavior.

Configuring Cache-Control Headers in Firebase Hosting

Firebase Hosting serves all files through a global CDN, and your caching rules determine how long browsers and CDN edge nodes keep copies of your files before re-fetching. Proper caching dramatically improves load times for returning visitors while ensuring they always get fresh HTML content. This tutorial walks through setting up a complete caching strategy in firebase.json with real-world patterns for SPAs, static sites, and asset pipelines.

Prerequisites

  • A Firebase project with Hosting initialized (firebase init hosting)
  • Firebase CLI installed and authenticated
  • A built frontend project with fingerprinted assets (e.g., Vite, Next.js, or CRA build output)
  • Basic understanding of HTTP caching and Cache-Control headers

Step-by-step guide

1

Add a headers section to firebase.json

Open your firebase.json file and add a headers array inside the hosting configuration. Each entry in the array specifies a source glob pattern and an array of key-value header pairs. The source pattern uses glob syntax where ** matches any path segment and * matches any filename. Firebase evaluates headers top to bottom and applies all matching rules, so more specific patterns should come after general ones.

typescript
1{
2 "hosting": {
3 "public": "dist",
4 "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
5 "headers": [
6 {
7 "source": "**/*.html",
8 "headers": [
9 {
10 "key": "Cache-Control",
11 "value": "no-cache"
12 }
13 ]
14 }
15 ]
16 }
17}

Expected result: Your firebase.json now has a headers section that sets Cache-Control for HTML files.

2

Set long-lived caching for fingerprinted static assets

Build tools like Vite and webpack produce files with content hashes in the filename (e.g., main.a1b2c3d4.js). Since the filename changes whenever the content changes, you can safely cache these files for a very long time. Set max-age to one year (31536000 seconds) with the immutable directive, which tells the browser to never revalidate the file during its max-age period. Apply this to JavaScript, CSS, images, and fonts in your build output.

typescript
1{
2 "hosting": {
3 "public": "dist",
4 "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
5 "headers": [
6 {
7 "source": "**/*.html",
8 "headers": [
9 {
10 "key": "Cache-Control",
11 "value": "no-cache"
12 }
13 ]
14 },
15 {
16 "source": "**/*.@(js|css)",
17 "headers": [
18 {
19 "key": "Cache-Control",
20 "value": "public, max-age=31536000, immutable"
21 }
22 ]
23 },
24 {
25 "source": "**/*.@(jpg|jpeg|png|gif|svg|webp|avif|ico|woff2|woff|ttf)",
26 "headers": [
27 {
28 "key": "Cache-Control",
29 "value": "public, max-age=31536000, immutable"
30 }
31 ]
32 }
33 ]
34 }
35}

Expected result: JavaScript, CSS, images, and font files are cached for one year with the immutable directive.

3

Configure caching for your SPA index.html

Single-page applications serve the same index.html for all routes using a rewrite rule. This file must never be aggressively cached because it contains the script tags pointing to your fingerprinted bundles. When you deploy a new version, users need to fetch the updated index.html to get the new bundle references. Use no-cache to force revalidation on every visit, or set a short max-age like 300 seconds (5 minutes) if you want some caching benefit.

typescript
1{
2 "hosting": {
3 "public": "dist",
4 "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
5 "rewrites": [
6 {
7 "source": "**",
8 "destination": "/index.html"
9 }
10 ],
11 "headers": [
12 {
13 "source": "/index.html",
14 "headers": [
15 {
16 "key": "Cache-Control",
17 "value": "no-cache"
18 }
19 ]
20 },
21 {
22 "source": "/",
23 "headers": [
24 {
25 "key": "Cache-Control",
26 "value": "no-cache"
27 }
28 ]
29 }
30 ]
31 }
32}

Expected result: The index.html file is never served from a stale cache, while all fingerprinted assets load instantly from the browser cache.

4

Add security headers alongside caching headers

The headers configuration in firebase.json supports any HTTP response header, not just Cache-Control. You can add security headers like X-Content-Type-Options, X-Frame-Options, and Referrer-Policy alongside your caching rules. Apply these globally using a ** source pattern to cover all responses.

typescript
1{
2 "source": "**",
3 "headers": [
4 {
5 "key": "X-Content-Type-Options",
6 "value": "nosniff"
7 },
8 {
9 "key": "X-Frame-Options",
10 "value": "DENY"
11 },
12 {
13 "key": "Referrer-Policy",
14 "value": "strict-origin-when-cross-origin"
15 }
16 ]
17}

Expected result: All responses include security headers in addition to their specific caching rules.

5

Deploy and verify your caching headers

Deploy the updated firebase.json to Firebase Hosting and verify the headers are applied correctly. Use your browser's DevTools Network tab to inspect response headers on different file types. Check that HTML files show no-cache, fingerprinted JS/CSS files show the long max-age with immutable, and all files include your security headers. You can also use a preview channel to test before deploying to production.

typescript
1# Deploy to a preview channel first
2firebase hosting:channel:deploy staging
3
4# Once verified, deploy to production
5firebase deploy --only hosting

Expected result: DevTools Network tab shows the correct Cache-Control headers for each file type. HTML returns no-cache and assets return max-age=31536000, immutable.

Complete working example

firebase.json
1{
2 "hosting": {
3 "public": "dist",
4 "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
5 "rewrites": [
6 {
7 "source": "**",
8 "destination": "/index.html"
9 }
10 ],
11 "headers": [
12 {
13 "source": "**",
14 "headers": [
15 {
16 "key": "X-Content-Type-Options",
17 "value": "nosniff"
18 },
19 {
20 "key": "X-Frame-Options",
21 "value": "DENY"
22 },
23 {
24 "key": "Referrer-Policy",
25 "value": "strict-origin-when-cross-origin"
26 }
27 ]
28 },
29 {
30 "source": "/index.html",
31 "headers": [
32 {
33 "key": "Cache-Control",
34 "value": "no-cache"
35 }
36 ]
37 },
38 {
39 "source": "/",
40 "headers": [
41 {
42 "key": "Cache-Control",
43 "value": "no-cache"
44 }
45 ]
46 },
47 {
48 "source": "**/*.html",
49 "headers": [
50 {
51 "key": "Cache-Control",
52 "value": "no-cache"
53 }
54 ]
55 },
56 {
57 "source": "**/*.@(js|css)",
58 "headers": [
59 {
60 "key": "Cache-Control",
61 "value": "public, max-age=31536000, immutable"
62 }
63 ]
64 },
65 {
66 "source": "**/*.@(jpg|jpeg|png|gif|svg|webp|avif|ico)",
67 "headers": [
68 {
69 "key": "Cache-Control",
70 "value": "public, max-age=31536000, immutable"
71 }
72 ]
73 },
74 {
75 "source": "**/*.@(woff2|woff|ttf|eot)",
76 "headers": [
77 {
78 "key": "Cache-Control",
79 "value": "public, max-age=31536000, immutable"
80 }
81 ]
82 },
83 {
84 "source": "/manifest.json",
85 "headers": [
86 {
87 "key": "Cache-Control",
88 "value": "no-cache"
89 }
90 ]
91 }
92 ]
93 }
94}

Common mistakes when setting Caching Rules in Firebase Hosting

Why it's a problem: Setting a long max-age on index.html, causing users to see stale app versions after deployment

How to avoid: Always use no-cache for HTML files. Only apply long max-age to fingerprinted assets where the filename changes when content changes.

Why it's a problem: Using no-store instead of no-cache for HTML files, preventing any caching benefit including 304 responses

How to avoid: Use no-cache to allow conditional caching with revalidation. Reserve no-store for truly sensitive data like authenticated API responses.

Why it's a problem: Forgetting to set caching headers on the root path / separately from /index.html in SPA configurations

How to avoid: Add Cache-Control headers for both / and /index.html since Firebase Hosting treats these as separate routes even with SPA rewrites.

Why it's a problem: Deploying without fingerprinted filenames but setting long max-age, causing browsers to serve stale JS/CSS

How to avoid: Ensure your build tool generates content-hashed filenames (e.g., main.a1b2c3.js) before using immutable caching. If filenames do not change, use shorter max-age values.

Best practices

  • Use no-cache for HTML files and max-age=31536000, immutable for fingerprinted static assets
  • Always pair long max-age with content-hashed filenames from your build tool to avoid serving stale files
  • Add security headers (X-Content-Type-Options, X-Frame-Options) in the same headers configuration
  • Test caching behavior using preview channels before deploying to production
  • Set no-cache on manifest.json and service worker files to ensure updates propagate immediately
  • Use the ** glob pattern for global headers and more specific patterns for per-file-type caching
  • Verify headers in DevTools Network tab after each deployment to catch configuration errors

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I need to set up caching rules in my firebase.json for a React SPA hosted on Firebase. Show me how to configure Cache-Control headers with long caching for fingerprinted JS/CSS assets and no-cache for HTML files, plus security headers.

Firebase Prompt

Configure the headers section in firebase.json for a Vite + React SPA on Firebase Hosting. Set immutable caching for hashed JS, CSS, images, and fonts, no-cache for index.html and the root path, and add X-Content-Type-Options and X-Frame-Options security headers.

Frequently asked questions

Does Firebase Hosting have its own CDN caching separate from my Cache-Control headers?

Yes. Firebase Hosting uses a global CDN that caches responses at edge nodes. Your Cache-Control headers influence both the browser cache and the CDN cache. After a new deployment, Firebase automatically invalidates the CDN cache for all files.

What happens to cached files when I deploy a new version?

Firebase Hosting purges the CDN cache on every deployment, so edge nodes fetch fresh copies. However, browsers that already cached files with a long max-age will continue using those cached versions until the max-age expires, which is why fingerprinted filenames are essential.

Can I set different caching rules for different directories?

Yes. Use directory-specific glob patterns like assets/** or images/** in your source field to apply different Cache-Control values per directory.

What is the difference between no-cache and no-store?

no-cache allows the browser to cache the file but requires revalidation with the server on every request (enabling 304 Not Modified responses). no-store tells the browser to never cache the file at all, re-downloading it entirely on every request.

Should I set caching headers on my service worker file?

Yes. Service worker files (sw.js, service-worker.js) should use no-cache or max-age=0 to ensure the browser always checks for updates. Browsers already limit service worker caching to 24 hours, but explicit headers prevent edge cases.

How do I verify that my caching headers are working correctly?

Open your browser DevTools Network tab, load your site, and inspect the Response Headers for each file. Check that HTML files show no-cache and fingerprinted assets show your long max-age value. Also check the Size column for 'disk cache' to confirm caching is active.

Can RapidDev help optimize my Firebase Hosting configuration for performance?

Yes. RapidDev can audit and optimize your Firebase Hosting setup including caching strategies, CDN configuration, security headers, and SPA routing for maximum performance and security.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.