Skip to main content

The Ultimate Guide: Hosting a Firebase Service Worker on Blogger via Cloudflare (~ Zero-CPU)

Adding progressive web app (PWA) features like background push notifications to a Google Blogger site has historically been an uphill battle. The core issue? Browsers demand that your background communication engine (firebase-messaging-sw.js) must live at the absolute root directory of your custom domain (e.g., yourdomain.com/firebase-messaging-sw.js) to securely manage the site's network scope.

Because Blogger is a completely managed platform, it lacks a traditional file directory system. You cannot simply upload a raw JavaScript file to your root path. If you try hosting it on an external CDN or storage bucket, the browser will instantly block it due to strict Service Worker cross-origin security protocols.

The Architecture Strategy

We bypass this platform limitation using Cloudflare Workers as an edge routing layer. By proxying your domain traffic through Cloudflare, we can intercept the exact path your service worker requires and dynamically inject the code at the edge. Furthermore, by implementing Cloudflare's native Workers Cache pipeline, we serve this asset with zero continuous CPU runtime billing. Once cached, requests hit the global edge cache tier directly without spinning up worker isolates.



Step 1 Cloudflare DNS & Proxy Configuration

For a Cloudflare Worker to successfully intercept a URL path, your domain's web traffic must actively route through Cloudflare's global edge infrastructure first. This is controlled entirely by your DNS zone routing state.

  1. Map Your Domain: Link your custom domain (e.g., antinna.in) inside your Cloudflare Dashboard.
  2. Delegate Nameservers: Update your primary registrar records (GoDaddy, Namecheap, etc.) to use the designated Cloudflare edge nameservers.
  3. Verify Blogger Target Records: Ensure your core records required by Google (the CNAME pointing www to ghs.google.com and the four fallback root A records) are completely populated within your Cloudflare DNS configuration panel.
  4. Activate the Orange Cloud (CRITICAL): Look down the Proxy status column for your routing records. Ensure the toggle switches are turned ON so they display an Orange Cloud (Proxied) icon.
Why the Proxy Toggle is Mandatory

Setting your status to "DNS Only" (Grey Cloud) instructs Cloudflare to act merely as a passive phonebook, pointing traffic straight to Google's clusters. Turning on the Proxy (Orange Cloud) forces the traffic through Cloudflare's reverse proxy nodes, allowing our Worker script to hook into the request cycle and say: "If the incoming path is exactly /firebase-messaging-sw.js, halt standard routing and serve this edge asset instead."

Step 2 Structuring the Local Workspace

To avoid manual copy-pasting within the web UI, configure a lightweight, reproducible infrastructure-as-code workspace on your machine. Initialize your workspace using the terminal:

Terminal
mkdir blogger-sw-worker
cd blogger-sw-worker
npm init -y
npm install --save-dev wrangler

Construct a clean directory layout conforming to standard Cloudflare architecture patterns:

Workspace Structure
├── package.json
├── wrangler.toml
└── src
    └── index.js

Populate your manifest properties within package.json to declare clean ES-module execution rules and deployment aliases:

package.json
{
  "name": "antinna-fb-worker",
  "version": "1.0.0",
  "description": "High-performance edge cached Firebase Service Worker for Blogger",
  "main": "src/index.js",
  "type": "commonjs",
  "scripts": {
    "deploy": "wrangler deploy"
  },
  "devDependencies": {
    "wrangler": "^4.92.0"
  }
}

Step 3 Configuring Wrangler and Cache Flags

Your wrangler.toml acts as your deployment configuration manifest. We explicitly map our target custom domain path and use the modern [cache] block configuration flag to initialize a global reverse-proxy edge cache layer sitting in front of your code logic.

wrangler.toml
name = "firebase-messaging-sw"
main = "src/index.js"
compatibility_date = "2026-05-15"

# Opt into Cloudflare's native Workers Cache optimization pipeline
[cache]
enabled = true

# Route matching criteria
[[routes]]
pattern = "www.antinna.in/firebase-messaging-sw.js"
zone_name = "antinna.in"

# Safe public environment configuration metadata
[vars]
FIREBASE_AUTH_DOMAIN = "xxxxxxxxxx.firebaseapp.com"
FIREBASE_PROJECT_ID = "xxxxxxxxxx"
FIREBASE_STORAGE_BUCKET = "xxxxxxxxxx.appspot.com"
FIREBASE_MESSAGING_SENDER_ID = "xxxxxxxxxx"

Step 4 Writing the Edge-Cached Worker Core

Create your execution logic script under src/index.js. By setting an intelligent Cache-Control response header combining max-age=900 with stale-while-revalidate=86400, Cloudflare serves the script directly from its regional edge datacenters for subsequent hits without using any paid worker CPU time.

src/index.js
export default {
  async fetch(request, env, ctx) {
    const jsContent = `
      importScripts('https://www.gstatic.com/firebasejs/11.1.0/firebase-app-compat.js');
      importScripts('https://www.gstatic.com/firebasejs/11.1.0/firebase-messaging-compat.js');

      const firebaseConfig = {
        apiKey: "${env.FIREBASE_API_KEY || "AIzaSyDxxxxxxxxxxxxxxxxxxxxxxxx"}",
        authDomain: "${env.FIREBASE_AUTH_DOMAIN || "xxxxxxxxxx.firebaseapp.com"}",
        projectId: "${env.FIREBASE_PROJECT_ID || "xxxxxxxxxx"}",
        storageBucket: "${env.FIREBASE_STORAGE_BUCKET || "xxxxxxxxxx.appspot.com"}",
        messagingSenderId: "${env.FIREBASE_MESSAGING_SENDER_ID || "xxxxxxxxxx"}",
        appId: "${env.FIREBASE_APP_ID || "1:xxxxxxxxx:web:xxxxxxxxxxxxxxxxxx"}"
      };

      firebase.initializeApp(firebaseConfig);
      const messaging = firebase.messaging();

      // Handle background messages
      messaging.onBackgroundMessage((payload) => {
        console.log('[firebase-messaging-sw.js] Received background message:', payload);
        
        // Broadcast to all active client tabs to update UI instantly
        self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((windowClients) => {
          windowClients.forEach((client) => {
            client.postMessage({
              type: 'FCM_NOTIFICATION_RECEIVED',
              payload: payload
            });
          });
        });

        if (payload.notification) {
          console.log('Firebase is handling this display natively.');
          return;
        }

        const notificationTitle = payload.data?.title || 'New Notification';
        const notificationOptions = {
          body: payload.data?.body,
          icon: payload.data?.image || '/favicon.ico',
          data: { url: payload.data?.url || '/' }
        };

        self.registration.showNotification(notificationTitle, notificationOptions);
      });

      // Handle notification clicks securely
      self.addEventListener("notificationclick", (event) => {
        event.notification.close();
        let targetUrl = event.notification.data?.url || event.notification.click_action;
        
        if (!targetUrl) {
          targetUrl = self.location.origin + "/";
        } else {
          targetUrl = new URL(targetUrl, self.location.origin).href;
        }

        event.waitUntil(
          clients.matchAll({ type: 'window', includeUncontrolled: true }).then((windowClients) => {
            for (const client of windowClients) {
              if (client.url === targetUrl && 'focus' in client) {
                client.postMessage({ type: 'FCM_NOTIFICATION_CLICKED', url: targetUrl });
                return client.focus();
              }
            }
            return clients.openWindow(targetUrl);
          })
        );
      });
    `;

    return new Response(jsContent, {
      headers: {
        "Content-Type": "application/javascript; charset=utf-8",
        // Edge-native cache directive
        "Cache-Control": "public, max-age=900, stale-while-revalidate=86400",
        "Cache-Tag": "firebase-sw-asset",
        "Service-Worker-Allowed": "/", // Unlocks service worker scope permissions for the root domain
      },
    });
  },
};

Step 5 Secure Secret Handling & Deployment

To avoid credential leaking within shared code repositories, keep your highly sensitive Firebase production keys isolated outside your files. We use Cloudflare Secrets to bind tokens at runtime securely.

Terminal Live Deployment
# 1. Authenticate your local terminal to Cloudflare's cloud pipeline
npx wrangler login

# 2. Inject secrets directly into the encrypted environment namespace
npx wrangler secret put FIREBASE_API_KEY
# (Paste your active production API key when prompted and press Enter)

# 3. Compile optimization structures and deploy live globally
npm run deploy

Step 6 Theme Integration inside Blogger

With your edge configuration handling path distribution, update your blogger application layouts to start utilizing background processing handshakes.

  1. Navigate to your **Blogger Dashboard → Theme Settings**.
  2. Click the expansion dropdown arrow sitting beside your active layout and click **Edit HTML**.
  3. Scroll down to your closing </body> structure tag and inject the following worker handshake code block cleanly right above it:
Blogger HTML Integration Script
<script>
  if ('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
      // Explicitly override client scope boundaries to cover the entire root domain path
      navigator.serviceWorker.register('/firebase-messaging-sw.js', { scope: '/' })
        .then(function(reg) {
          console.log('Edge-cached Firebase SW registered successfully! Scope:', reg.scope);
        })
        .catch(function(err) {
          console.error('Service Worker registration lifecycle crashed:', err);
        });
    });
  }
</script>

FAQ Troubleshooting & Cache Purging

If you face deployment hurdles or validation warnings, check these diagnostic solutions:

  • MIME Type Security Refusal Warning:
    Double check your Cloudflare DNS control panel. If your custom proxy status indicator reads "DNS Only" (Grey Cloud), requests fall back to Blogger's layout router, serving a 404 HTML document instead of your compiled JavaScript payload.
  • Purging Stale Edge Cache Assets:
    Because we use high-efficiency cache-control parameters, changes to your environment parameters won't reach users instantly. To clear the old asset globally, navigate to your Cloudflare Dashboard → Caching → Configuration → Custom Purge, and clear your environment using the custom tag name identifier we defined: firebase-sw-asset.
  • Scope Restrictions Blocking Registrations:
    If the browser console returns a security scope violation, make sure your worker's return payload headers include the accurate "Service-Worker-Allowed": "/" value. This explicitly tells the browser engine that your file has root access permission.

Comments

Popular posts from this blog

Flutter™ Installation Without Android Studio™

When developing with Flutter™, you typically install Android Studio™ as part of the setup process. However, if you want a lightweight alternative, you can install Flutter™ with only the Android command-line tools. This guide walks you through the steps to set up Flutter™ on Windows without Android Studio™. Step 1: Install Android Command-Line Tools Download Command-Line Tools: Download the Android SDK Command-Line Tools from the Android Developer website . Extract the downloaded zip file: Extract it to a directory, e.g., C:\Users\<User Name>\AppData\Local\Android\Sdk\cmdline-tools . Set up SDK directories: Inside the cmdline-tools folder, create a subfolder named latest . Move the extracted files and folders into this latest folder. The structure should look like: ...

Building a Modular Project with a Monorepo Using Pub Workspaces Feature

Flutter™  projects often start small, but as they scale, managing multiple packages becomes challenging. If you find yourself working with several modules or features in one project, organizing them efficiently is crucial. Enter Monorepos with Pub Workspaces – an excellent solution for managing a Flutter project with multiple packages in a modular way. In this post, we’ll explore how to structure a Flutter project in a modular way using a monorepo , and how to manage dependencies across various sub-projects without worrying about version conflicts. We’ll also look at how to configure Pub Workspaces to simplify dependency resolution and management. When working with Flutter™ and Dart™ , structuring a project in a modular way can improve scalability and maintainability. This is especially important when managing multiple packages in a single repository, also known as a monorepo . Pub Workspaces , introduced to simplify dependency management, provide a robust solution...

Stop Guessing: A Scalable Strategy for Flutter Environment Management

When building production-grade applications, hardcoding API endpoints, feature flags, or tracking keys directly into your source code is an architectural anti-pattern. To guarantee security and runtime predictability, setups should cleanly isolate target environments depending on compile-time parameters. This guide establishes a compile-time, zero-overhead environment orchestration system leveraging strict encapsulation patterns and modern features in native Dart. 1. Define the Immutable Flavor Domain ( flavor_enum.dart ) The foundation of this pattern relies on an explicit Flavor enumeration. This handles your application's environmental constraints and leverages a resilient static factory fallback to transform incoming build-time string tokens safely into type-safe domains. lib/flavor/flavor_enum.dart Dart /// flavor_enum.dart library; enum Flavor { development, ...