Firebase Automatic Push Notifications on Blogger
As you know, if you are a Blogger user and have a website hosted on Blogger, you need to use a third-party service for push notifications, which requires extra payment and is risky, slows down your website speed, etc. Integrating Firebase seems difficult, and automating it is even more challenging.
Therefore, in this post, we are providing a completely free, excellent code with full guidelines that will prove very beneficial for you. Every time you publish a new post on Blogger, a push notification will automatically be sent to your users with a premium look!
While WordPress users have access to various plugins, Blogger users often feel left behind. But not anymore!
Although WordPress users can also set this code on their website, the setup method is the same, regardless of whether the website is on Blogger or WordPress. WordPress users will simply need to change the Checking Cloudflare worker are using.
In this comprehensive guide, we are going to build a cutting-edge, fully automated push notification system using Cloudflare Workers and the latest Firebase Cloud Messaging (FCM) V1 API. This isn't just a basic setup; it’s a professional-grade architecture that handles automation, scheduling, and real-time monitoring.
What This Post Covers
- Firebase V1 API Integration: How to use secure JWT-based authentication for sending messages.
- Cloudflare Workers Logic: Writing a backend script that monitors your RSS feed every 15 minutes.
- Smart Scheduling: How to automatically trigger "Explore" notifications at peak Indian times (11 AM, 5 PM, 9 PM) to maximize traffic.
- Advanced Service Worker: Setting up a browser-side script that handles navigation preloading for speed and interactive action buttons for higher CTR.
- Telegram Monitoring: Linking a Telegram Bot to get instant alerts on your phone if your system faces any errors.
How This System Works (The Workflow)
The beauty of this setup lies in its seamless "handshake" between different technologies:
- Subscription: When a visitor lands on your site, our Frontend Script asks for permission. Once granted, it generates a unique token and sends it to your Cloudflare Worker.
- Topic Management: The Worker receives this token and registers it into a Firebase "Topic" (e.g., 'all'). This means you don't need to manage a database of thousands of tokens; Firebase handles the heavy lifting.
- Automated Monitoring: Your Cloudflare Worker "wakes up" every 15 minutes. It fetches your Blogger RSS Feed and compares the latest post link with the one stored in Cloudflare KV memory.
- Triggering Push: If a new post is detected (or it's a scheduled time), the Worker generates a JWT (Secure Token) and tells Firebase to broadcast a notification to everyone in the 'all' topic.
- Delivery: Even if the user's browser is closed, your Service Worker catches the signal from Firebase and displays a beautiful notification with images and "Read More" buttons.
By the end of this guide, you will have a 24/7 automated traffic machine that works tirelessly to bring your readers back to your site.
Step 1: Firebase Project and Private Key (FCM V1)
If you haven't created a Google Firebase project yet, please create one. If you have already created one, that's great.
To use the modern FCM V1 API, you must generate a Service Account key.
- Go to the Firebase Console.
- Select your project and open Project Settings.
- Navigate to the Service Accounts tab.
- Click Generate New Private Key.
- A JSON file will be downloaded to your system.
Open the downloaded JSON file. You will need the following values:
- project_id
- private_key
- client_email
Step 2: Cloudflare Worker Setup
1. Create KV Namespace
In your Cloudflare Dashboard, follow these steps to create a KV Namespace:
- Go to Workers & Pages > KV.
- Click Create a Namespace.
- Name the namespace: BLOG_STORE.
- Once you create the Cloudflare Worker for checking, you will need to bind this KV store to it. Instructions on how to create the worker are provided later. Learn how to bind it now:
- Open your Worker and go to Binding section.
- Navigate to KV > KV Namespace Bindings.
- Click Add Binding.
- Set Variable name to BLOG_STORE.
- Select Namespace as BLOG_STORE.
Step 4: Create a Cloudflare Worker as a Service Worker
This Cloudflare Worker works as a Service Worker.
It runs in the user’s browser and is responsible for showing registration, showing push notifications, payload buttons, handling Navigation Preload and offline.html to improve loading speed. Go to [Edit code] and put this code:
const FIREBASE_SDK_VERSION = '10.12.2';
const FIREBASE_CONFIG = {
apiKey: "YOUR_API_KEY",
authDomain: "yourfirebasedomain.firebaseapp.com",
projectId: "yourprojectid",
storageBucket: "yourfirebase.appspot.com",
messagingSenderId: "YOUR_FIREBASE_SENDER_ID",
appId: "YOUR_FIREBASE_APP_ID"
};
async function handleRequest(request) {
try {
const url = new URL(request.url);
if (url.pathname.endsWith('/firebase-messaging-sw.js')) {
const serviceWorkerCode = `importScripts('https://www.gstatic.com/firebasejs/${FIREBASE_SDK_VERSION}/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/${FIREBASE_SDK_VERSION}/firebase-messaging-compat.js');
const firebaseConfig = ${JSON.stringify(FIREBASE_CONFIG)};
const app = firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging(app);
messaging.onBackgroundMessage((payload) => {
const title = payload.data?.title || "New Update";
const options = {
body: payload.data?.body || "",
icon: payload.data?.icon || '/icon.png',
image: payload.data?.image,
badge: '/badge.png',
tag: 'push-notification',
renotify: true,
actions: JSON.parse(payload.data?.actions || '[]'),
data: { url: payload.data?.click_action || '/' }
};
return self.registration.showNotification(title, options);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
const urlToOpen = event.notification.data.url;
event.waitUntil(clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
for (let i = 0; i < clientList.length; i++) {
const client = clientList[i];
if (client.url === urlToOpen && 'focus' in client) return client.focus();
}
if (clients.openWindow) return clients.openWindow(urlToOpen);
}));
});
const CACHE_NAME = "offline-v1";
const OFFLINE_URL = "offline.html";
self.addEventListener("install", (event) => {
event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.add(new Request(OFFLINE_URL, { cache: "reload" }))));
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
event.waitUntil((async () => {
if ("navigationPreload" in self.registration) await self.registration.navigationPreload.enable();
})());
self.clients.claim();
});
self.addEventListener("fetch", (event) => {
if (event.request.mode === "navigate") {
event.respondWith((async () => {
try {
const pResponse = await event.preloadResponse;
if (pResponse) return pResponse;
return await fetch(event.request);
} catch (e) {
const cache = await caches.open(CACHE_NAME);
return await cache.match(OFFLINE_URL);
}
})());
}
});`;
return new Response(serviceWorkerCode, { headers: { 'Content-Type': 'application/javascript' } });
}
return fetch(request);
} catch (e) { return new Response('Error', { status: 500 }); }
}
addEventListener('fetch', (event) => { event.respondWith(handleRequest(event.request)); });
NOTE: You must enter the Firebase configuration codes. Replace the image placeholder with your image URL, even if it's a Blogger-hosted image. The offline.html page is also required; create it if it doesn't exist.
Add Routes
- Go to worker setting > Domain and Routes.
- Click Add Route.
- Choose your domain and Route: www.yourdomain.com/firebase-messaging-sw.js
Step 3: The Cloudflare checking Worker setup and Code
Create a new Cloudflare Worker and paste the provided code into it.
This Worker is responsible for the following tasks:
- Checking the RSS feed for new posts
- Running scheduled jobs at 11 AM, 5 PM, and 9 PM (IST)
- Sending any error status and reports to Telegram
export default {
async scheduled(event, env, ctx) {
try {
const istDate = new Date(new Date().getTime() + (5.5 * 60 * 60 * 1000));
const hours = istDate.getUTCHours();
const minutes = istDate.getUTCMinutes();
const newPostSent = await checkAndNotify(env);
if (!newPostSent && [11, 17, 21].includes(hours) && minutes < 15) {
ctx.waitUntil(sendFixedNotification(env));
}
} catch (e) {
ctx.waitUntil(reportError("Scheduled Run Failed: " + e.message, env));
}
},
async fetch(request, env, ctx) {
try {
const url = new URL(request.url);
if (url.pathname !== '/subscribe') return new Response("Access Denied", { status: 403 });
const referer = request.headers.get('Referer') || request.headers.get('Origin') || "";
if (!referer.includes("yourdomain.com")) return new Response("Security Error", { status: 403 });
const token = url.searchParams.get('token');
if (!token || token.length < 20) return new Response("Error: Invalid Token", { status: 400 });
const result = await subscribeToTopic(token, env);
return new Response(result, { status: result === "Success" ? 200 : 500 });
} catch (e) {
ctx.waitUntil(reportError("Fetch Error: " + e.message, env));
return new Response("Error", { status: 500 });
}
}
};
async function reportError(msg, env) {
try {
if (!env.TELEGRAM_TOKEN || !env.TELEGRAM_CHAT_ID) return;
await fetch(`https://api.telegram.org/bot${env.TELEGRAM_TOKEN}/sendMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ chat_id: env.TELEGRAM_CHAT_ID, text: "🚨 Worker Alert: " + msg })
});
} catch (e) { console.log(e); }
}
async function sendFixedNotification(env) {
try {
const accessToken = await getAccessToken(env);
if (!accessToken || accessToken.startsWith("Error")) {
await reportError("Fixed Notification Auth Failed", env);
return;
}
const message = {
message: {
topic: "all",
data: {
title: "Explore Popular Categories 🌟",
body: "Discover the best collection of latest Shayari. Click to browse your favorite topics!",
icon: "https://yourdomain.com/icon.png",
badge: "https://yourdomain.com/badge.png",
click_action: "https://www.yourdomain.com/p/explore.html?utm_source=scheduled_push",
actions: JSON.stringify([
{ action: "open", title: "Read Now" },
{ action: "explore", title: "View More" }
])
}
}
};
await fetch(`https://fcm.googleapis.com/v1/projects/${env.PROJECT_ID}/messages:send`, {
method: "POST",
headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" },
body: JSON.stringify(message)
});
} catch (e) { await reportError("Fixed Notification Send Error: " + e.message, env); }
}
async function subscribeToTopic(token, env) {
try {
const accessToken = await getAccessToken(env);
if (accessToken.startsWith("Error")) return accessToken;
const response = await fetch('https://iid.googleapis.com/iid/v1:batchAdd', {
method: 'POST',
headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', 'access_token_auth': 'true' },
body: JSON.stringify({ to: '/topics/all', registration_tokens: [token] })
});
return response.ok ? "Success" : "Google Error: " + response.status;
} catch (e) { return "Crash: " + e.message; }
}
async function checkAndNotify(env) {
try {
const RSS_URL = "https://www.yourdomain.com/feeds/posts/default?alt=rss";
const KV = env.BLOG_STORE;
const response = await fetch(RSS_URL);
const text = await response.text();
const itemMatch = text.match(/<item>([\s\S]*?)<\/item>/);
if (!itemMatch) return false;
const itemContent = itemMatch[1];
let title = (itemContent.match(/<title>([\s\S]*?)<\/title>/) || [])[1] || "";
title = title.replace("<![CDATA[", "").replace("]]>", "").trim();
const link = (itemContent.match(/<link>([\s\S]*?)<\/link>/) || [])[1];
const pubDate = (itemContent.match(/<pubDate>([\s\S]*?)<\/pubDate>/) || [])[1] || "";
if (!link) return false;
const fingerprint = link + "::" + pubDate;
if (fingerprint === await KV.get("last_post_fingerprint")) return false;
const accessToken = await getAccessToken(env);
if (!accessToken || accessToken.startsWith("Error")) return false;
let desc = (itemContent.match(/<description>([\s\S]*?)<\/description>/) || [])[1] || "";
desc = desc.replace(/<!\[CDATA\[|\]\]>/g, "");
desc = desc.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/&/g, "&").replace(/'/g, "'").replace(/ /g, " ");
desc = desc.replace(/<style([\s\S]*?)<\/style>/gi, "").replace(/<script([\s\S]*?)<\/script>/gi, "");
desc = desc.replace(/<[^>]*>?/gm, " ").replace(/\s+/g, " ").trim();
if (desc.length > 100) desc = desc.substring(0, 100).trim() + "...";
const imgMatch = itemContent.match(/<media:thumbnail[\s\S]*?url=['"]([\s\S]*?)['"]/);
let imgUrl = imgMatch ? imgMatch[1] : "";
if (imgUrl) imgUrl = imgUrl.replace(/\/(s\d+|w\d+|h\d+|w\d+-h\d+)(-[a-zA-Z0-9-]+)?\//, '/w1024-rw/');
const message = {
message: {
topic: "all",
data: {
title: title,
body: desc,
image: imgUrl,
icon: "https://yourdomain.com/icon.png",
badge: "https://yourdomain.com/badge.png",
click_action: link.includes("?") ? link + "&utm_source=webpush" : link + "?utm_source=webpush",
actions: JSON.stringify([{ action: "read", title: "Read Full Post" }])
}
}
};
const fcmResp = await fetch(`https://fcm.googleapis.com/v1/projects/${env.PROJECT_ID}/messages:send`, {
method: "POST",
headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" },
body: JSON.stringify(message)
});
if (fcmResp.ok) {
await KV.put("last_post_fingerprint", fingerprint);
return true;
}
return false;
} catch (e) {
await reportError("Check RSS Failed: " + e.message, env);
return false;
}
}
async function getAccessToken(env) {
try {
const cleanKey = env.PRIVATE_KEY.replace(/-----BEGIN PRIVATE KEY-----|-----END PRIVATE KEY-----|\\n|\s|"/g, '');
const binaryDer = new Uint8Array(atob(cleanKey).split("").map(c => c.charCodeAt(0)));
const key = await crypto.subtle.importKey("pkcs8", binaryDer.buffer, { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["sign"]);
const header = btoa(JSON.stringify({ alg: "RS256", typ: "JWT" })).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
const now = Math.floor(Date.now() / 1000);
const claim = btoa(JSON.stringify({ iss: env.CLIENT_EMAIL, scope: "https://www.googleapis.com/auth/firebase.messaging", aud: "https://oauth2.googleapis.com/token", exp: now + 3600, iat: now })).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
const sig = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", key, new TextEncoder().encode(header + "." + claim));
const jwt = header + "." + claim + "." + btoa(String.fromCharCode(...new Uint8Array(sig))).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
const resp = await fetch("https://oauth2.googleapis.com/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}` });
const data = await resp.json();
return data.access_token;
} catch (e) { return "Error: Auth Failed"; }
}
NOTE: Replace yourdomain.com with your own domain and set the full URL for images assets hosted on your Blogger site.
2. Set Environment Variables
Open checking Cloudflare worker and go setting tab: Variables tab, add the following variables and mark them as Encrypted:
| Variable Name | Value |
|---|---|
| PROJECT_ID | Your Firebase Project ID |
| CLIENT_EMAIL | Your Firebase Client Email |
| PRIVATE_KEY | Your Firebase Private Key (include \n) |
| TELEGRAM_TOKEN | Your Telegram Bot Token |
| TELEGRAM_CHAT_ID | Your Telegram Chat ID |
TELEGRAM ALTERNATIVE: Since Cloudflare Workers and Firebase rarely fail, your notifications are unlikely to fail either. Therefore, if you don't want notification error messages sent to Telegram, we can remove that functionality. This will avoid including any third-party code and keep your setup very fast. Use this code in your Worker for this purpose:
export default {
async scheduled(event, env, ctx) {
try {
const istDate = new Date(new Date().getTime() + (5.5 * 60 * 60 * 1000));
const hours = istDate.getUTCHours();
const minutes = istDate.getUTCMinutes();
const newPostSent = await checkAndNotify(env);
if (!newPostSent && [11, 17, 21].includes(hours) && minutes < 15) {
ctx.waitUntil(sendFixedNotification(env));
}
} catch (e) { console.log(e.message); }
},
async fetch(request, env, ctx) {
try {
const url = new URL(request.url);
if (url.pathname !== '/subscribe') return new Response("Access Denied", { status: 403 });
const referer = request.headers.get('Referer') || request.headers.get('Origin') || "";
if (!referer.includes("yourdomain.com")) return new Response("Security Error", { status: 403 });
const token = url.searchParams.get('token');
if (!token || token.length < 20) return new Response("Error: Invalid Token", { status: 400 });
const result = await subscribeToTopic(token, env);
return new Response(result, { status: result === "Success" ? 200 : 500 });
} catch (e) { return new Response("Error", { status: 500 }); }
}
};
async function sendFixedNotification(env) {
try {
const accessToken = await getAccessToken(env);
if (!accessToken || accessToken.startsWith("Error")) return;
const message = {
message: {
topic: "all",
data: {
title: "Explore Popular Categories 🌟",
body: "Discover the best collection of latest Shayari. Click to browse your favorite topics!",
icon: "https://yourdomain.com/icon.png",
badge: "https://yourdomain.com/badge.png",
click_action: "https://www.yourdomain.com/p/explore.html?utm_source=scheduled_push",
actions: JSON.stringify([
{ action: "open", title: "Read Now" },
{ action: "explore", title: "View More" }
])
}
}
};
await fetch(`https://fcm.googleapis.com/v1/projects/${env.PROJECT_ID}/messages:send`, {
method: "POST",
headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" },
body: JSON.stringify(message)
});
} catch (e) { console.log(e); }
}
async function subscribeToTopic(token, env) {
try {
const accessToken = await getAccessToken(env);
if (accessToken.startsWith("Error")) return accessToken;
const response = await fetch('https://iid.googleapis.com/iid/v1:batchAdd', {
method: 'POST',
headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', 'access_token_auth': 'true' },
body: JSON.stringify({ to: '/topics/all', registration_tokens: [token] })
});
return response.ok ? "Success" : "Google Error: " + response.status;
} catch (e) { return "Crash: " + e.message; }
}
async function checkAndNotify(env) {
try {
const RSS_URL = "https://www.yourdomain.com/feeds/posts/default?alt=rss";
const KV = env.BLOG_STORE;
const response = await fetch(RSS_URL);
const text = await response.text();
const itemMatch = text.match(/<item>([\s\S]*?)<\/item>/);
if (!itemMatch) return false;
const itemContent = itemMatch[1];
let title = (itemContent.match(/<title>([\s\S]*?)<\/title>/) || [])[1] || "";
title = title.replace("<![CDATA[", "").replace("]]>", "").trim();
const link = (itemContent.match(/<link>([\s\S]*?)<\/link>/) || [])[1];
const pubDate = (itemContent.match(/<pubDate>([\s\S]*?)<\/pubDate>/) || [])[1] || "";
if (!link) return false;
const fingerprint = link + "::" + pubDate;
if (fingerprint === await KV.get("last_post_fingerprint")) return false;
const accessToken = await getAccessToken(env);
if (!accessToken || accessToken.startsWith("Error")) return false;
let desc = (itemContent.match(/<description>([\s\S]*?)<\/description>/) || [])[1] || "";
desc = desc.replace(/<!\[CDATA\[|\]\]>/g, "");
desc = desc.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/&/g, "&").replace(/'/g, "'").replace(/ /g, " ");
desc = desc.replace(/<style([\s\S]*?)<\/style>/gi, "").replace(/<script([\s\S]*?)<\/script>/gi, "");
desc = desc.replace(/<[^>]*>?/gm, " ").replace(/\s+/g, " ").trim();
if (desc.length > 100) desc = desc.substring(0, 100).trim() + "...";
const imgMatch = itemContent.match(/<media:thumbnail[\s\S]*?url=['"]([\s\S]*?)['"]/);
let imgUrl = imgMatch ? imgMatch[1] : "";
if (imgUrl) imgUrl = imgUrl.replace(/\/(s\d+|w\d+|h\d+|w\d+-h\d+)(-[a-zA-Z0-9-]+)?\//, '/w1024-rw/');
const message = {
message: {
topic: "all",
data: {
title: title,
body: desc,
image: imgUrl,
icon: "https://yourdomain.com/icon.png",
badge: "https://yourdomain.com/badge.png",
click_action: link.includes("?") ? link + "&utm_source=webpush" : link + "?utm_source=webpush",
actions: JSON.stringify([{ action: "read", title: "Read Full Post" }])
}
}
};
const fcmResp = await fetch(`https://fcm.googleapis.com/v1/projects/${env.PROJECT_ID}/messages:send`, {
method: "POST",
headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" },
body: JSON.stringify(message)
});
if (fcmResp.ok) {
await KV.put("last_post_fingerprint", fingerprint);
return true;
}
return false;
} catch (e) { return false; }
}
async function getAccessToken(env) {
try {
const cleanKey = env.PRIVATE_KEY.replace(/-----BEGIN PRIVATE KEY-----|-----END PRIVATE KEY-----|\\n|\s|"/g, '');
const binaryDer = new Uint8Array(atob(cleanKey).split("").map(c => c.charCodeAt(0)));
const key = await crypto.subtle.importKey("pkcs8", binaryDer.buffer, { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["sign"]);
const header = btoa(JSON.stringify({ alg: "RS256", typ: "JWT" })).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
const now = Math.floor(Date.now() / 1000);
const claim = btoa(JSON.stringify({ iss: env.CLIENT_EMAIL, scope: "https://www.googleapis.com/auth/firebase.messaging", aud: "https://oauth2.googleapis.com/token", exp: now + 3600, iat: now })).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
const sig = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", key, new TextEncoder().encode(header + "." + claim));
const jwt = header + "." + claim + "." + btoa(String.fromCharCode(...new Uint8Array(sig))).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
const resp = await fetch("https://oauth2.googleapis.com/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}` });
const data = await resp.json();
return data.access_token;
} catch (e) { return "Error: Auth Failed"; }
}
Step 4: Triggers and Routes
Configure the following triggers and routes for your Cloudflare Worker.
1. Cron Trigger- Go to worker setting> Triggers > Cron Triggers.
- Click Add Trigger.
- Set the schedule to */15 * * * * (runs every 15 minutes).
2. Routes
- Go to worker setting > Routes.
- Click Add Route.
- Choose your domain and Route: yourdomain.com/subscribe*
Step 6: Frontend Script (Blogger Template)
Paste the frontend script inside your Blogger XML template.
Make sure the script is added just before the </body> tag.
<script type='text/javascript'>
//<![CDATA[
window.addEventListener("load",()=>{
const e="push_v1";
if(!("Notification"in window)||"denied"===Notification.permission)return;
Promise.all([import("https://www.gstatic.com/firebasejs/12.7.0/firebase-app.js"),import("https://www.gstatic.com/firebasejs/12.7.0/firebase-messaging.js")]).then(([t,n])=>{
const i=t.initializeApp({apiKey:"YOUR_API_KEY",authDomain:"yourdomain.firebaseapp.com",projectId:"yourdomain",messagingSenderId:"SENDER_ID",appId:"APP_ID"});
const r=n.getMessaging(i);
if("serviceWorker"in navigator){
navigator.serviceWorker.register("/firebase-messaging-sw.js").then(o=>{
if(localStorage.getItem(e))return;
n.getToken(r,{vapidKey:"YOUR_VAPID_KEY",serviceWorkerRegistration:o}).then(t=>{
if(t){fetch("https://yourdomain.com/subscribe?token="+t).then(()=>{localStorage.setItem(e,"1")})}
});
});
}
});
});
//]]>
</script>
NOTE: You must enter the Firebase configuration codes. Replace yourdomain.com with your actual domain. Replace the VAPID key with your Firebase web push certificate key, which you can find in the Cloud Messaging tab of your Firebase project settings. If you don't see it, click the generate button to generate it. The system will only work after you do this.
Step 7: Telegram Bot Alerts
Follow these steps to set up Telegram alerts:
- Create Bot: Search @BotFather on Telegram, send /newbot, and get your API Token.
- Get Chat ID: Search @userinfobot, send /start, and copy your Chat ID.
- Start the Bot: Open your own bot and click Start so it has permission to message you.
Final Checks
- Ensure Blogger RSS is set to Full.
- Verify your domain matches the referer check in the Cloudflare Worker.
- Check your Telegram for instant alerts if anything fails.
Conclusion: Bridging the Gap Between Blogger and Success
Implementing this automated push notification system is a game-changer for any Blogger user who wants to stand toe-to-toe with high-end WordPress sites. By combining the speed of Cloudflare Workers with the security of the Firebase V1 API, you create a powerful ecosystem where your audience stays engaged with real-time updates and smart, scheduled reminders—without any manual effort.
Whether you choose the Simple Method for its clean efficiency or the Advanced Method for robust Telegram monitoring, you now have a reliable 24/7 infrastructure. This setup not only boosts returning traffic but also establishes your blog as a technically strong, forward-thinking brand in your niche.
🛠️ Need Professional Assistance?
We understand that working with API keys, Cloudflare Workers, and Service Worker scripts can sometimes feel overwhelming. If you get stuck at any step or want a guaranteed, error-free setup done directly on your blog, we are here to help.
Contact Us for professional setup services, and let us handle all the technical work for you—so you can focus on what you do best: creating amazing content.
If you liked this post, please don't forget to give it a 5-star rating. If you have any other requests, please let us know by Email or contact social media.