DEV Community

seller-mind
seller-mind

Posted on

How I Built a Global Edge SEO Analyzer Using Cloudflare Workers

When I first started building SaaS products, I genuinely thought getting the core business logic working was the hardest part. I was wrong. The real challenge hit me when I launched my first developer tool and real users started interacting with it. Users from Europe and Asia began complaining about slow response times. I was struggling with API latency, and it was driving me crazy. My serverless functions were hosted in a single US region. Every time someone ran a site analysis, the request traveled across the ocean, the server fetched the target site, processed it, and sent the response back. It was a literal daisy chain of network hops. I tried adding edge caching for my static assets, but the actual compute was still bottlenecked in one geographic region. I also spent way too much time wrestling with Stripe webhook configurations and NextAuth.js OAuth setups from scratch. So I decided to build something to solve both the latency and the boilerplate fatigue.

The problem with traditional serverless architectures for global tools is geographic latency and the hidden costs of cold starts. When you build something like an SEO analyzer, you need to fetch a target URL, read the raw HTML, and parse the DOM. If your API lives in us-east-1, a user in Tokyo waits for the request to reach the US. Then the US server fetches the target website (which might be hosted in Frankfurt). Then it processes the HTML and sends it back to Tokyo. Ignoring this means a frustrating user experience. For developer tools, where speed is a core feature, this latency cost is simply too high. Furthermore, setting up the foundational SaaS infrastructure—like Stripe subscription billing, webhook handling, and authentication—takes weeks of repetitive work. You end up building the same plumbing for every new project instead of focusing on the actual product. You need the compute to happen close to the user, and you need a solid foundation to build on.

To solve this, I shifted the heavy lifting to the edge. I used Next.js 14+ with the App Router for the frontend and core authentication, but I offloaded the external fetching and parsing to Cloudflare Workers. Workers run in V8 isolates at the edge, meaning the code executes in a data center physically close to the user.

Here is how I structured the edge worker for the SEO analysis:

// Cloudflare Worker for edge SEO analysis
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    if (url.pathname !== '/api/analyze') {
      return new Response('Not found', { status: 404 });
    }

    const targetUrl = url.searchParams.get('url');
    if (!targetUrl) {
      return new Response(JSON.stringify({ error: 'Missing url' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Fetch target page at the edge (low latency worldwide)
    const response = await fetch(targetUrl, {
      cf: { cacheTtl: 300, cacheEverything: true },
    });
    const html = await response.text();

    // Run SEO checks (regex-based, no DOM needed)
    const title = html.match(/<title[^>]*>([^<]*)<\/title>/i)?.[1] || '';
    const desc = html.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']*)["']/i)?.[1] || '';
    const h1Count = (html.match(/<h1[ >]/gi) || []).length;

    return new Response(JSON.stringify({ title, desc, h1Count }), {
      headers: { 'Content-Type': 'application/json' },
    });
  },
};
Enter fullscreen mode Exit fullscreen mode

Let's break down what this code is doing. First, we validate the incoming request and extract the target URL. Then, we use the native fetch API, but with a twist: we pass Cloudflare-specific cf options. By setting cacheTtl and cacheEverything, we can cache the raw HTML at the edge for a few minutes, drastically reducing redundant fetches and lowering latency for subsequent requests.

Instead of importing a heavy library like JSDOM to parse the HTML (which would bloat the Worker's bundle size and increase cold starts), I used regex. It's not perfect for every edge case of the web, but for extracting the <title>, meta description, and counting <h1> tags, it's incredibly fast and keeps the Worker lightweight.

Moving this logic to the edge changed everything for the tool's performance. What worked really well was the reduction in Time to First Byte (TTFB) for international users. The difference was night and day compared to the single-region serverless approach.

What surprised me was how seamless the integration was. Because the SaaSLaunch boilerplate I used is TypeScript-first, I could share type definitions between the Next.js App Router and the Cloudflare Worker without duplicating code. Full type safety across the entire stack meant fewer runtime errors and a much smoother developer experience.

If I were to change one thing, it would be my initial approach to HTML parsing. I originally tried to use a lightweight DOM parser, but it still added unnecessary overhead to the edge function. Switching to regex was a pragmatic trade-off that kept things snappy.

Having a production-ready foundation with Stripe subscription billing and NextAuth already wired up saved me countless hours. Being able to do a one-click deployment to Vercel for the frontend and Cloudflare Pages for the edge workers made iterating much faster. I could focus entirely on the product features rather than wrestling with infrastructure.

If you want to see this edge architecture in action, or if you just need to quickly analyze your own site's SEO metadata, I packaged this exact logic into a free tool. You can try the SEO Checker right here: https://saaslaunch.dev/free-tools/seo-checker. It runs entirely at the edge, so you'll get your results in milliseconds, no matter where you are in the world.


Full disclosure: I'm the developer of SaaSLaunch. I built this because I needed it myself.

Top comments (0)