The Secure, Serverless Chat Widget for Modern Sites.
Add AI-powered chat to your website in minutes using Cloudflare Workers. No servers to manage, no exposed API keys, and blazing fast performance at the edge.
- Glassmorphism Header: Beautiful, blurred header background (
backdrop-filter: blur(12px)) - Pulse Animations: Ambient, pure-CSS glowing attention triggers on chat bots.
- Mobile Optimized: Custom mappings to prevent iOS browser zooming and to clear mobile keyboards (
env(safe-area)usage). - Zero Spacing Overhead: Natively prevents the injection div from blocking document heights below host sites footers.
View the latest screenshots of the widget in the screenshots/ directory.
graph LR
User["User's Browser"] -->|Loads widget.js| CDN["Cloudflare Pages (CDN)"]
User -->|Sends Message| Worker["Cloudflare Worker (Backend)"]
Worker -->|Forwards Data| n8n["n8n / OpenAI / Database"]
- Frontend: The user loads
widget.jsfrom a CDN (Cloudflare Pages). - Security layer: The widget talks only to your Cloudflare Worker.
- Backend: The Worker acts as a secure proxy, adding keys/secrets and forwarding the request to your actual AI or automation backend (n8n, Zapier, etc).
This worker will be the secure "middleman" that protects your API keys.
- Log in to Cloudflare Dashboard.
- Go to Workers & Pages → Create Application → Create Worker.
- Name it
chatpulse-backendand click Deploy. - Click Edit Code, delete everything, and paste this:
export default {
async fetch(request, env, ctx) {
// 1. CORS: Allow your website to talk to this worker
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': '*', // Change to your specific domain in production
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
},
});
}
if (request.method !== 'POST') {
return new Response('Method Not Allowed', { status: 405 });
}
try {
const { message, sessionId } = await request.json();
// --- YOUR LOGIC GOES HERE (Default: Echo Bot) ---
const responseText = `You said: "${message}"`;
// ------------------------------------------------
return new Response(JSON.stringify({ message: responseText }), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
});
} catch (error) {
return new Response(JSON.stringify({ error: 'Server Error' }), { status: 500 });
}
},
};- Deploy and copy your Worker URL (e.g.,
https://chatpulse-backend.your-subdomain.workers.dev).
You need to host widget.js, widget.css, and avatar.png so your website can load them. We recommend Cloudflare Pages.
- Create a new GitHub repository (e.g.,
chatpulse-cdn). - Upload
widget.js,widget.css, andavatar.pngto it. - Go to Cloudflare Dashboard -> Workers & Pages -> Create Application.
- Click Pages tab -> Connect to Git -> Select your new repo.
- Build Settings: Leave everything blank/default.
- Click Save and Deploy.
Cloudflare will give you a CDN URL (e.g., https://chatpulse-cdn.pages.dev).
Your assets are now available at:
https://chatpulse-cdn.pages.dev/widget.jshttps://chatpulse-cdn.pages.dev/widget.css
Paste this code before the closing </body> tag of your website.
⚠️ Important: Replace the URLs below with YOUR actual CDN and Worker URLs from Phase 1 & 2.
<!-- 1. Load Styles -->
<link rel="stylesheet" href="https://your-new-cdn.pages.dev/widget.css">
<!-- 2. Load the Widget Script -->
<script src="https://your-new-cdn.pages.dev/widget.js"></script>
<!-- 3. Configure & Initialize -->
<script>
ChatWidget.config({
// This is your Backend Worker (The Security Guard)
webhookUrl: 'https://chatpulse-backend.your-subdomain.workers.dev',
welcomeMessage: 'Hi! How can I help you today?',
primaryColor: '#F03E3E'
});
</script>Now that your worker is listening, connect it to something powerful!
Option A: Connect to n8n / Zapier / Make (Click to Expand)
Copy this into your Cloudflare Worker:
// ... (inside try block)
const WEBHOOK_URL = 'https://n8n.your-domain.com/webhook/chatbot';
const webhookResponse = await fetch(WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, sessionId })
});
const data = await webhookResponse.json();
const responseText = data.output || data.message;
// ...Option B: Connect to OpenAI (ChatGPT) (Click to Expand)
- Set
OPENAI_API_KEYin Worker Settings -> Variables. - Use this code:
// ... (inside try block)
const aiResponse = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${env.OPENAI_API_KEY}`
},
body: JSON.stringify({
model: "gpt-4o-mini",
messages: [{ role: "user", content: message }]
})
});
// ...If you see a "CORS Error" in your browser console, it means your Worker is blocking your website.
Rule: Access-Control-Allow-Origin should match the website where the widget lives.
In your Worker Code:
headers: {
// Allow ONLY your actual website
'Access-Control-Allow-Origin': 'https://your-website.com',
// ...
}- Avatar: Replace
avatar.pngin your CDN repo with your own logo. - Colors: Edit
widget.cssor passprimaryColorin the config object. - Text: Customize the
welcomeMessagein the embed code.
Made with ❤️ for the Serverless Community