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.
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