Advanced Technical SEO for Jekyll Sites with Cloudflare Edge Functions

Recent Posts

Ditti Kovร cs miamalkova Mia Malkova fit.suny84 Sara | Darmstadt ๐Ÿ‡ฉ๐Ÿ‡ช ๐Ÿ‡ต๐Ÿ‡ฑ officialqueenrogue Queenrogue team marie6 Shannon Weaver๐Ÿฆ‹ d kear Diane kear mariella.1.0.1 Mariella Veroza sandra.curvydaily Sandra We | Daily Looks tizianamiglietta78 Tiziana Miglietta๐ŸŒท๐Ÿ’ฏ๐ŸŒธ By .Prof. Maximilian Catenacci romirain Romi Rain gwensinger GWEN โšก๏ธ elainastjames Elaina St James evapadlock โžฐEva Padlockโžฐ andrea.mops Andrea franciellyh Francielly Henicka alexisgolden4real Alexis Golden whitegirlpoliticking Alexis Texas dianathehuntress10 Diana the Goddess and Huntress ellanabryan Ellana Bryan roxyleone original Roxy Leone ryankeelytm Ryan Keelyโ„ข๏ธ wow kiki Kiki anna marisax ANNA MARISAX ellienewby Ellie Mae Newby reba fitness Reba ๐ŸŒบ elgaghironi77 Elga Ghironi bowfishing baddie Bowfishing Baddie imkaeleereneee kaelee rene โค๏ธโ€๐Ÿ”ฅ rachelgutvilen โค๏ธ๐ŸคŸ๐Ÿผ๐ŸŒŽ carolines8castelines Carolina Castelinho missparaskeva Pasha Pozdniakova ladysoft68 LadyChic official antonellakahllo ๐Ÿ‡บ๐Ÿ‡ธ๐Ÿ’„๐Ÿ‡ช๐Ÿ‡จAntonella Kahllo thefoxprojects Shay Fox katlac860 Katrina LaClair eva ginger.69 Eva. A redhead from the EU : macarenainthesun Macarena belafernandez Bela Fernandez marie madore Marie Madore secret pineapple babe Secret pineapple babe maceytoney built Macey Eckerfield IFBB Pro WPD emmatequilaa Emma Andersson healthyhappyandworkinghard Living Life โค๏ธ laetitialacourt Laetitia Lacourt home alone mum Holly nursegwennie.1977 Gwendolyn Patricia sallymidwestxx Sallyโœจ butter.fly.girl ๐•ญ๐–š๐–™๐–™๐–Š๐–—๐–‹๐–‘๐–ž๐–Œ๐–Ž๐–—๐–‘ โ˜พ itssamshaw Sam agnireni Agnieszka san.glm San Glm persiav Valentina Persia realromemajor Rome Major caly.morgan7 caly morgan thefitchampion Fitchampion hotwifeblondie2 Heidi mariah 970 Mariah Medina sofiaslittlesecret Sofia officialamandabreden Amanda Bredรฉn ms sandralyn Sandra Lyn bianca paradis BIANCA PARADIS moreheathervip2 Heather anna sanderson se Anna Sanderson ๐Ÿ‡ธ๐Ÿ‡ช theamandanicole The Amanda Nicole rosita massignan Rosita Massignan marianaangel7036 Mariana Ryadkova dolorescouceiro DOLORES COUCEIRO orsolya.laura Molnar OrsiโœจLaura๐ŸŽ€๐ŸคŸ๐Ÿ’๐Ÿฆ‹ paola.mercurio official Paola Mercurio thesilviasaige Silvia Saige tat.globaa sweety babyyyyyy ๐’ฎ๐“Œ๐‘’๐‘’๐“‰๐“Ž ๐Ÿ‡ฎ๐Ÿ‡น๐Ÿ€ carleefinkk Carlee Fink renae.olivia Fashion Beauty Blogger thepumaswede Puma Swede kelly sunn1 Kelly hollyjane.xo.xo Holly Jane Johnston wineloverlisa1019 Lisa J kareneng1xoxobackup4 kareneng1xoxo4backup yourbrookemarcell Brooke Marcell missdaniscreams ๐•ฏ๐–†๐–“๐–Ž ๐•ฟ๐–๐–”๐–’๐–•๐–˜๐–”๐–“ kiki run love Krโ„น๏ธstโ„น๏ธna | Motivational Sports Junkie ๐Ÿ™Œ meganrain Megan Rain lisas.worldof.wine Lisa J kasia.klosowska.czajka ๐“š๐“ช๐“ฝ๐“ช๐“ป๐”ƒ๐”‚๐“ท๐“ช ๐“šล‚๐“ธ๐“ผ๐“ธ๐”€๐“ผ๐“ด๐“ช ๐“’๐”ƒ๐“ช๐“ณ๐“ด๐“ชโค๏ธ yesjulz YESJULZ c no5 CHANEL-RENEEโ€™ paola.marradi Paola Marradi ๐Ÿ’– Unico e vero mio profilo ufficiale capturednaturalbeauty Captured Natural Beauty claudia.santoro Claudia Santoro alluring beautys Alluring beauty conniesfashionvault ๐“’๐“ธ๐“ท๐“ท๐“ฒ๐“ฎ ๐“ฆ๐“ฎ๐“ผ๐“ผ๐“ธ๐“ท elena gbogdan Elena Bogdan valeria.cannon28 ๐Ÿ‘‘Valeria karma.rx Alicia Marie Killian only1msnicole Matessa Nicole ashleyniccoleacro Ashley Niccole fili sfakia Erifili Sfakianakis charliemw78 CharlieMW78 lacikaysomers Laci Kay Somers simca9982024 Simi angelacavagna ANGELA CAVAGNA theofficialbrit itsmemichellewilliams Michelle Williams daniellachavezofficial Sexy Queen romichaseforever Romi Chase bambiblacks offical Bambiblacks offical jadeatkinson Jade Atkinson | Stronger By Jade carty ti23 Carty chera.leigh Chera Leigh โ€ข Digital Product Affiliate Mentor thebansheemoon Bansheemoon susanabiancaberry SUSANA BIANCA giorginadipietro29 Giorginadipietro29๐Ÿฆ„ mirra.s Simona Mirra ItalianaFitness showingthesea underwater the.real.bella.lexi.2 Lieutenant Bella Lexi omgitsamberj Amber J cmaryce5 Maria Valverde mima mimica2 Mima Mimica dkm 65official dkm 65 nosecretsmom Faby Monteiro molliejade093 Mollie reissmannevelyn Evelyn Gisela ReiรŸmann roncikelv Renata Szalai debbie.johnson544 Debbie Johnson realnicoleaniston Nicole Aniston berrydogbikini Berrydog Bikinis rollsroycesahar Sahar Manley mynina.co Mynina.co ange hell66 Ange Hell i.am.jana J A N A tatianatornese ะขะฐั‚ัŒัะฝะฐ.๐Ÿ’›๐Ÿงฟ ุชุงุชูŠุงู†ุง selenestyle Selene gabriagagliardi Gabria Gagliardi aphrodite 1979 Aphrodite rebeccalynne d DeepThoughtsByBlondie moreheathervip Heather Hathaway carrooberg Caroline ร–berg mysti.sherryxoxo S.Mysti giamacool Gia Marie Macool therealagelessvixen Niecy msmature1 Miss Mature ๐Ÿฅ‚๐Ÿ’‹๐Ÿ‡ฆ๐Ÿ‡บ realchanelstjames2 Chanel st james sarahharlowsworld Sarah Harlow valerianunesrj Valeria Nunes Lifestyle nickyferrariofficial Nicky Ferrari anita.bonita.pepita ๐‘จ๐’๐’Š๐’•๐’‚ ๐’—๐’‚๐’ ๐’๐’๐’ fetishmamakeeley Keeley fionapemberton1 Fiona Pemberton CLASSIC MODEL moreheathervip1 Heather barberiomarilureal Marilรน Barberio lady rox business Roxana Sowada Crypto | Forex | Marketing lisa onthe weekend Lisa J fitlakemomma ๐Ÿ”ฅJessica Leigh๐Ÿ”ฅ thefinancialladder THE FINANCIAL LADDERโ„ข | Personal Mentorship anastasiya kvitko Anastasiya Kvitko mrsmnbcpl mrsmnbcpl bigdataspecialist Big Data | Data Science | ML thedarksideofnature The Dark Side of Nature chelle.fit.mama Chelle | Fitness Over 50 dltscd DS itsmontanagirl Gracie mamaandthetribe Priscila ๐Ÿ‡ง๐Ÿ‡ท| Stylist Model country.reese1 Reese Leon dianavipofficial Diana ๐Ÿ‘‘ greta di vona official Greta Di Vona natashayoung silvermodel Natasha Young burtonregina Regina Burton janergreene Jane Greene zairaortiztorres Zaira Veronica Ortiz Torres my friend deirdre Deirdre Nicholas dulcesecretocaliente Vida Caliente rachelpetsiavas rachel petsiavas robin rmatthews Robin Rinkle Matthews tonialand Tonia lockett lifetsway Tays Expat Life ๐Ÿ matureandconfident suliahosiery Sulia Shavi themichellesilver Michelle Silver kaylampowers Kayla Marie Powers amalfi.beauty Amalfi Gutierrez 30pluswomen1 30 Plus Women jamieallen250 JAMIE FLETCHER jlynn2929 Joycelynn thesydneycole ๐ŸŽ€ ๐•Š๐•ช๐••๐•Ÿ๐•–๐•ช โ„‚๐• ๐•๐•– ๐ŸŽ€ omgitsmswhite Ms White dannijones 2427 DanniJones 2427 secretofficegirlfriend Secret office girlfriend mariiamiau Mariiamiau sa.bine.368 Sabine D indydreamgirl Indiana DreamGirl vickyvette Vicky Vette krystalshay Snap๐Ÿ‘ปKrystalNow โ™ก Deleted 276k olivieri marina Olivieri Marina k1m3yk1m Kimkimmy inkd.model Tattoos clara guggiari Clara Guggiari mehta.is.back Ashutosh Mehta sheswityou hotgrandma.11 luciad Lucia D โค๏ธ aemeliafox AEMELIA FOX madame letitia ann ๐’๐ญ๐ž๐ฉ๐ก๐š๐ง๐ข๐ž ๐‰๐จ๐ฒ๐œ๐ž ๐’๐ญ๐ž๐ฐ๐š๐ซ๐ lauradamore LAURA D AMORE ยฎ sierracutieexx Sierra ๐ŸŒธ beauty60beauty Natali katrinakavvalos KATRINA KAVVALOS stilzivota by marija Marija Panic spicy chevelle Carrissa lolliecakez Lollie noemi.giangrande Noemi Emi Giangrande lovecurvy laurabrioschi Laura Brioschi sonjaweiske Sonja Weiske rossanadoria Rossana ๐ŸŒ™โ˜€๏ธ carmenann40 Carmen Moreno daytonatemptress Charice chrissy.victoria โšก๏ธCHRISSYโšก๏ธ ba fataturchina Fataturchina misskatieaz misskatieaz wesslieexo Wesslieexo extremecomplicity ExtremeComplicity jen edawgthecat Jennifer Scott | Fitness l.henderson23 L.henderson23 miss lisa Lisa Rothman veroniicamacchi Veronica Macchi karenficarelli Karen Ficarelli hotwifetiffany2 Fit Tiff alettaoceanofficial1 Aletta Ocean carrieyourbeauty CARRIE MAIO fatimaaj.36 Fรกtima Alamo mspaigebauer Paige Bauer torontojules Julia Faith Gourlie sofianka S O F I A N K A tgit tammy ๐ŸŽถ๐Ÿฆ‹TGIT Tammy๐Ÿฆ‹๐ŸŽถ TGITโ˜€๏ธ roxyleoneok Roxana Andrea Leone claudia calderonr97 Claudia Calderรณn loribadman20 Lori Badman babels cameron Tanya Lawrence | Craft Beer Tourism | Beer Education thereallisaann Lisa Ann iam mary bon โ˜€๏ธ ๐Ÿ‘‘MARY BONVISSUTO ๐Ÿ‘‘โ˜€๏ธ olyria roy Olyria Roy napaulabrook eumesma Ana paula Brook avarosali3 Ava Rose angyal.ada Angyal Ada realchanelstjames Chanel St. James theoamilfy Theodora Jayde katie k beach Katie K bikinibeachsydney Jemma openheartscanunite Rhyanna Watson shaneanddaff Shane and Daff mrsskyrose Sky Rose lilymichi main Lily Michi ๐ŸŽ€ delovely curves Delovely

Your Jekyll site follows basic SEO best practices, but you're hitting a ceiling. Competitors with similar content outrank you because they've mastered technical SEO. Cloudflare's edge computing capabilities offer powerful technical SEO advantages that most Jekyll sites ignore. The problem is that technical SEO requires constant maintenance and edge-case handling that's difficult with static sites alone. The solution is leveraging Cloudflare Workers to implement advanced technical SEO at the edge.

Edge SEO Architecture for Static Sites

Traditional technical SEO assumes server-side control, but Jekyll sites on GitHub Pages have limited server capabilities. Cloudflare Workers bridge this gap by allowing you to modify requests and responses at the edge. This creates a new architecture where your static site gains dynamic SEO capabilities without sacrificing performance.

The key insight: search engine crawlers are just another type of visitor. With Workers, you can detect crawlers (Googlebot, Bingbot, etc.) and serve optimized content specifically for them. You can also implement SEO features that would normally require server-side logic, like dynamic canonical tags, hreflang implementations, and crawler-specific sitemaps. This edge-first approach to technical SEO gives you capabilities similar to dynamic sites while maintaining static site benefits.

Edge SEO Components Architecture

Component Traditional Approach Edge Approach with Workers SEO Benefit
Canonical Tags Static in templates Dynamic based on query params Prevents duplicate content issues
Hreflang Manual implementation Auto-generated from geo data Better international targeting
Sitemaps Static XML files Dynamic with priority based on traffic Better crawl prioritization
Robots.txt Static file Dynamic rules based on crawler Optimized crawl budget
Structured Data Static JSON-LD Dynamic based on content type Rich results optimization
Redirects Static _redirects file Smart redirects with 301/302 logic Preserves link equity

Core Web Vitals Optimization at the Edge

Core Web Vitals are critical ranking factors. Cloudflare Workers can optimize them in real-time:

1. LCP (Largest Contentful Paint) Optimization

// workers/lcp-optimizer.js
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  const response = await fetch(request)
  const contentType = response.headers.get('Content-Type')
  
  if (!contentType || !contentType.includes('text/html')) {
    return response
  }
  
  let html = await response.text()
  
  // 1. Inject preload links for critical resources
  html = injectPreloadLinks(html)
  
  // 2. Lazy load non-critical images
  html = addLazyLoading(html)
  
  // 3. Remove render-blocking CSS/JS
  html = deferNonCriticalResources(html)
  
  // 4. Add resource hints
  html = addResourceHints(html, request)
  
  return new Response(html, response)
}

function injectPreloadLinks(html) {
  // Find hero image (first content image)
  const heroImageMatch = html.match(/]+src="([^"]+)"[^>]*>/)
  
  if (heroImageMatch) {
    const preloadLink = `<link rel="preload" as="image" href="${heroImageMatch[1]}">`
    html = html.replace('</head>', `${preloadLink}</head>`)
  }
  
  return html
}

2. CLS (Cumulative Layout Shift) Prevention

// workers/cls-preventer.js
function addImageDimensions(html) {
  // Add width/height attributes to all images without them
  return html.replace(
    /])+src="([^"]+)"([^>]*)>/g,
    (match, before, src, after) => {
      // Fetch image dimensions (cached)
      const dimensions = getImageDimensions(src)
      
      if (dimensions) {
        return `<img${before}src="${src}" width="${dimensions.width}" height="${dimensions.height}"${after}>`
      }
      
      return match
    }
  )
}

function reserveSpaceForAds(html) {
  // Reserve space for dynamic ad units
  return html.replace(
    /
]*><\/div>/g, '<div class="ad-unit" style="min-height: 250px;"></div>' ) }

3. FID (First Input Delay) Improvement

// workers/fid-improver.js
function deferJavaScript(html) {
  // Add defer attribute to non-critical scripts
  return html.replace(
    /]+)src="([^"]+)">/g,
    (match, attributes, src) => {
      if (!src.includes('analytics') && !src.includes('critical')) {
        return `<script${attributes}src="${src}" defer>`
      }
      return match
    }
  )
}

function optimizeEventListeners(html) {
  // Replace inline event handlers with passive listeners
  return html.replace(
    /onscroll="([^"]+)"/g,
    'data-scroll-handler="$1"'
  ).replace(
    /onclick="([^"]+)"/g,
    'data-click-handler="$1"'
  )
}

Dynamic Schema Markup Generation

Generate structured data dynamically based on content and context:

// workers/schema-generator.js
async function generateDynamicSchema(request, html) {
  const url = new URL(request.url)
  const userAgent = request.headers.get('User-Agent')
  
  // Only generate for crawlers
  if (!isSearchEngineCrawler(userAgent)) {
    return html
  }
  
  // Extract page type from URL and content
  const pageType = determinePageType(url, html)
  
  // Generate appropriate schema
  const schema = await generateSchemaForPageType(pageType, url, html)
  
  // Inject into page
  return injectSchema(html, schema)
}

function determinePageType(url, html) {
  if (url.pathname.includes('/blog/') || url.pathname.includes('/post/')) {
    return 'Article'
  } else if (url.pathname.includes('/product/')) {
    return 'Product'
  } else if (url.pathname === '/') {
    return 'Website'
  } else if (html.includes('recipe')) {
    return 'Recipe'
  } else if (html.includes('faq') || html.includes('question')) {
    return 'FAQPage'
  }
  
  return 'WebPage'
}

async function generateSchemaForPageType(pageType, url, html) {
  const baseSchema = {
    "@context": "https://schema.org",
    "@type": pageType,
    "url": url.href,
    "datePublished": extractDatePublished(html),
    "dateModified": extractDateModified(html)
  }
  
  switch(pageType) {
    case 'Article':
      return {
        ...baseSchema,
        "headline": extractTitle(html),
        "description": extractDescription(html),
        "author": extractAuthor(html),
        "publisher": {
          "@type": "Organization",
          "name": "Your Site Name",
          "logo": {
            "@type": "ImageObject",
            "url": "https://yoursite.com/logo.png"
          }
        },
        "image": extractImages(html),
        "mainEntityOfPage": {
          "@type": "WebPage",
          "@id": url.href
        }
      }
      
    case 'FAQPage':
      const questions = extractFAQs(html)
      return {
        ...baseSchema,
        "mainEntity": questions.map(q => ({
          "@type": "Question",
          "name": q.question,
          "acceptedAnswer": {
            "@type": "Answer",
            "text": q.answer
          }
        }))
      }
      
    default:
      return baseSchema
  }
}

function injectSchema(html, schema) {
  const schemaScript = `<script type="application/ld+json">${JSON.stringify(schema, null, 2)}</script>`
  return html.replace('</head>', `${schemaScript}</head>`)
}

Intelligent Sitemap Generation and Management

Create dynamic sitemaps that reflect actual content importance:

// workers/dynamic-sitemap.js
addEventListener('fetch', event => {
  const url = new URL(event.request.url)
  
  if (url.pathname === '/sitemap.xml' || url.pathname.endsWith('sitemap.xml')) {
    event.respondWith(generateSitemap(event.request))
  } else {
    event.respondWith(fetch(event.request))
  }
})

async function generateSitemap(request) {
  // Fetch site content (from KV store or API)
  const pages = await getPagesFromKV()
  
  // Get traffic data for priority calculation
  const trafficData = await getTrafficData()
  
  // Generate sitemap with dynamic priorities
  const sitemap = generateXMLSitemap(pages, trafficData)
  
  return new Response(sitemap, {
    headers: {
      'Content-Type': 'application/xml',
      'Cache-Control': 'public, max-age=3600'
    }
  })
}

function generateXMLSitemap(pages, trafficData) {
  let xml = '<?xml version="1.0" encoding="UTF-8"?>\n'
  xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
  
  pages.forEach(page => {
    const priority = calculatePriority(page, trafficData)
    const changefreq = calculateChangeFrequency(page)
    
    xml += '  <url>\n'
    xml += `    <loc>${page.url}</loc>\n`
    xml += `    <lastmod>${page.lastmod}</lastmod>\n`
    xml += `    <changefreq>${changefreq}</changefreq>\n`
    xml += `    <priority>${priority}</priority>\n`
    xml += '  </url>\n'
  })
  
  xml += '</urlset>'
  return xml
}

function calculatePriority(page, trafficData) {
  // Base priority on actual traffic and importance
  const pageTraffic = trafficData[page.url] || 0
  const maxTraffic = Math.max(...Object.values(trafficData))
  
  let priority = 0.5 // Default
  
  if (page.url === '/') {
    priority = 1.0
  } else if (pageTraffic > maxTraffic * 0.1) { // Top 10% of traffic
    priority = 0.9
  } else if (pageTraffic > maxTraffic * 0.01) { // Top 1% of traffic
    priority = 0.7
  } else if (pageTraffic > 0) {
    priority = 0.5
  } else {
    priority = 0.3
  }
  
  return priority.toFixed(1)
}

function calculateChangeFrequency(page) {
  const now = new Date()
  const lastMod = new Date(page.lastmod)
  const daysSinceUpdate = (now - lastMod) / (1000 * 60 * 60 * 24)
  
  if (daysSinceUpdate < 7) return 'daily'
  if (daysSinceUpdate < 30) return 'weekly'
  if (daysSinceUpdate < 90) return 'monthly'
  return 'yearly'
}

International SEO Implementation

Implement hreflang and geo-targeting at the edge:

// workers/international-seo.js
const SUPPORTED_LOCALES = {
  'en': 'https://yoursite.com',
  'en-US': 'https://yoursite.com/us/',
  'en-GB': 'https://yoursite.com/uk/',
  'es': 'https://yoursite.com/es/',
  'fr': 'https://yoursite.com/fr/',
  'de': 'https://yoursite.com/de/'
}

addEventListener('fetch', event => {
  event.respondWith(handleInternationalRequest(event.request))
})

async function handleInternationalRequest(request) {
  const url = new URL(request.url)
  const userAgent = request.headers.get('User-Agent')
  
  // Add hreflang for crawlers
  if (isSearchEngineCrawler(userAgent)) {
    const response = await fetch(request)
    
    if (response.headers.get('Content-Type')?.includes('text/html')) {
      const html = await response.text()
      const enhancedHtml = addHreflangTags(html, url)
      
      return new Response(enhancedHtml, response)
    }
    
    return response
  }
  
  // Geo-redirect for users
  const country = request.headers.get('CF-IPCountry')
  const acceptLanguage = request.headers.get('Accept-Language')
  
  const targetLocale = determineBestLocale(country, acceptLanguage, url)
  
  if (targetLocale && targetLocale !== 'en') {
    // Redirect to localized version
    const localizedUrl = getLocalizedUrl(url, targetLocale)
    return Response.redirect(localizedUrl, 302)
  }
  
  return fetch(request)
}

function addHreflangTags(html, currentUrl) {
  let hreflangTags = ''
  
  Object.entries(SUPPORTED_LOCALES).forEach(([locale, baseUrl]) => {
    const localizedUrl = getLocalizedUrl(currentUrl, locale, baseUrl)
    hreflangTags += `<link rel="alternate" hreflang="${locale}" href="${localizedUrl}" />\n`
  })
  
  // Add x-default
  hreflangTags += `<link rel="alternate" hreflang="x-default" href="${SUPPORTED_LOCALES['en']}${currentUrl.pathname}" />\n`
  
  // Inject into head
  return html.replace('</head>', `${hreflangTags}</head>`)
}

function determineBestLocale(country, acceptLanguage, url) {
  // Country-based detection
  const countryToLocale = {
    'US': 'en-US',
    'GB': 'en-GB',
    'ES': 'es',
    'FR': 'fr',
    'DE': 'de'
  }
  
  if (country && countryToLocale[country]) {
    return countryToLocale[country]
  }
  
  // Language header detection
  if (acceptLanguage) {
    const languages = acceptLanguage.split(',')
    for (const lang of languages) {
      const locale = lang.split(';')[0].trim()
      if (SUPPORTED_LOCALES[locale]) {
        return locale
      }
    }
  }
  
  return null
}

Crawl Budget Optimization Techniques

Optimize how search engines crawl your site:

// workers/crawl-optimizer.js
addEventListener('fetch', event => {
  const url = new URL(event.request.url)
  const userAgent = event.request.headers.get('User-Agent')
  
  // Serve different robots.txt for different crawlers
  if (url.pathname === '/robots.txt') {
    event.respondWith(serveDynamicRobotsTxt(userAgent))
  }
  
  // Rate limit aggressive crawlers
  if (isAggressiveCrawler(userAgent)) {
    event.respondWith(handleAggressiveCrawler(event.request))
  }
})

async function serveDynamicRobotsTxt(userAgent) {
  let robotsTxt = `User-agent: *\n`
  robotsTxt += `Disallow: /admin/\n`
  robotsTxt += `Disallow: /private/\n`
  robotsTxt += `Allow: /$\n`
  robotsTxt += `\n`
  
  // Custom rules for specific crawlers
  if (userAgent.includes('Googlebot')) {
    robotsTxt += `User-agent: Googlebot\n`
    robotsTxt += `Allow: /\n`
    robotsTxt += `Crawl-delay: 1\n`
    robotsTxt += `\n`
  }
  
  if (userAgent.includes('Bingbot')) {
    robotsTxt += `User-agent: Bingbot\n`
    robotsTxt += `Allow: /\n`
    robotsTxt += `Crawl-delay: 2\n`
    robotsTxt += `\n`
  }
  
  // Block AI crawlers if desired
  if (isAICrawler(userAgent)) {
    robotsTxt += `User-agent: ${userAgent}\n`
    robotsTxt += `Disallow: /\n`
    robotsTxt += `\n`
  }
  
  robotsTxt += `Sitemap: https://yoursite.com/sitemap.xml\n`
  
  return new Response(robotsTxt, {
    headers: {
      'Content-Type': 'text/plain',
      'Cache-Control': 'public, max-age=86400'
    }
  })
}

async function handleAggressiveCrawler(request) {
  const crawlerKey = `crawler:${request.headers.get('CF-Connecting-IP')}`
  const requests = await CRAWLER_KV.get(crawlerKey)
  
  if (requests && parseInt(requests) > 100) {
    // Too many requests, serve 429
    return new Response('Too Many Requests', {
      status: 429,
      headers: {
        'Retry-After': '3600'
      }
    })
  }
  
  // Increment counter
  await CRAWLER_KV.put(crawlerKey, (parseInt(requests || 0) + 1).toString(), {
    expirationTtl: 3600
  })
  
  // Add crawl-delay header
  const response = await fetch(request)
  const newResponse = new Response(response.body, response)
  newResponse.headers.set('X-Robots-Tag', 'crawl-delay: 5')
  
  return newResponse
}

function isAICrawler(userAgent) {
  const aiCrawlers = [
    'GPTBot',
    'ChatGPT-User',
    'Google-Extended',
    'CCBot',
    'anthropic-ai'
  ]
  
  return aiCrawlers.some(crawler => userAgent.includes(crawler))
}

Start implementing edge SEO gradually. First, create a Worker that optimizes Core Web Vitals. Then implement dynamic sitemap generation. Finally, add international SEO support. Monitor search console for improvements in crawl stats, index coverage, and rankings. Each edge SEO improvement compounds, giving your static Jekyll site technical advantages over competitors.