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.
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.
Implementation Breakdown
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.
- Map Your Domain: Link your custom domain (e.g.,
antinna.in) inside your Cloudflare Dashboard. - Delegate Nameservers: Update your primary registrar records (GoDaddy, Namecheap, etc.) to use the designated Cloudflare edge nameservers.
- Verify Blogger Target Records: Ensure your core records required by Google (the
CNAMEpointingwwwtoghs.google.comand the four fallback rootArecords) are completely populated within your Cloudflare DNS configuration panel. - 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.
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:
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:
├── package.json
├── wrangler.toml
└── src
└── index.js
Populate your manifest properties within package.json to declare clean ES-module execution rules and deployment aliases:
{
"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.
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.
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.
# 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.
- Navigate to your **Blogger Dashboard → Theme Settings**.
- Click the expansion dropdown arrow sitting beside your active layout and click **Edit HTML**.
- Scroll down to your closing
</body>structure tag and inject the following worker handshake code block cleanly right above it:
<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
Post a Comment