Dynamic email banners at the edge: turning Webflow CMS into a live image server

A lightweight serverless architecture that turns Webflow CMS into a live image server for dynamic email banners, built for Capte Technologies.
November 7, 2026

Email remains one of the most durable communication channels in marketing operations, but its technical foundation is frozen in an earlier era of the web.

In modern browser applications, we take reactive state, client-side rendering, and service workers for granted. In the inbox, however, email clients aggressively sanitize code: they strip executable JavaScript, disable external stylesheets, and route every media request through caching proxies. As a result, standard campaign banners remain permanently locked to the exact second an email is sent.

A recent challenge within marketing operations at Capte Technologies brought this constraint to the forefront: our team needed the ability to display live event countdowns, changing webinar dates, and dynamic seat availability inside campaign emails—without burdening engineering with continuous image re-exports, and without paying thousands of dollars a month for enterprise dynamic-creative software.

The solution was to move the dynamic logic out of the email client and shift it upstream to the edge, turning Webflow CMS into a live image server.

Why dynamic email must happen upstream

Browser applications can register a client-side Service Worker or run JavaScript to intercept network requests and manipulate the DOM dynamically.

In email design, this model is impossible. Email clients—from Apple Mail and Gmail to Outlook and Yahoo—enforce strict sandboxing:

  • No client-side JavaScript: All script tags are stripped before rendering to prevent cross-site scripting (XSS)‍
  • No browser service workers: The email viewport is sandboxed and cannot register background tasks.
  • Inconsistent CSS engines: Outlook on desktop still relies on Microsoft Word's HTML parsing engine, while webmail clients strip external font faces and CSS grid layouts.

Because an email client will only execute a standard HTTP GET request for a static image file, all dynamic logic must execute upstream. The email client expects a static PNG; the server on the other end must synthesize that image in real time.

Turning Webflow CMS into the content source of truth

Marketing teams already manage campaigns, event schedules, and design collateral directly inside Webflow. Forcing editors to log into a separate proprietary dashboard or coordinate manual assets across departments introduces friction.

Instead, the Webflow CMS serves as the single content engine.

We structured an Email Banners collection in Webflow with fields mapped to SVG text and style nodes:

  • Campaign Name: Internal plain text identifier (e.g. Q4 Product Keynote)‍
  • Slug: URL key for the image tag (e.g. q4-keynote)
  • Badge Text: Short status tag (e.g. REGISTRATION OPEN, TODAY AT 1PM, SOLD OUT)‍
  • Headline Text: Primary banner copy rendered at 28px bold.‍
  • Subhead Text: Supporting description or speaker title.
  • Event Date / Target: Date/time used to calculate dynamic countdown offsets.
  • Background Variant: Option dropdown selecting dark, brand blue, or neutral grid canvas styles.
  • Is Live: Master switch toggling dynamic output versus an evergreen fallback graphic.

When a marketing manager updates a headline in Webflow and clicks Publish, Webflow's webhook notifies our edge layer, updating the banner across delivered inboxes instantly.

Sub-50ms rasterization at the edge with WebAssembly

Generating dynamic images on a traditional server typically requires spinning up headless Chrome via Puppeteer. While flexible, headless browsers are memory-heavy, slow to boot, and expensive to scale during high-volume email opens.

Instead, I implemented this at the edge using Cloudflare Workers and WebAssembly.

Because SVG is standard XML text, string manipulation inside a V8 isolate takes single-digit milliseconds. Once the SVG is assembled with the live Webflow CMS data, a lightweight WebAssembly port of the resvg rendering engine (@resvg/resvg-wasm) compiles the SVG directly into a high-density binary PNG buffer.

‍

import { Resvg, initWasm } from '@resvg/resvg-wasm';

interface Env {
  WEBFLOW_API_TOKEN: string;
  WEBFLOW_COLLECTION_ID: string;
  BANNER_CACHE: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const slug = url.pathname.replace(/^\/banner\//, '').replace(/\.png$/, '');

    // 1. Retrieve cached CMS payload from Edge KV (60s TTL prevents API throttling)
    let bannerData = await env.BANNER_CACHE.get(slug, { type: 'json' });

    if (!bannerData) {
      const apiResponse = await fetch(
        `https://api.webflow.com/v2/collections/${env.WEBFLOW_COLLECTION_ID}/items`,
        {
          headers: {
            'Authorization': `Bearer ${env.WEBFLOW_API_TOKEN}`,
            'Accept': 'application/json'
          }
        }
      );
      const json: any = await apiResponse.json();
      bannerData = json.items?.find((item: any) => item.fieldData.slug === slug)?.fieldData;

      if (bannerData) {
        await env.BANNER_CACHE.put(slug, JSON.stringify(bannerData), { expirationTtl: 60 });
      }
    }

    // Fallback if item is unpublished or disabled
    if (!bannerData || !bannerData['is-live']) {
      return fetch('https://cdn.johnhansen.design/assets/email/fallback-banner.png');
    }

    // 2. Compose SVG template with live Webflow CMS data
    const svg = `
      <svg width="600" height="240" viewBox="0 0 600 240" xmlns="http://www.w3.org/2000/svg">
        <rect width="100%" height="100%" fill="#0E1117" />
        <rect x="36" y="36" width="130" height="26" rx="4" fill="#1F2430" />
        <text x="48" y="53" fill="#00D26A" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto" font-size="11" font-weight="700" letter-spacing="1">
          ${bannerData['badge-text'] || 'CAPTE UPDATE'}
        </text>
        <text x="36" y="116" fill="#FFFFFF" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto" font-size="26" font-weight="700">
          ${bannerData['headline-text']}
        </text>
        <text x="36" y="152" fill="#8B949E" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto" font-size="15">
          ${bannerData['subhead-text']}
        </text>
      </svg>
    `;

    // 3. Compile SVG to binary PNG buffer using WebAssembly
    const resvg = new Resvg(svg, { fitTo: { mode: 'width', value: 600 } });
    const renderedPng = resvg.render();
    const pngBuffer = renderedPng.asPng();

    // 4. Return binary stream with proxy-busting headers
    return new Response(pngBuffer, {
      headers: {
        'Content-Type': 'image/png',
        'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
        'Pragma': 'no-cache',
        'Expires': '0'
      }
    });
  }
};

Navigating the proxy barrier: Gmail and Apple MPP

The primary challenge with open-time email rendering is webmail proxy caching:

     
  1. Google Image Proxy (googleusercontent.com): Gmail routes every external image through its caching proxy. If caching headers specify a long max-age, Google caches the image on the initial delivery scan. Returning no-cache, no-store, must-revalidate, max-age=0 ensures that Google re-validates and fetches fresh banner content on recipient interaction.
  2. Apple Mail Privacy Protection (MPP): Apple pre-fetches email images over proxy IPs when an incoming message lands in iCloud Mail, regardless of whether the user has opened the email. This means Apple devices receive the snapshot accurate at delivery time. By maintaining strong contrast and evergreen default copy, banners remain visually coherent whether opened instantly or hours later.
  3.  

Practical results

Deploying this architecture inside Capte Technologies' web operations produced three tangible outcomes:

     
  • Zero Recurring SaaS Costs: Completely bypassed five-figure enterprise subscriptions and per-open fees.
  • Single Content Workflow: Marketing managers create and manage campaign copy entirely inside Webflow, preserving existing brand styles and permissions
  • Sub-50ms Response Latency: Edge execution ensures email recipients experience no noticeable image loading delay.
  •  

Key takeaways

     
  • Email dynamics live upstream: Email clients prohibit client-side scripting; dynamic personalization must happen at the server or edge level before the image bytes are returned
  • Webflow as a headless CMS: Webflow's REST API and intuitive visual interface make it an ideal database for non-technical marketing teams managing dynamic creative.
  • WebAssembly at the edge: Pairing Cloudflare Workers with WASM-based SVG rasterizers delivers sub-50ms compute speeds without the cost or latency of headless browsers.
  • Proxy header rigor: Explicit no-cache, no-store header configurations are mandatory to prevent Gmail and proxy networks from locking in stale campaign graphics.