{"id":481836,"date":"2026-06-01T06:24:52","date_gmt":"2026-06-01T06:24:52","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=481836"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=481836","title":{"rendered":"\u041f\u043e\u0434\u043c\u0435\u043d\u0430 hero \u043d\u0430 edge \u043f\u043e UTM: Cloudflare Pages Functions + HTMLRewriter \u0434\u043b\u044f React SSG \u0437\u0430 200 \u0441\u0442\u0440\u043e\u043a"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><strong>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430.<\/strong> \u0423 \u0432\u0430\u0441 \u043e\u0434\u0438\u043d SSG-\u043b\u0435\u043d\u0434\u0438\u043d\u0433, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043b\u044c\u0451\u0442\u0441\u044f \u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0442\u0440\u0430\u0444\u0438\u043a \u0438\u0437 12 \u0440\u0430\u0437\u043d\u044b\u0445 \u0440\u0435\u043a\u043b\u0430\u043c\u043d\u044b\u0445 \u043a\u0430\u043c\u043f\u0430\u043d\u0438\u0439. \u041a\u0430\u0436\u0434\u0430\u044f \u0433\u0440\u0443\u043f\u043f\u0430 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0439 \u0441\u0434\u0435\u043b\u0430\u043d\u0430 \u043f\u043e\u0434 \u0441\u0432\u043e\u044e \u0431\u043e\u043b\u044c \u0426\u0410: \u00abAI-\u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0438\u00bb, \u00abAI-\u0430\u0433\u0435\u043d\u0442\u044b\u00bb, \u00ab\u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0441\u0435\u0441\u0441\u0438\u044f\u00bb, \u00ab\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0447\u0435\u0441\u043a\u0430\u044f \u043e\u0442\u0447\u0451\u0442\u043d\u043e\u0441\u0442\u044c\u00bb. \u0412\u0441\u0435 \u0432\u0435\u0434\u0443\u0442 \u043d\u0430 \u043e\u0434\u0438\u043d \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 hero \u00ab\u0418\u0418 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430\u00bb. \u041a\u043e\u043d\u0432\u0435\u0440\u0441\u0438\u044f \u0432 \u0437\u0430\u044f\u0432\u043a\u0443 \u043f\u0440\u043e\u0441\u0435\u0434\u0430\u0435\u0442 \u043d\u0430 30\u201350% \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 \u0440\u0430\u0437\u043d\u043e\u0442\u0435\u043c\u043d\u044b\u043c\u0438 \u043b\u0435\u043d\u0434\u0438\u043d\u0433\u0430\u043c\u0438 \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u0443\u044e \u0433\u0440\u0443\u043f\u043f\u0443. \u0414\u0435\u043b\u0430\u0442\u044c 12 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u043b\u0435\u043d\u0434\u0438\u043d\u0433\u043e\u0432 \u2014 \u0434\u043e\u0440\u043e\u0433\u043e \u043f\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0438 \u0443\u0431\u0438\u0432\u0430\u0435\u0442 SEO. \u041f\u043e\u0434\u043c\u0435\u043d\u044f\u0442\u044c hero JavaScript-\u043e\u043c \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u2014 FOUC, \u043f\u043b\u043e\u0445\u043e\u0439 Core Web Vitals, \u0438 \u042f\u043d\u0434\u0435\u043a\u0441\/Google \u0432\u0438\u0434\u044f\u0442 \u0434\u0435\u0444\u043e\u043b\u0442.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u2014 \u0440\u0430\u0431\u043e\u0447\u0430\u044f \u0441\u0445\u0435\u043c\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u0437\u0430 \u043e\u0434\u0438\u043d \u0434\u0435\u043d\u044c: <strong>edge-\u0444\u0443\u043d\u043a\u0446\u0438\u044f Cloudflare Pages \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 HTML \u043d\u0430 \u043b\u0435\u0442\u0443 \u0447\u0435\u0440\u0435\u0437 HTMLRewriter, SSG \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043f\u0435\u0440\u0432\u044b\u043c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c \u0438\u0441\u0442\u0438\u043d\u044b, client-side React \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0442\u0443 \u0436\u0435 \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0440\u0438 \u0433\u0438\u0434\u0440\u0430\u0442\u0430\u0446\u0438\u0438<\/strong>. 200 \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430, \u043d\u043e\u043b\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0441\u0432\u0435\u0440\u0445 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445, \u043b\u0430\u0442\u0435\u043d\u0441\u0438 \u0431\u0435\u0437 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 (HTMLRewriter \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0442\u043e\u043a\u043e\u043c), Lighthouse \u043d\u0435 \u0441\u0442\u0440\u0430\u0434\u0430\u0435\u0442.<\/p>\n<h3>\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u0438 \u043e\u0442\u0431\u0440\u043e\u0441\u0438\u043b\u0438<\/h3>\n<p><strong>\u0414\u0435\u043b\u0430\u0442\u044c N \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043b\u0435\u043d\u0434\u0438\u043d\u0433\u043e\u0432<\/strong> \u2014 \u0440\u0430\u0441\u0442\u0451\u0442 \u0441 \u0447\u0438\u0441\u043b\u043e\u043c UTM-\u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u043b\u0438\u043d\u0435\u0439\u043d\u043e, \u043b\u043e\u043c\u0430\u0435\u0442 \u043a\u0430\u043d\u043e\u043d\u0438\u043a\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e, \u0434\u0443\u0431\u043b\u0438\u0440\u0443\u0435\u0442 SEO-\u0441\u0438\u0433\u043d\u0430\u043b\u044b. \u0414\u043b\u044f 12 \u043a\u0430\u043c\u043f\u0430\u043d\u0438\u0439 \u2014 12 \u043a\u043e\u043f\u0438\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0434\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043a\u043e\u0433\u0434\u0430 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0431\u043b\u043e\u043a \u043d\u0438\u0436\u0435 hero.<\/p>\n<p><strong>Client-side \u043f\u043e\u0434\u043c\u0435\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 React.useEffect<\/strong> \u2014 FOUC: \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0438\u0434\u0438\u0442 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 hero, \u043f\u043e\u0442\u043e\u043c \u043e\u043d \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f. \u041d\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0438 \u0432\u0438\u0434\u043d\u0430 \u0432\u0441\u043f\u044b\u0448\u043a\u0430, \u043d\u0430 \u0431\u044b\u0441\u0442\u0440\u043e\u043c \u2014 \u0437\u0430\u043c\u0435\u0442\u043d\u0430, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e hero \u2014 \u044d\u0442\u043e \u043f\u0435\u0440\u0432\u043e\u0435 \u0447\u0442\u043e \u0433\u043b\u0430\u0437 \u0444\u043e\u043a\u0443\u0441\u0438\u0440\u0443\u0435\u0442. \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u2014 \u042f\u043d\u0434\u0435\u043a\u0441 \u0438 Google \u0432\u0438\u0434\u044f\u0442 \u043f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u0440\u0435\u043d\u0434\u0435\u0440\u0435 \u0434\u0435\u0444\u043e\u043b\u0442, \u0447\u0442\u043e \u0434\u043b\u044f SEO \u043d\u0435 \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e, \u043d\u043e \u0434\u043b\u044f \u0440\u0435\u043a\u043b\u0430\u043c\u043d\u044b\u0445 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c (Quality Score) \u2014 \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e.<\/p>\n<p><strong>Server-side \u0440\u0435\u043d\u0434\u0435\u0440 \u0441 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u0432 URL<\/strong> \u2014 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 Next.js \/ Remix \u0441 SSR, runtime-\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c, \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u0434\u0435\u043f\u043b\u043e\u0439. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 SSG (vite-react-ssg, Astro, Eleventy) \u2014 \u044d\u0442\u043e \u0448\u0430\u0433 \u043d\u0430\u0437\u0430\u0434.<\/p>\n<p><strong>Edge Workers \u0441 HTMLRewriter<\/strong> \u2014 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u043d\u0438\u0435 HTML \u043d\u0430 \u043f\u043e\u0442\u043e\u043a\u0435 \u043c\u0435\u0436\u0434\u0443 origin \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c. Latency-overhead \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u043c\u0438\u043b\u043b\u0438\u0441\u0435\u043a\u0443\u043d\u0434. SSG \u043a\u0430\u043a \u0431\u044b\u043b, \u0442\u0430\u043a \u0438 \u043e\u0441\u0442\u0430\u043b\u0441\u044f \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0432\u0435\u0440\u0445. \u042d\u0442\u043e \u0438 \u0435\u0441\u0442\u044c \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438.<\/p>\n<h3>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/h3>\n<pre><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510   GET \/?utm_offer=ai-agents   \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u2502   Browser       \u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25b6 \u2502  Cloudflare Edge     \u2502\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518                               \u2502                      \u2502        \u25b2                                          \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u2502        \u2502                                          \u2502  \u2502 _middleware.ts \u2502  \u2502        \u2502                                          \u2502  \u2502  \u0447\u0438\u0442\u0430\u0435\u0442 UTM,   \u2502  \u2502        \u2502                                          \u2502  \u2502  next() \u0432 SSG, \u2502  \u2502        \u2502                                          \u2502  \u2502  HTMLRewriter  \u2502  \u2502        \u2502                                          \u2502  \u2502  \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442  \u2502  \u2502        \u2502                                          \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518  \u2502        \u2502                                          \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518        \u2502                                                      \u2502        \u2502                                                      \u25bc        \u2502                                          \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510        \u2502                                          \u2502  Static SSG asset    \u2502        \u2502                                          \u2502  \/index.html         \u2502        \u2502   \u043f\u043e\u0434\u043c\u0435\u043d\u0451\u043d\u043d\u044b\u0439 HTML                       \u2502  \u0441 data-offer-slot=* \u2502        \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u043b\u044e\u0447\u0435\u0432\u0430\u044f \u0438\u0434\u0435\u044f \u2014 <strong>data-attribute \u044f\u043a\u043e\u0440\u044f<\/strong>. \u0412 React-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 Hero \u0441\u0442\u0430\u0432\u0438\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043d\u0430 DOM-\u0443\u0437\u043b\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f:<\/p>\n<pre><code>\/\/ src\/sections\/Hero\/Hero.tsximport { useSearchParams } from 'react-router-dom'import { resolveOffer } from '@\/data\/offers'export function Hero() {  const [searchParams] = useSearchParams()  const offer = resolveOffer(searchParams.get('utm_offer'))  return (    &lt;section&gt;      &lt;div data-offer-slot=\"eyebrow-wrap\"&gt;        &lt;span aria-hidden className=\"dot\" \/&gt;        &lt;span data-offer-slot=\"eyebrow\"&gt;{offer.eyebrow}&lt;\/span&gt;      &lt;\/div&gt;      &lt;h1&gt;        &lt;span data-offer-slot=\"h1\"&gt;{offer.h1}&lt;\/span&gt;        &lt;br \/&gt;        &lt;span data-offer-slot=\"h1-sub\"&gt;{offer.h1Sub}&lt;\/span&gt;      &lt;\/h1&gt;      &lt;p data-offer-slot=\"lede\"&gt;{offer.lede}&lt;\/p&gt;      &lt;a href=\"#cta\" className=\"btn-primary\"&gt;        &lt;span data-offer-slot=\"cta\"&gt;{offer.ctaText}&lt;\/span&gt;      &lt;\/a&gt;    &lt;\/section&gt;  )}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>data-offer-slot<\/code> \u2014 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0432\u0435\u0449\u044c, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0437\u043d\u0430\u044e\u0442 \u043e\u0431\u0430 \u0441\u043b\u043e\u044f: \u0438 React, \u0438 edge-\u0444\u0443\u043d\u043a\u0446\u0438\u044f. \u042d\u0442\u043e \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438.<\/p>\n<p>\u0427\u0443\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043f\u0440\u043e \u0441\u043b\u043e\u0442\u044b: \u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b <code>data-offer-slot=\"eyebrow\"<\/code> \u043d\u0430 <strong>\u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439<\/strong> span, \u0430 \u043d\u0435 \u043d\u0430 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 div. \u0415\u0441\u043b\u0438 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043d\u0430 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f, HTMLRewriter \u0437\u0430\u0442\u0440\u0451\u0442 decorative-\u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b (\u0442\u043e\u0447\u043a\u0430 <code>&lt;span aria-hidden&gt;<\/code> \u0441\u043b\u0435\u0432\u0430 \u043e\u0442 eyebrow). \u041f\u0440\u0430\u0432\u0438\u043b\u043e: \u0441\u043b\u043e\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c <strong>\u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0443\u0437\u0435\u043b<\/strong>, \u0431\u0435\u0437 \u0441\u0438\u0431\u043b\u0438\u043d\u0433\u043e\u0432.<\/p>\n<h3>Edge-\u0444\u0443\u043d\u043a\u0446\u0438\u044f<\/h3>\n<p>Cloudflare Pages \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435 <code>functions\/<\/code> \u0440\u044f\u0434\u043e\u043c \u0441 \u043a\u043e\u0434\u043e\u043c. \u0424\u0430\u0439\u043b <code>_middleware.ts<\/code> \u043e\u0442\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043f\u0443\u0442\u0435\u0439 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 (\u0435\u0441\u043b\u0438 \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0451\u043d <code>context.next()<\/code> \u044f\u0432\u043d\u043e).<\/p>\n<pre><code>\/\/ functions\/_middleware.tsimport { OFFERS, isOfferKey } from '..\/src\/data\/offers'export const onRequest: PagesFunction = async (context) =&gt; {  const url = new URL(context.request.url)  \/\/ \u041f\u043e\u0434\u043c\u0435\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043a\u043e\u0440\u043d\u0435. \u0414\u043b\u044f \/blog, \/guides \u0438 \u0442.\u043f.  \/\/ \u0443 \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0441\u0432\u043e\u0439 \u0441\u043c\u044b\u0441\u043b, hero \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0442\u044c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e.  if (url.pathname !== '\/' &amp;&amp; url.pathname !== '\/index.html') {    return context.next()  }  if (context.request.method !== 'GET') {    return context.next()  }  const utmOffer = url.searchParams.get('utm_offer')  if (!isOfferKey(utmOffer)) {    \/\/ \u0414\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 hero \u0443\u0436\u0435 \u0432 SSG \u2014 \u043e\u0442\u0434\u0430\u0451\u043c \u043a\u0430\u043a \u0435\u0441\u0442\u044c.    return context.next()  }  const offer = OFFERS[utmOffer]  const response = await context.next()  \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u0447\u0442\u043e \u044d\u0442\u043e HTML, \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0430\u0440\u0441\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437 HTMLRewriter.  const contentType = response.headers.get('Content-Type') ?? ''  if (!contentType.includes('text\/html')) {    return response  }  const rewriter = new HTMLRewriter()    .on('[data-offer-slot=\"eyebrow\"]', textReplacer(offer.eyebrow))    .on('[data-offer-slot=\"h1\"]', textReplacer(offer.h1))    .on('[data-offer-slot=\"h1-sub\"]', textReplacer(offer.h1Sub))    .on('[data-offer-slot=\"lede\"]', textReplacer(offer.lede))    .on('[data-offer-slot=\"cta\"]', textReplacer(offer.ctaText))  const rewritten = rewriter.transform(response)  \/\/ Cache-Control: private \u2014 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b hero \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f  \/\/ \u043d\u0430 CDN-\u0443\u0440\u043e\u0432\u043d\u0435 \u043a\u0430\u043a \u043e\u0431\u0449\u0438\u0439 \u0440\u0435\u0441\u0443\u0440\u0441.  const newHeaders = new Headers(rewritten.headers)  newHeaders.set('Cache-Control', 'private, no-store')  newHeaders.set('Vary', 'Accept-Encoding')  newHeaders.set('X-Offer-Variant', utmOffer)  return new Response(rewritten.body, {    status: rewritten.status,    statusText: rewritten.statusText,    headers: newHeaders,  })}function textReplacer(text: string) {  return {    element(el: Element) {      \/\/ html: false \u2014 escape'\u0438\u0442 \u0441\u043f\u0435\u0446\u0441\u0438\u043c\u0432\u043e\u043b\u044b, \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e \u0434\u043b\u044f XSS.      el.setInnerContent(text, { html: false })    },  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0427\u0442\u043e \u0432\u0430\u0436\u043d\u043e \u0432 \u044d\u0442\u043e\u043c \u043a\u043e\u0434\u0435:<\/p>\n<p><code><strong>return context.next()<\/strong><\/code><strong> \u043f\u044f\u0442\u044c \u0440\u0430\u0437 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435<\/strong> \u2014 \u044d\u0442\u043e early return \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0441\u043b\u0443\u0447\u0430\u0435\u0432, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u0434\u043c\u0435\u043d\u0430 \u043d\u0435 \u043d\u0443\u0436\u043d\u0430. \u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e edge-\u0444\u0443\u043d\u043a\u0446\u0438\u0439: \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u0430\u043c, \u0433\u0434\u0435 \u043d\u0435 \u043d\u0430\u0434\u043e. \u041b\u044e\u0431\u0430\u044f \u043b\u0438\u0448\u043d\u044f\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a TTFB.<\/p>\n<p><strong>HTMLRewriter \u2014 \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0441\u0435\u0440<\/strong>. \u041e\u043d \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0432\u0435\u0441\u044c HTML \u0432 \u043f\u0430\u043c\u044f\u0442\u044c, \u0430 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0442\u043e\u043a\u0435\u043d \u0437\u0430 \u0442\u043e\u043a\u0435\u043d\u043e\u043c. \u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e SSG-HTML (\u0443 \u043d\u0430\u0441 ~70 \u041a\u0411) \u044d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0447\u0442\u043e first-byte \u043e\u0442\u0434\u0430\u0451\u0442\u0441\u044f \u043f\u043e\u0447\u0442\u0438 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u044f \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u0430, \u0430 \u043d\u0435 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430. \u0417\u0430\u043c\u0435\u0440 \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u0441\u0430\u0439\u0442\u0435: +3\u20135 \u043c\u0441 \u043a TTFB \u043d\u0430 edge, \u043d\u0435\u0437\u0430\u043c\u0435\u0442\u043d\u043e.<\/p>\n<p><code><strong>setInnerContent(text, { html: false })<\/strong><\/code> \u2014 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c. HTMLRewriter \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 escape\u2019\u0438\u0442 <code>&lt;<\/code>, <code>&gt;<\/code>, <code>&amp;<\/code>, <code>\"<\/code> \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043d <code>{ html: false }<\/code>. \u042d\u0442\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e \u2014 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u0438\u0437 URL-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430, \u0434\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0438\u043c \u043d\u0435\u043b\u044c\u0437\u044f. \u0415\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u043e\u0442\u043a\u0440\u043e\u0435\u0442 <code>?utm_offer=&lt;script&gt;...&lt;\/script&gt;<\/code>, \u043c\u043e\u0439 <code>isOfferKey<\/code> \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u0442 \u044d\u0442\u043e \u0440\u0430\u043d\u044c\u0448\u0435, \u043d\u043e defence-in-depth \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043b\u0438\u0448\u043d\u0438\u0439.<\/p>\n<p><code><strong>Cache-Control: private, no-store<\/strong><\/code> \u2014 \u0434\u043b\u044f \u043f\u043e\u0434\u043c\u0435\u043d\u0451\u043d\u043d\u044b\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432. \u0411\u0435\u0437 \u044d\u0442\u043e\u0433\u043e CF-edge \u0437\u0430\u043a\u0435\u0448\u0438\u0440\u0443\u0435\u0442 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0441 <code>utm_offer=ai-agents<\/code> \u0438 \u043d\u0430\u0447\u043d\u0451\u0442 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u0435\u043b\u044f\u043c \u0442\u043e\u0433\u043e \u0436\u0435 edge-\u0443\u0437\u043b\u0430. SSG-\u0434\u0435\u0444\u043e\u043b\u0442 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f <code>public, cacheable<\/code>.<\/p>\n<p><strong>\u0418\u043c\u043f\u043e\u0440\u0442 <\/strong><code><strong>from '..\/src\/data\/offers'<\/strong><\/code> \u2014 Cloudflare Pages \u043f\u0440\u0438 \u0441\u0431\u043e\u0440\u043a\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0443\u043c\u0435\u0435\u0442 \u0431\u0430\u043d\u0434\u043b\u0438\u0442\u044c TypeScript-\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0438\u0437 \u0441\u043e\u0441\u0435\u0434\u043d\u0438\u0445 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u043e\u0432. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0438\u0441\u0442\u0438\u043d\u044b \u0434\u043b\u044f \u043e\u0444\u0444\u0435\u0440\u043e\u0432: \u0438 React, \u0438 edge \u0447\u0438\u0442\u0430\u044e\u0442 \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430. \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u2014 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 <code>functions\/_lib\/<\/code>, \u0447\u0442\u043e \u043c\u044b \u043f\u0440\u043e\u0431\u043e\u0432\u0430\u043b\u0438 \u0438 \u043e\u0442\u0431\u0440\u043e\u0441\u0438\u043b\u0438 \u0438\u0437-\u0437\u0430 \u0440\u0430\u0441\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0430.<\/p>\n<h3>\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0438\u0441\u0442\u0438\u043d\u044b: \u043c\u0430\u043f\u0430 \u043e\u0444\u0444\u0435\u0440\u043e\u0432<\/h3>\n<pre><code>\/\/ src\/data\/offers.tsexport type OfferKey =  | 'ai-employees'  | 'ai-agents'  | 'strat-session'  | 'analytics'  | 'automation'  | 'ai-crm'export type Offer = {  eyebrow: string  h1: string  h1Sub: string  lede: string  ctaText: string}export const DEFAULT_OFFER: Offer = {  eyebrow: '\u0414\u043b\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0438\u043a\u043e\u0432 \u00b7 \u0432\u044b\u0440\u0443\u0447\u043a\u0430 \u043e\u0442 50 \u043c\u043b\u043d \u20bd',  h1: '\u0418\u0418-\u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0438 \u0434\u043b\u044f \u0440\u043e\u0441\u0442\u0430 \u043c\u0430\u0440\u0436\u0438 \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.',  h1Sub: 'AI-\u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0441 KPI \u043d\u0430 P&amp;L. \u0418\u043d\u0436\u0438\u043d\u0438\u0440\u0438\u043d\u0433, \u043d\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f.',  lede: '\u2026',  ctaText: 'AI-\u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u0430 (30 \u043c\u0438\u043d, \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e)',}export const OFFERS: Record&lt;OfferKey, Offer&gt; = {  'ai-agents': {    eyebrow: '\u0414\u043b\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0438\u043a\u043e\u0432 \u00b7 \u0432\u044b\u0440\u0443\u0447\u043a\u0430 \u043e\u0442 50 \u043c\u043b\u043d \u20bd',    h1: '\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c AI-\u0430\u0433\u0435\u043d\u0442\u043e\u0432 \u043f\u043e\u0434 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u0430\u0448\u0435\u0433\u043e \u0431\u0438\u0437\u043d\u0435\u0441\u0430.',    h1Sub: '\u041e\u0442 \u0431\u043e\u0442\u0430-\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0434\u043e \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e\u0433\u043e \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430. \u0421 KPI \u043d\u0430 P&amp;L.',    lede: '\u2026',    ctaText: 'AI-\u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u0430 (30 \u043c\u0438\u043d, \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e)',  },  \/\/ \u2026 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 5 \u043e\u0444\u0444\u0435\u0440\u043e\u0432}export function isOfferKey(value: unknown): value is OfferKey {  return typeof value === 'string' &amp;&amp; value in OFFERS}export function resolveOffer(utmOffer: string | null | undefined): Offer {  if (utmOffer &amp;&amp; isOfferKey(utmOffer)) {    return OFFERS[utmOffer]  }  return DEFAULT_OFFER}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0434\u0438\u043d \u0444\u0430\u0439\u043b, \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043c\u0430\u043f, \u0434\u0432\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u0435\u043b\u044f (\u0442\u0438\u043f-guard <code>isOfferKey<\/code> \u0434\u043b\u044f edge \u0438 \u0441\u0430\u043c resolver <code>resolveOffer<\/code> \u0434\u043b\u044f React). \u041a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043e\u0444\u0444\u0435\u0440 \u2014 \u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u044d\u0442\u043e\u0442 \u0444\u0430\u0439\u043b, \u043f\u0435\u0440\u0435\u0431\u0438\u043b\u0434 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439.<\/p>\n<h3>\u0414\u0432\u043e\u0439\u043d\u0430\u044f \u0437\u0430\u0449\u0438\u0442\u0430: client-side \u043a\u0430\u043a fallback<\/h3>\n<p>Edge-\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0442\u0440\u0451\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445:<\/p>\n<ol>\n<li>\n<p>\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 <code>pnpm dev<\/code> \u2014 Vite \u043d\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 CF Pages Functions.<\/p>\n<\/li>\n<li>\n<p>Cloudflare Workers \u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0436\u0435\u043d (\u0440\u0435\u0434\u043a\u043e, \u043d\u043e \u0431\u044b\u0432\u0430\u0435\u0442).<\/p>\n<\/li>\n<li>\n<p>\u0422\u0435\u0441\u0442\u043e\u0432\u0430\u044f \u0432\u0435\u0442\u043a\u0430 \u0437\u0430\u0434\u0435\u043f\u043b\u043e\u0438\u043b\u0430\u0441\u044c \u0431\u0435\u0437 functions\/.<\/p>\n<\/li>\n<\/ol>\n<p>\u0427\u0442\u043e\u0431\u044b \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043e\u0444\u0444\u0435\u0440\u0430 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043b\u0441\u044f, React-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <strong>\u0442\u043e\u0436\u0435<\/strong> \u0447\u0438\u0442\u0430\u0435\u0442 <code>utm_offer<\/code> \u0447\u0435\u0440\u0435\u0437 <code>useSearchParams<\/code> \u0438 \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 hero \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435. \u042d\u0442\u043e <strong>idempotent<\/strong> \u2014 \u0435\u0441\u043b\u0438 edge \u0443\u0436\u0435 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043b HTML, React \u0432\u0438\u0434\u0438\u0442 \u0442\u0443 \u0436\u0435 \u0441\u0442\u0440\u043e\u043a\u0443 \u0432 DOM, \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 DOM \u043d\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f, \u043f\u0430\u0442\u0447 \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f. \u0415\u0441\u043b\u0438 \u043d\u0435 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043b \u2014 React \u0434\u0435\u043b\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u0440\u0438 \u0433\u0438\u0434\u0440\u0430\u0442\u0430\u0446\u0438\u0438.<\/p>\n<p>\u042d\u0442\u043e \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0432\u0430\u0436\u043d\u043e \u0434\u043b\u044f preview-deploy\u2019\u0435\u0432 \u0438 \u0434\u043b\u044f \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0448\u0430\u0440\u0438\u0442 URL <code>https:\/\/site.com\/?utm_offer=ai-agents<\/code> \u0434\u0440\u0443\u0437\u044c\u044f\u043c \u0431\u0435\u0437 proxy \u2014 friendly URL \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c \u0432\u0435\u0437\u0434\u0435.<\/p>\n<h3>\u0427\u0442\u043e \u044f \u0443\u0437\u043d\u0430\u043b \u0432 \u043f\u0440\u043e\u0434\u0435<\/h3>\n<p><strong>HTMLRewriter \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u043c\u0443\u043b\u044c\u0442\u0438-\u043d\u043e\u0434\u043d\u044b\u043c\u0438 \u0441\u043b\u043e\u0442\u0430\u043c\u0438.<\/strong> \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0432 DOM <code>&lt;div data-offer-slot=\"x\"&gt;&lt;span&gt;\u0447\u0430\u0441\u0442\u044c1&lt;\/span&gt; &lt;em&gt;\u0447\u0430\u0441\u0442\u044c2&lt;\/em&gt;&lt;\/div&gt;<\/code> \u2014 <code>setInnerContent<\/code> \u0437\u0430\u0442\u0440\u0451\u0442 \u0438 span, \u0438 em. \u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u043b\u043e\u0442 \u043d\u0430 \u043b\u0438\u0441\u0442 \u0434\u0435\u0440\u0435\u0432\u0430 (\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0443\u0437\u0435\u043b), \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u0441\u0438\u0431\u043b\u0438\u043d\u0433\u0438 \u0432\u044b\u043d\u043e\u0441\u0438\u0442\u044c \u0437\u0430 \u043f\u0440\u0435\u0434\u0435\u043b\u044b \u0441\u043b\u043e\u0442\u0430.<\/p>\n<p><strong>Cache-Control: private \u0432\u0430\u0436\u0435\u043d.<\/strong> \u0411\u0435\u0437 \u043d\u0435\u0433\u043e CF-edge \u0437\u0430\u043a\u0435\u0448\u0438\u0440\u0443\u0435\u0442 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442. \u042f \u044d\u0442\u043e\u0442 \u0448\u0430\u0433 \u0437\u0430\u0431\u044b\u043b \u0432 \u043f\u0435\u0440\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u2014 \u0434\u0432\u0430 \u0434\u043d\u044f \u0432\u0441\u0435 \u044e\u0437\u0435\u0440\u044b \u0441 edge-\u0443\u0437\u043b\u0430 Frankfurt \u0432\u0438\u0434\u0435\u043b\u0438 <code>?utm_offer=strat-session<\/code> \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 UTM. \u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u043f\u043e\u0434\u043c\u0435\u043d\u0443 \u0441 \u0440\u0430\u0437\u043d\u044b\u0445 IP \u0438 \u0440\u0430\u0437\u043d\u044b\u0445 \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0439.<\/p>\n<p><strong>vite-react-ssg \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 data-router (react-router-dom v6.4+).<\/strong> \u042d\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 side-effect: data-router \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043a\u043b\u0438\u043a\u0438 \u043d\u0430 <code>&lt;a href=\"\/...\"&gt;<\/code> \u0432\u043d\u0443\u0442\u0440\u0438 <code>&lt;RouterProvider&gt;<\/code>. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0432 <code>public\/<\/code> \u043b\u0435\u0436\u0438\u0442 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0432\u043d\u0435 SPA-routes \u2014 \u043a\u043b\u0438\u043a \u043d\u0430 \u0441\u0441\u044b\u043b\u043a\u0443 \u043a \u043d\u0435\u0439 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a 404 \u043e\u0442 React, \u0430 \u043d\u0435 \u043a \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430. \u041b\u0435\u0447\u0438\u0442\u0441\u044f <code>onClick={(e) =&gt; { e.preventDefault(); window.location.assign(href + '\/') }}<\/code> \u0434\u043b\u044f \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0443\u0442\u0435\u0439.<\/p>\n<p><strong>Bundle cache \u043d\u0430 JS-\u0444\u0430\u0439\u043b\u044b.<\/strong> Cloudflare \u0441\u0442\u0430\u0432\u0438\u0442 <code>Cache-Control: public, max-age=31536000, immutable<\/code> \u043d\u0430 <code>\/assets\/*.js<\/code>. \u042d\u0442\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e (hash \u0432 \u0438\u043c\u0435\u043d\u0438 \u0444\u0430\u0439\u043b\u0430 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442 cache-bust \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438), \u043d\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 DevTools \u0441 \u0432\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u043c \u043a\u044d\u0448\u0435\u043c \u2014 \u0441\u0442\u0430\u0440\u044b\u0439 JS-\u0431\u0430\u043d\u0434\u043b \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u043f\u043e\u0434\u0445\u0432\u0430\u0442\u0438\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u0434\u0435\u043f\u043b\u043e\u044f. Hard refresh \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d.<\/p>\n<p><strong>Edge-\u0446\u0435\u043d\u0430.<\/strong> Cloudflare Pages \u0434\u0430\u0451\u0442 100k \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0445 function-invocations\/\u0434\u0435\u043d\u044c. \u041f\u043e\u0434\u043c\u0435\u043d\u0430 hero \u2014 \u044d\u0442\u043e 1 invocation \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043a\u043e\u0440\u043d\u044f \u0441\u0430\u0439\u0442\u0430. \u041d\u0430 \u043d\u0430\u0448\u0435\u043c \u0442\u0440\u0430\u0444\u0438\u043a\u0435 (\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u044b\u0441\u044f\u0447 \u0432 \u0434\u0435\u043d\u044c) \u2014 \u0434\u0430\u043b\u0435\u043a\u043e \u043e\u0442 \u043b\u0438\u043c\u0438\u0442\u0430. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u044b PV \u2014 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043c\u044b\u0441\u043b \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 Cloudflare Workers Paid tier ($5\/\u043c\u0435\u0441 \u0437\u0430 10M invocations).<\/p>\n<h3>\u041a\u043e\u0433\u0434\u0430 \u0441\u0445\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442<\/h3>\n<p><strong>\u0415\u0441\u043b\u0438 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0441\u0438\u043b\u044c\u043d\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439.<\/strong> HTMLRewriter \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0442\u0435\u043a\u0441\u0442\u0430 \u0432\u043d\u0443\u0442\u0440\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u043c\u0435\u043d\u0438\u0442\u044c \u0446\u0435\u043b\u044b\u0435 \u0431\u043b\u043e\u043a\u0438 (\u0434\u0440\u0443\u0433\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0441\u0435\u043a\u0446\u0438\u0439, \u0434\u0440\u0443\u0433\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b) \u2014 \u044d\u0442\u043e \u043d\u0435 HTMLRewriter, \u044d\u0442\u043e R\/SSR.<\/p>\n<p><strong>\u0415\u0441\u043b\u0438 SEO \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 UTM-\u0432\u0430\u0440\u0438\u0430\u0446\u0438\u0438.<\/strong> \u041f\u043e\u0434\u043c\u0435\u043d\u0451\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 HMRR-\u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 <code>Cache-Control: private<\/code> \u2014 \u042f\u043d\u0434\u0435\u043a\u0441\/Google \u0435\u0433\u043e \u043d\u0435 \u0432\u0438\u0434\u044f\u0442. \u041e\u043d\u0438 \u0432\u0438\u0434\u044f\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 SSG. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e UTM-\u0432\u0430\u0440\u0438\u0430\u0446\u0438\u044f\u043c (\u0447\u0442\u043e \u043d\u0435\u043b\u043e\u0433\u0438\u0447\u043d\u043e, \u043d\u043e \u0431\u044b\u0432\u0430\u0435\u0442) \u2014 \u043d\u0443\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 URL\u2019\u044b.<\/p>\n<p><strong>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435 Cloudflare.<\/strong> Vercel Edge Functions \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442 \u043f\u043e\u0445\u043e\u0436\u0438\u0439 API (<code>new HTMLRewriter()<\/code>), \u043d\u043e API \u0447\u0443\u0442\u044c \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f. Netlify Edge Functions \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0447\u0435\u0440\u0435\u0437 Deno \u0438 <code>HTMLRewriter<\/code> \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0447\u0435\u0440\u0435\u0437 polyfill. AWS CloudFront Functions \u2014 \u043d\u0435\u0442 \u043d\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e HTMLRewriter, \u043d\u0430\u0434\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0451. \u0421\u0430\u043c\u044b\u0439 \u0447\u0438\u0441\u0442\u044b\u0439 \u0441\u0442\u0435\u043a \u2014 \u0438\u043c\u0435\u043d\u043d\u043e Cloudflare Pages.<\/p>\n<h3>\u0427\u0442\u043e \u0434\u0430\u043b\u044c\u0448\u0435<\/h3>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u0443 \u043d\u0430\u0441 6 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 hero \u043f\u043e\u0434 12 \u0433\u0440\u0443\u043f\u043f \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0439 (\u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0433\u0440\u0443\u043f\u043f\u044b \u0434\u0435\u043b\u044f\u0442 \u043e\u0444\u0444\u0435\u0440). \u0427\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f \u0447\u0442\u043e \u0441\u0445\u0435\u043c\u0430 \u0434\u0430\u0451\u0442 \u043f\u0440\u0438\u0440\u043e\u0441\u0442 \u043a\u043e\u043d\u0432\u0435\u0440\u0441\u0438\u0438 \u2014 \u043d\u0443\u0436\u043d\u043e: 1) \u0438\u0437\u043c\u0435\u0440\u0438\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u043a\u043e\u043d\u0432\u0435\u0440\u0441\u0438\u044e \u043d\u0430 \u0434\u0435\u0444\u043e\u043b\u0442\u0435, 2) \u0438\u0437\u043c\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u0439 UTM-\u0432\u0430\u0440\u0438\u0430\u0446\u0438\u0438, 3) \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u0441 A\/B-\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u043c \u0433\u0434\u0435 \u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0430 \u0442\u0440\u0430\u0444\u0438\u043a\u0430 \u043f\u043e \u0442\u043e\u0439 \u0436\u0435 UTM \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 hero. \u042d\u0442\u043e\u0442 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442 \u0432 \u043f\u043b\u0430\u043d\u0430\u0445, \u043e\u0442\u043f\u0438\u0448\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u043f\u043e\u0441\u0442\u043e\u043c \u0447\u0435\u0440\u0435\u0437 4\u20136 \u043d\u0435\u0434\u0435\u043b\u044c \u043a\u043e\u0433\u0434\u0430 \u0441\u043e\u0431\u0435\u0440\u0451\u043c \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0437\u043d\u0430\u0447\u0438\u043c\u043e\u0441\u0442\u044c.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u2014 \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u044b\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u043e\u0444\u0444\u0435\u0440\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u043a\u0441\u0442-\u0432-\u0433\u043e\u043b\u043e\u0441 \u043d\u0430 edge \u0434\u043b\u044f \u0442\u0435\u0445, \u043a\u0442\u043e \u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0441\u0430\u0439\u0442 \u0441 iPhone \u0432 \u043d\u0430\u0443\u0448\u043d\u0438\u043a\u0430\u0445. \u042d\u0442\u043e \u0443\u0436\u0435 next-next-step.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u043e\u0445\u043e\u0436\u0443\u044e edge-\u043f\u043e\u0434\u043c\u0435\u043d\u0443 \u2014 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u043f\u0440\u043e \u0434\u0440\u0443\u0433\u0438\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u044b. \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u2014 \u043f\u0440\u043e \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u0431\u043b\u043e\u043a\u0430\u043c\u0438 (\u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438), \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0443 HTMLRewriter \u0442\u0443\u0442 \u043c\u043d\u043e\u0433\u043e \u043d\u044e\u0430\u043d\u0441\u043e\u0432.<\/p>\n<p>\u041a\u043e\u0434 \u0432 \u043e\u0434\u043d\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438, \u0431\u0435\u0437 \u0441\u0435\u043a\u0440\u0435\u0442\u043e\u0432, \u043c\u043e\u0436\u043d\u043e \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434 \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442: \u044f \u043d\u0435 \u0432\u044b\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e \u0432\u0435\u0441\u044c \u0440\u0435\u043f\u043e \u0446\u0435\u043b\u0438\u043a\u043e\u043c (\u0442\u0430\u043c \u043c\u043d\u043e\u0433\u043e \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e), \u043d\u043e \u043a\u0443\u0441\u043e\u043a \u043f\u0440\u043e \u043f\u043e\u0434\u043c\u0435\u043d\u0443 \u2014 \u044d\u0442\u043e \u0440\u043e\u0432\u043d\u043e \u0442\u0435 200 \u0441\u0442\u0440\u043e\u043a \u0447\u0442\u043e \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u044b \u0432\u044b\u0448\u0435. \u041b\u0438\u0446\u0435\u043d\u0437\u0438\u044f MIT, \u043c\u043e\u0436\u0435\u0442\u0435 \u0431\u0440\u0430\u0442\u044c \u0438 \u0444\u043e\u0440\u043a\u0430\u0442\u044c.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u043e\u0445\u043e\u0436\u0443\u044e edge-\u043f\u043e\u0434\u043c\u0435\u043d\u0443 \u2014 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u043f\u0440\u043e \u0434\u0440\u0443\u0433\u0438\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u044b. \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u0440\u043e \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u0431\u043b\u043e\u043a\u0430\u043c\u0438 (\u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438) \u2014 \u0443 HTMLRewriter \u0442\u0443\u0442 \u043c\u043d\u043e\u0433\u043e \u043d\u044e\u0430\u043d\u0441\u043e\u0432.<\/p>\n<p><a href=\"https:\/\/github.com\/batyaro777\/cf-pages-utm-hero\" rel=\"noopener noreferrer nofollow\"><strong>\u041a\u043e\u0434 \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u0432 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/strong><\/a>  \u2014 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0430\u043c\u043e\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440: middleware \u0441 HTMLRewriter, \u043c\u0430\u043f\u0430 \u043e\u0444\u0444\u0435\u0440\u043e\u0432, React-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0441 data-offer-slot, \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b\u0439 HTML \u0431\u0435\u0437 React, README \u0441 \u0433\u0440\u0430\u0431\u043b\u044f\u043c\u0438 \u0438\u0437 \u043f\u0440\u043e\u0434\u0430. \u041b\u0438\u0446\u0435\u043d\u0437\u0438\u044f MIT, \u0431\u0435\u0440\u0438\u0442\u0435 \u0438 \u0444\u043e\u0440\u043a\u0430\u0439\u0442\u0435.<\/p>\n<p>Production-\u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043e \u043d\u0430 dnai.engineering \u2014 \u043e\u0442\u043a\u0440\u043e\u0439 <code>?utm_offer=ai-agents<\/code>, <code>?utm_offer=strat-session<\/code>, <code>?utm_offer=ai-employees<\/code> \u0438 \u0443\u0432\u0438\u0434\u0438\u0448\u044c \u043f\u043e\u0434\u043c\u0435\u043d\u0443 \u0432\u0436\u0438\u0432\u0443\u044e (\u0438\u043b\u0438 curl \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u0442\u0440\u0438 \u0440\u0430\u0437\u043d\u044b\u0445 h1 \u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443 \u0438 \u0442\u043e\u043c\u0443 \u0436\u0435 URL \u2014 \u044d\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u043e, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u043f\u043b\u0430\u0442\u043d\u043e\u0433\u043e \u0442\u0440\u0430\u0444\u0438\u043a\u0430).<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1041964\/\">https:\/\/habr.com\/ru\/articles\/1041964\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430. \u0423 \u0432\u0430\u0441 \u043e\u0434\u0438\u043d SSG-\u043b\u0435\u043d\u0434\u0438\u043d\u0433, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043b\u044c\u0451\u0442\u0441\u044f \u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0442\u0440\u0430\u0444\u0438\u043a \u0438\u0437 12 \u0440\u0430\u0437\u043d\u044b\u0445 \u0440\u0435\u043a\u043b\u0430\u043c\u043d\u044b\u0445 \u043a\u0430\u043c\u043f\u0430\u043d\u0438\u0439. \u041a\u0430\u0436\u0434\u0430\u044f \u0433\u0440\u0443\u043f\u043f\u0430 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0439 \u0441\u0434\u0435\u043b\u0430\u043d\u0430 \u043f\u043e\u0434 \u0441\u0432\u043e\u044e \u0431\u043e\u043b\u044c \u0426\u0410: \u00abAI-\u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0438\u00bb, \u00abAI-\u0430\u0433\u0435\u043d\u0442\u044b\u00bb, \u00ab\u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0441\u0435\u0441\u0441\u0438\u044f\u00bb, \u00ab\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0447\u0435\u0441\u043a\u0430\u044f \u043e\u0442\u0447\u0451\u0442\u043d\u043e\u0441\u0442\u044c\u00bb. \u0412\u0441\u0435 \u0432\u0435\u0434\u0443\u0442 \u043d\u0430 \u043e\u0434\u0438\u043d \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 hero \u00ab\u0418\u0418 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430\u00bb. \u041a\u043e\u043d\u0432\u0435\u0440\u0441\u0438\u044f \u0432 \u0437\u0430\u044f\u0432\u043a\u0443 \u043f\u0440\u043e\u0441\u0435\u0434\u0430\u0435\u0442 \u043d\u0430 30\u201350% \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 \u0440\u0430\u0437\u043d\u043e\u0442\u0435\u043c\u043d\u044b\u043c\u0438 \u043b\u0435\u043d\u0434\u0438\u043d\u0433\u0430\u043c\u0438 \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u0443\u044e \u0433\u0440\u0443\u043f\u043f\u0443. \u0414\u0435\u043b\u0430\u0442\u044c 12 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u043b\u0435\u043d\u0434\u0438\u043d\u0433\u043e\u0432 \u2014 \u0434\u043e\u0440\u043e\u0433\u043e \u043f\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0438 \u0443\u0431\u0438\u0432\u0430\u0435\u0442 SEO. \u041f\u043e\u0434\u043c\u0435\u043d\u044f\u0442\u044c hero JavaScript-\u043e\u043c \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u2014 FOUC, \u043f\u043b\u043e\u0445\u043e\u0439 Core Web Vitals, \u0438 \u042f\u043d\u0434\u0435\u043a\u0441\/Google \u0432\u0438\u0434\u044f\u0442 \u0434\u0435\u0444\u043e\u043b\u0442.\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u2014 \u0440\u0430\u0431\u043e\u0447\u0430\u044f \u0441\u0445\u0435\u043c\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u0437\u0430 \u043e\u0434\u0438\u043d \u0434\u0435\u043d\u044c: edge-\u0444\u0443\u043d\u043a\u0446\u0438\u044f Cloudflare Pages \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 HTML \u043d\u0430 \u043b\u0435\u0442\u0443 \u0447\u0435\u0440\u0435\u0437 HTMLRewriter, SSG \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043f\u0435\u0440\u0432\u044b\u043c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c \u0438\u0441\u0442\u0438\u043d\u044b, client-side React \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0442\u0443 \u0436\u0435 \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0440\u0438 \u0433\u0438\u0434\u0440\u0430\u0442\u0430\u0446\u0438\u0438. 200 \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430, \u043d\u043e\u043b\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0441\u0432\u0435\u0440\u0445 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445, \u043b\u0430\u0442\u0435\u043d\u0441\u0438 \u0431\u0435\u0437 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 (HTMLRewriter \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0442\u043e\u043a\u043e\u043c), Lighthouse \u043d\u0435 \u0441\u0442\u0440\u0430\u0434\u0430\u0435\u0442.\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u0438 \u043e\u0442\u0431\u0440\u043e\u0441\u0438\u043b\u0438\u0414\u0435\u043b\u0430\u0442\u044c N \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043b\u0435\u043d\u0434\u0438\u043d\u0433\u043e\u0432 \u2014 \u0440\u0430\u0441\u0442\u0451\u0442 \u0441 \u0447\u0438\u0441\u043b\u043e\u043c UTM-\u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u043b\u0438\u043d\u0435\u0439\u043d\u043e, \u043b\u043e\u043c\u0430\u0435\u0442 \u043a\u0430\u043d\u043e\u043d\u0438\u043a\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e, \u0434\u0443\u0431\u043b\u0438\u0440\u0443\u0435\u0442 SEO-\u0441\u0438\u0433\u043d\u0430\u043b\u044b. \u0414\u043b\u044f 12 \u043a\u0430\u043c\u043f\u0430\u043d\u0438\u0439 \u2014 12 \u043a\u043e\u043f\u0438\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0434\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043a\u043e\u0433\u0434\u0430 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0431\u043b\u043e\u043a \u043d\u0438\u0436\u0435 hero.Client-side \u043f\u043e\u0434\u043c\u0435\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 React.useEffect \u2014 FOUC: \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0438\u0434\u0438\u0442 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 hero, \u043f\u043e\u0442\u043e\u043c \u043e\u043d \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f. \u041d\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0438 \u0432\u0438\u0434\u043d\u0430 \u0432\u0441\u043f\u044b\u0448\u043a\u0430, \u043d\u0430 \u0431\u044b\u0441\u0442\u0440\u043e\u043c \u2014 \u0437\u0430\u043c\u0435\u0442\u043d\u0430, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e hero \u2014 \u044d\u0442\u043e \u043f\u0435\u0440\u0432\u043e\u0435 \u0447\u0442\u043e \u0433\u043b\u0430\u0437 \u0444\u043e\u043a\u0443\u0441\u0438\u0440\u0443\u0435\u0442. \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u2014 \u042f\u043d\u0434\u0435\u043a\u0441 \u0438 Google \u0432\u0438\u0434\u044f\u0442 \u043f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u0440\u0435\u043d\u0434\u0435\u0440\u0435 \u0434\u0435\u0444\u043e\u043b\u0442, \u0447\u0442\u043e \u0434\u043b\u044f SEO \u043d\u0435 \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e, \u043d\u043e \u0434\u043b\u044f \u0440\u0435\u043a\u043b\u0430\u043c\u043d\u044b\u0445 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c (Quality Score) \u2014 \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e.Server-side \u0440\u0435\u043d\u0434\u0435\u0440 \u0441 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u0432 URL \u2014 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 Next.js \/ Remix \u0441 SSR, runtime-\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c, \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u0434\u0435\u043f\u043b\u043e\u0439. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 SSG (vite-react-ssg, Astro, Eleventy) \u2014 \u044d\u0442\u043e \u0448\u0430\u0433 \u043d\u0430\u0437\u0430\u0434.Edge Workers \u0441 HTMLRewriter \u2014 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u043d\u0438\u0435 HTML \u043d\u0430 \u043f\u043e\u0442\u043e\u043a\u0435 \u043c\u0435\u0436\u0434\u0443 origin \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c. Latency-overhead \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u043c\u0438\u043b\u043b\u0438\u0441\u0435\u043a\u0443\u043d\u0434. SSG \u043a\u0430\u043a \u0431\u044b\u043b, \u0442\u0430\u043a \u0438 \u043e\u0441\u0442\u0430\u043b\u0441\u044f \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0432\u0435\u0440\u0445. \u042d\u0442\u043e \u0438 \u0435\u0441\u0442\u044c \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438.\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510   GET \/?utm_offer=ai-agents   \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u2502   Browser       \u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25b6 \u2502  Cloudflare Edge     \u2502\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518                               \u2502                      \u2502        \u25b2                                          \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u2502        \u2502                                          \u2502  \u2502 _middleware.ts \u2502  \u2502        \u2502                                          \u2502  \u2502  \u0447\u0438\u0442\u0430\u0435\u0442 UTM,   \u2502  \u2502        \u2502                                          \u2502  \u2502  next() \u0432 SSG, \u2502  \u2502        \u2502                                          \u2502  \u2502  HTMLRewriter  \u2502  \u2502        \u2502                                          \u2502  \u2502  \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442  \u2502  \u2502        \u2502                                          \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518  \u2502        \u2502                                          \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518        \u2502                                                      \u2502        \u2502                                                      \u25bc        \u2502                                          \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510        \u2502                                          \u2502  Static SSG asset    \u2502        \u2502                                          \u2502  \/index.html         \u2502        \u2502   \u043f\u043e\u0434\u043c\u0435\u043d\u0451\u043d\u043d\u044b\u0439 HTML                       \u2502  \u0441 data-offer-slot=* \u2502        \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\u041a\u043b\u044e\u0447\u0435\u0432\u0430\u044f \u0438\u0434\u0435\u044f \u2014 data-attribute \u044f\u043a\u043e\u0440\u044f. \u0412 React-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 Hero \u0441\u0442\u0430\u0432\u0438\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043d\u0430 DOM-\u0443\u0437\u043b\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f:\/\/ src\/sections\/Hero\/Hero.tsximport { useSearchParams } from &#8216;react-router-dom&#8217;import { resolveOffer } from &#8216;@\/data\/offers&#8217;export function Hero() {  const [searchParams] = useSearchParams()  const offer = resolveOffer(searchParams.get(&#8216;utm_offer&#8217;))  return (    &lt;section&gt;      &lt;div data-offer-slot=&#187;eyebrow-wrap&#187;&gt;        &lt;span aria-hidden className=&#187;dot&#187; \/&gt;        &lt;span data-offer-slot=&#187;eyebrow&#187;&gt;{offer.eyebrow}&lt;\/span&gt;      &lt;\/div&gt;      &lt;h1&gt;        &lt;span data-offer-slot=&#187;h1&#8243;&gt;{offer.h1}&lt;\/span&gt;        &lt;br \/&gt;        &lt;span data-offer-slot=&#187;h1-sub&#187;&gt;{offer.h1Sub}&lt;\/span&gt;      &lt;\/h1&gt;      &lt;p data-offer-slot=&#187;lede&#187;&gt;{offer.lede}&lt;\/p&gt;      &lt;a href=&#187;#cta&#187; className=&#187;btn-primary&#187;&gt;        &lt;span data-offer-slot=&#187;cta&#187;&gt;{offer.ctaText}&lt;\/span&gt;      &lt;\/a&gt;    &lt;\/section&gt;  )}data-offer-slot \u2014 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0432\u0435\u0449\u044c, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0437\u043d\u0430\u044e\u0442 \u043e\u0431\u0430 \u0441\u043b\u043e\u044f: \u0438 React, \u0438 edge-\u0444\u0443\u043d\u043a\u0446\u0438\u044f. \u042d\u0442\u043e \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438.\u0427\u0443\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043f\u0440\u043e \u0441\u043b\u043e\u0442\u044b: \u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b data-offer-slot=&#187;eyebrow&#187; \u043d\u0430 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 span, \u0430 \u043d\u0435 \u043d\u0430 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 div. \u0415\u0441\u043b\u0438 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043d\u0430 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f, HTMLRewriter \u0437\u0430\u0442\u0440\u0451\u0442 decorative-\u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b (\u0442\u043e\u0447\u043a\u0430 &lt;span aria-hidden&gt; \u0441\u043b\u0435\u0432\u0430 \u043e\u0442 eyebrow). \u041f\u0440\u0430\u0432\u0438\u043b\u043e: \u0441\u043b\u043e\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0443\u0437\u0435\u043b, \u0431\u0435\u0437 \u0441\u0438\u0431\u043b\u0438\u043d\u0433\u043e\u0432.Edge-\u0444\u0443\u043d\u043a\u0446\u0438\u044fCloudflare Pages \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435 functions\/ \u0440\u044f\u0434\u043e\u043c \u0441 \u043a\u043e\u0434\u043e\u043c. \u0424\u0430\u0439\u043b _middleware.ts \u043e\u0442\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043f\u0443\u0442\u0435\u0439 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 (\u0435\u0441\u043b\u0438 \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0451\u043d context.next() \u044f\u0432\u043d\u043e).\/\/ functions\/_middleware.tsimport { OFFERS, isOfferKey } from &#8216;..\/src\/data\/offers&#8217;export const onRequest: PagesFunction = async (context) =&gt; {  const url = new URL(context.request.url)  \/\/ \u041f\u043e\u0434\u043c\u0435\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043a\u043e\u0440\u043d\u0435. \u0414\u043b\u044f \/blog, \/guides \u0438 \u0442.\u043f.  \/\/ \u0443 \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0441\u0432\u043e\u0439 \u0441\u043c\u044b\u0441\u043b, hero \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0442\u044c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e.  if (url.pathname !== &#8216;\/&#8217; &amp;&amp; url.pathname !== &#8216;\/index.html&#8217;) {    return context.next()  }  if (context.request.method !== &#8216;GET&#8217;) {    return context.next()  }  const utmOffer = url.searchParams.get(&#8216;utm_offer&#8217;)  if (!isOfferKey(utmOffer)) {    \/\/ \u0414\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 hero \u0443\u0436\u0435 \u0432 SSG \u2014 \u043e\u0442\u0434\u0430\u0451\u043c \u043a\u0430\u043a \u0435\u0441\u0442\u044c.    return context.next()  }  const offer = OFFERS[utmOffer]  const response = await context.next()  \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u0447\u0442\u043e \u044d\u0442\u043e HTML, \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0430\u0440\u0441\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437 HTMLRewriter.  const contentType = response.headers.get(&#8216;Content-Type&#8217;) ?? &#187;  if (!contentType.includes(&#8216;text\/html&#8217;)) {    return response  }  const rewriter = new HTMLRewriter()    .on(&#8216;[data-offer-slot=&#187;eyebrow&#187;]&#8217;, textReplacer(offer.eyebrow))    .on(&#8216;[data-offer-slot=&#187;h1&#8243;]&#8217;, textReplacer(offer.h1))    .on(&#8216;[data-offer-slot=&#187;h1-sub&#187;]&#8217;, textReplacer(offer.h1Sub))    .on(&#8216;[data-offer-slot=&#187;lede&#187;]&#8217;, textReplacer(offer.lede))    .on(&#8216;[data-offer-slot=&#187;cta&#187;]&#8217;, textReplacer(offer.ctaText))  const rewritten = rewriter.transform(response)  \/\/ Cache-Control: private \u2014 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b hero \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f  \/\/ \u043d\u0430 CDN-\u0443\u0440\u043e\u0432\u043d\u0435 \u043a\u0430\u043a \u043e\u0431\u0449\u0438\u0439 \u0440\u0435\u0441\u0443\u0440\u0441.  const newHeaders = new Headers(rewritten.headers)  newHeaders.set(&#8216;Cache-Control&#8217;, &#8216;private, no-store&#8217;)  newHeaders.set(&#8216;Vary&#8217;, &#8216;Accept-Encoding&#8217;)  newHeaders.set(&#8216;X-Offer-Variant&#8217;, utmOffer)  return new Response(rewritten.body, {    status: rewritten.status,    statusText: rewritten.statusText,    headers: newHeaders,  })}function textReplacer(text: string) {  return {    element(el: Element) {      \/\/ html: false \u2014 escape&#8217;\u0438\u0442 \u0441\u043f\u0435\u0446\u0441\u0438\u043c\u0432\u043e\u043b\u044b, \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e \u0434\u043b\u044f XSS.      el.setInnerContent(text, { html: false })    },  }}\u0427\u0442\u043e \u0432\u0430\u0436\u043d\u043e \u0432 \u044d\u0442\u043e\u043c \u043a\u043e\u0434\u0435:return context.next() \u043f\u044f\u0442\u044c \u0440\u0430\u0437 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u2014 \u044d\u0442\u043e early return \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0441\u043b\u0443\u0447\u0430\u0435\u0432, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u0434\u043c\u0435\u043d\u0430 \u043d\u0435 \u043d\u0443\u0436\u043d\u0430. \u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e edge-\u0444\u0443\u043d\u043a\u0446\u0438\u0439: \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u0430\u043c, \u0433\u0434\u0435 \u043d\u0435 \u043d\u0430\u0434\u043e. \u041b\u044e\u0431\u0430\u044f \u043b\u0438\u0448\u043d\u044f\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a TTFB.HTMLRewriter \u2014 \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0441\u0435\u0440. \u041e\u043d \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0432\u0435\u0441\u044c HTML \u0432 \u043f\u0430\u043c\u044f\u0442\u044c, \u0430 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0442\u043e\u043a\u0435\u043d \u0437\u0430 \u0442\u043e\u043a\u0435\u043d\u043e\u043c. \u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e SSG-HTML (\u0443 \u043d\u0430\u0441 ~70 \u041a\u0411) \u044d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0447\u0442\u043e first-byte \u043e\u0442\u0434\u0430\u0451\u0442\u0441\u044f \u043f\u043e\u0447\u0442\u0438 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u044f \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u0430, \u0430 \u043d\u0435 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430. \u0417\u0430\u043c\u0435\u0440 \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u0441\u0430\u0439\u0442\u0435: +3\u20135 \u043c\u0441 \u043a TTFB \u043d\u0430 edge, \u043d\u0435\u0437\u0430\u043c\u0435\u0442\u043d\u043e.setInnerContent(text, { html: false }) \u2014 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c. HTMLRewriter \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 escape\u2019\u0438\u0442 &lt;, &gt;, &amp;, &#187; \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043d { html: false }. \u042d\u0442\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e \u2014 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u0438\u0437 URL-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430, \u0434\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0438\u043c \u043d\u0435\u043b\u044c\u0437\u044f. \u0415\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u043e\u0442\u043a\u0440\u043e\u0435\u0442 ?utm_offer=&lt;script&gt;&#8230;&lt;\/script&gt;, \u043c\u043e\u0439 isOfferKey \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u0442 \u044d\u0442\u043e \u0440\u0430\u043d\u044c\u0448\u0435, \u043d\u043e defence-in-depth \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043b\u0438\u0448\u043d\u0438\u0439.Cache-Control: private, no-store \u2014 \u0434\u043b\u044f \u043f\u043e\u0434\u043c\u0435\u043d\u0451\u043d\u043d\u044b\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432. \u0411\u0435\u0437 \u044d\u0442\u043e\u0433\u043e CF-edge \u0437\u0430\u043a\u0435\u0448\u0438\u0440\u0443\u0435\u0442 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0441 utm_offer=ai-agents \u0438 \u043d\u0430\u0447\u043d\u0451\u0442 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u0435\u043b\u044f\u043c \u0442\u043e\u0433\u043e \u0436\u0435 edge-\u0443\u0437\u043b\u0430. SSG-\u0434\u0435\u0444\u043e\u043b\u0442 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f public, cacheable.\u0418\u043c\u043f\u043e\u0440\u0442 from &#8216;..\/src\/data\/offers&#8217; \u2014 Cloudflare Pages \u043f\u0440\u0438 \u0441\u0431\u043e\u0440\u043a\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0443\u043c\u0435\u0435\u0442 \u0431\u0430\u043d\u0434\u043b\u0438\u0442\u044c TypeScript-\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0438\u0437 \u0441\u043e\u0441\u0435\u0434\u043d\u0438\u0445 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u043e\u0432. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0438\u0441\u0442\u0438\u043d\u044b \u0434\u043b\u044f \u043e\u0444\u0444\u0435\u0440\u043e\u0432: \u0438 React, \u0438 edge \u0447\u0438\u0442\u0430\u044e\u0442 \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430. \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u2014 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 functions\/_lib\/, \u0447\u0442\u043e \u043c\u044b \u043f\u0440\u043e\u0431\u043e\u0432\u0430\u043b\u0438 \u0438 \u043e\u0442\u0431\u0440\u043e\u0441\u0438\u043b\u0438 \u0438\u0437-\u0437\u0430 \u0440\u0430\u0441\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0430.\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0438\u0441\u0442\u0438\u043d\u044b: \u043c\u0430\u043f\u0430 \u043e\u0444\u0444\u0435\u0440\u043e\u0432\/\/ src\/data\/offers.tsexport type OfferKey =  | &#8216;ai-employees&#8217;  | &#8216;ai-agents&#8217;  | &#8216;strat-session&#8217;  | &#8216;analytics&#8217;  | &#8216;automation&#8217;  | &#8216;ai-crm&#8217;export type Offer = {  eyebrow: string  h1: string  h1Sub: string  lede: string  ctaText: string}export const DEFAULT_OFFER: Offer = {  eyebrow: &#8216;\u0414\u043b\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0438\u043a\u043e\u0432 \u00b7 \u0432\u044b\u0440\u0443\u0447\u043a\u0430 \u043e\u0442 50 \u043c\u043b\u043d \u20bd&#8217;,  h1: &#8216;\u0418\u0418-\u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0438 \u0434\u043b\u044f \u0440\u043e\u0441\u0442\u0430 \u043c\u0430\u0440\u0436\u0438 \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.&#8217;,  h1Sub: &#8216;AI-\u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0441 KPI \u043d\u0430 P&amp;L. \u0418\u043d\u0436\u0438\u043d\u0438\u0440\u0438\u043d\u0433, \u043d\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f.&#8217;,  lede: &#8216;\u2026&#8217;,  ctaText: &#8216;AI-\u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u0430 (30 \u043c\u0438\u043d, \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e)&#8217;,}export const OFFERS: Record&lt;OfferKey, Offer&gt; = {  &#8216;ai-agents&#8217;: {    eyebrow: &#8216;\u0414\u043b\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0438\u043a\u043e\u0432 \u00b7 \u0432\u044b\u0440\u0443\u0447\u043a\u0430 \u043e\u0442 50 \u043c\u043b\u043d \u20bd&#8217;,    h1: &#8216;\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c AI-\u0430\u0433\u0435\u043d\u0442\u043e\u0432 \u043f\u043e\u0434 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u0430\u0448\u0435\u0433\u043e \u0431\u0438\u0437\u043d\u0435\u0441\u0430.&#8217;,    h1Sub: &#8216;\u041e\u0442 \u0431\u043e\u0442\u0430-\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0434\u043e \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e\u0433\u043e \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430. \u0421 KPI \u043d\u0430 P&amp;L.&#8217;,    lede: &#8216;\u2026&#8217;,    ctaText: &#8216;AI-\u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u0430 (30 \u043c\u0438\u043d, \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e)&#8217;,  },  \/\/ \u2026 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 5 \u043e\u0444\u0444\u0435\u0440\u043e\u0432}export function isOfferKey(value: unknown): value is OfferKey {  return typeof value === &#8216;string&#8217; &amp;&amp; value in OFFERS}export function resolveOffer(utmOffer: string | null | undefined): Offer {  if (utmOffer &amp;&amp; isOfferKey(utmOffer)) {    return OFFERS[utmOffer]  }  return DEFAULT_OFFER}\u041e\u0434\u0438\u043d \u0444\u0430\u0439\u043b, \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043c\u0430\u043f, \u0434\u0432\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u0435\u043b\u044f (\u0442\u0438\u043f-guard isOfferKey \u0434\u043b\u044f edge \u0438 \u0441\u0430\u043c resolver resolveOffer \u0434\u043b\u044f React). \u041a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043e\u0444\u0444\u0435\u0440 \u2014 \u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u044d\u0442\u043e\u0442 \u0444\u0430\u0439\u043b, \u043f\u0435\u0440\u0435\u0431\u0438\u043b\u0434 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439.\u0414\u0432\u043e\u0439\u043d\u0430\u044f \u0437\u0430\u0449\u0438\u0442\u0430: client-side \u043a\u0430\u043a fallbackEdge-\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0442\u0440\u0451\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445:\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 pnpm dev \u2014 Vite \u043d\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 CF Pages Functions.Cloudflare Workers \u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0436\u0435\u043d (\u0440\u0435\u0434\u043a\u043e, \u043d\u043e \u0431\u044b\u0432\u0430\u0435\u0442).\u0422\u0435\u0441\u0442\u043e\u0432\u0430\u044f \u0432\u0435\u0442\u043a\u0430 \u0437\u0430\u0434\u0435\u043f\u043b\u043e\u0438\u043b\u0430\u0441\u044c \u0431\u0435\u0437 functions\/.\u0427\u0442\u043e\u0431\u044b \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043e\u0444\u0444\u0435\u0440\u0430 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043b\u0441\u044f, React-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0442\u043e\u0436\u0435 \u0447\u0438\u0442\u0430\u0435\u0442&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-481836","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/481836","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=481836"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/481836\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=481836"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=481836"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=481836"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}