Complete Tutorial: Meta Conversions API with Cloudflare Pages
Meta Conversions API with Cloudflare Pages
Set up server-side conversion tracking in 6 steps — no backend server needed, fully free to host.
📋 What We'll Cover
Create Backend Function in GitHub
This function runs on Cloudflare's edge — no server needed.
- Open your GitHub repository (e.g. TurboNifyIQ)
- Click "Add file" → "Create new file"
Name the file exactly as shown below — the path matters for Cloudflare Pages routing:
functions/api/track.js
Then paste this complete code into the file:
// Meta Conversions API Backend Function for Cloudflare Pages
export async function onRequestPost (context) {
const { request, env } = context;
try {
// Get conversion data from frontend
const data = await request.json ();
const { eventName, email, value, currency = 'USD' } = data;
// Generate unique event ID
const eventId = Date.now ().toString () + Math.random ().toString (36 ).substr (2 , 9 );
// Build the Meta CAPI payload
const conversionData = {
data: [{
event_name: eventName,
event_time: Math.floor (Date.now () / 1000 ),
event_id: eventId,
action_source: 'website' ,
event_source_url: request.headers.get ('referer' ) || 'https://your-domain.com' ,
user_data: {
em: email ? await hashEmail (email) : null ,
client_ip_address: request.headers.get ('CF-Connecting-IP' ),
client_user_agent: request.headers.get ('User-Agent' )
},
custom_data: {
currency: currency,
value: value ? parseFloat (value) : null
}
}],
access_token: env.META_ACCESS_TOKEN
};
// Send to Meta Conversions API
const response = await fetch (
`https://graph.facebook.com/v18.0/${env.META_PIXEL_ID }/events` ,
{
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON.stringify (conversionData)
}
);
const result = await response.json ();
return new Response (JSON.stringify ({ success: true , eventId, metaResponse: result }), {
status: 200 ,
headers: {
'Content-Type' : 'application/json' ,
'Access-Control-Allow-Origin' : '*' ,
'Access-Control-Allow-Methods' : 'POST, OPTIONS' ,
'Access-Control-Allow-Headers' : 'Content-Type'
}
});
} catch (error) {
return new Response (JSON.stringify ({ success: false , error: error.message }), {
status: 500 ,
headers: { 'Content-Type' : 'application/json' , 'Access-Control-Allow-Origin' : '*' }
});
}
}
// Handle preflight CORS requests
export async function onRequestOptions () {
return new Response (null , {
status: 200 ,
headers: {
'Access-Control-Allow-Origin' : '*' ,
'Access-Control-Allow-Methods' : 'POST, OPTIONS' ,
'Access-Control-Allow-Headers' : 'Content-Type'
}
});
}
// SHA-256 hash email for privacy compliance
async function hashEmail (email) {
const encoder = new TextEncoder ();
const data = encoder.encode (email.toLowerCase ().trim ());
const hashBuffer = await crypto.subtle.digest ('SHA-256' , data);
const hashArray = Array.from (new Uint8Array (hashBuffer));
return hashArray.map (b => b.toString (16 ).padStart (2 , '0' )).join ('' );
}
- Set commit message: Add Meta Conversions API backend function
- Click "Commit new file"
Configure Environment Variables in Cloudflare Pages
Store your secret keys safely — never hardcode them in your files.
- Go to Cloudflare Dashboard → Pages
- Click your project name → Settings → Environment Variables
| Variable Name | Value | Type |
|---|---|---|
META_ACCESS_TOKEN |
Your access token (starts with EAA...) |
SECRET |
META_PIXEL_ID |
Your Pixel ID (e.g. 521450497014626) |
TEXT |
Click "Save and deploy" and wait 1–2 minutes for the deployment to complete.
Add Frontend Code to Your HTML File
This code fires conversion events from the browser to your backend function.
- Open index.html in your repository
- Click the ✏️ pencil icon to edit
- Find the closing </body> tag
- Paste the code below right before </body>
/* ===== Meta Conversions API – Frontend ===== */
// Core function: sends an event to your /api/track backend
async function sendMetaConversion (eventName, email = null , value = null , currency = 'USD' ) {
try {
const response = await fetch ('/api/track' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON.stringify ({ eventName, email, value, currency })
});
const result = await response.json ();
console.log ('Meta conversion:' , result);
return result;
} catch (error) {
console.error ('Meta conversion error:' , error);
}
}
// 1. Auto-track page view on load
window.addEventListener ('load' , () => sendMetaConversion ('PageView' ));
// 2. Auto-track form submissions and CTA button clicks
document.addEventListener ('DOMContentLoaded' , function () {
// Track form submissions
document.querySelectorAll ('form' ).forEach (form => {
form.addEventListener ('submit' , function () {
const emailInput = form.querySelector ('input[type="email"]' );
sendMetaConversion ('Lead' , emailInput?.value);
});
});
// Track CTA button clicks — add your own class names here
document.querySelectorAll ('.cta-button, .download-btn, .signup-btn' ).forEach (btn => {
btn.addEventListener ('click' , () => sendMetaConversion ('ViewContent' ));
});
});
// 3. Helper functions – call these anywhere in your code
function trackPurchase (email, value, currency = 'USD' ) {
sendMetaConversion ('Purchase' , email, value, currency);
}
function trackLead (email) { sendMetaConversion ('Lead' , email); }
function trackSignup (email) { sendMetaConversion ('CompleteRegistration' , email); }
function trackDownload () { sendMetaConversion ('ViewContent' ); }
Commit message: Add Meta Conversions API frontend code
Test the Integration
Make sure everything is firing correctly before going live.
- Wait 2–3 minutes for Cloudflare Pages to deploy
- Open your live website
- Press F12 → go to the Console tab
- Refresh the page — you should see a success message
Meta conversion: {success: true, eventId: "1718xxx...", metaResponse: {…}}
// Test a page view
sendMetaConversion ('PageView' );
// Test a lead with email
sendMetaConversion ('Lead' , '[email protected]' );
// Test a purchase event
trackPurchase ('[email protected]' , 99.99 , 'USD' );
Verify in Meta Events Manager
Confirm that Meta is receiving your events on their end.
- Go to Meta Business Manager → Events Manager
- Select your Pixel → click the Test Events tab
- Your events should appear within a few seconds of firing them
PageViewLeadPurchaseCompleteRegistrationViewContentAddToCartTroubleshooting Common Issues
If something isn't working, check these things first.
| Problem | Fix |
|---|---|
| No events showing in Events Manager | Check browser console for JavaScript errors |
| 403 Forbidden error | Your META_ACCESS_TOKEN is invalid or expired |
| Invalid Pixel ID error | Double-check your META_PIXEL_ID value in Cloudflare |
| CORS errors in console | Make sure the backend function includes the CORS headers (already in the code above) |
| Function not found (404) | Confirm the file is at functions/api/track.js — path must match exactly |
- ✅ Browser console shows
{success: true} - ✅ Events appearing in Meta Events Manager
- ✅ No red errors in browser console
- ✅ API endpoint returns 200 status codes
- ✅ Environment variables saved and deployment completed
Bonus: Custom Event Examples
Copy-paste ready snippets for advanced tracking scenarios.
let scrollTracked = false ;
window.addEventListener ('scroll' , function () {
const pct = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100 ;
if (pct > 75 && !scrollTracked) {
scrollTracked = true ; // fire once only
sendMetaConversion ('ViewContent' );
}
});
document.querySelectorAll ('form' ).forEach (form => {
form.addEventListener ('submit' , function () {
const email = new FormData (form).get ('email' );
// Detect form type by CSS class
let event = 'Lead' ;
if (form.classList.contains ('signup-form' )) event = 'CompleteRegistration' ;
if (form.classList.contains ('purchase-form' )) event = 'Purchase' ;
sendMetaConversion (event, email);
});
});

Post a Comment