Инженерлік блог

AdSense сіздің Core Web Vitals-ті өлтіріп жатыр ма? SSR-ді Google Cloud Run-ға көшіру арқылы жарнаманы жүктеуді қалай 3 есе жылдамдаттық

09.08.2026
Шарафутдинов Р.

Баннерлер құдайына көбірек баннерлер, немесе Lighthouse-ты қалай құлаттық

Әрбір медиа жоба немесе контенттік сайт ерте ме, кеш пе бірдей дилеммаға тап болады. Бір жағынан сізге монетизация керек. Сіз Google AdSense блоктарын ілесіз, алғашқы центтерге қуанасыз, сосын бизнес: «Келіңіздер, мына жерге тағы бір sticky-баннер, ал мына жерге бет аралық баннер қосайық» дейді. Екінші жағынан, іздеу жүйесіндегі орынға әсер ететін Google-дың сапа метрикалары — Core Web Vitals (CWV) туралы аузынан көбігі ағып айқайлайтын SEO-мамандар бар.

Мәселе неде? AdSense скрипттері (және кез келген басқа программатик-жарнама) — бұл асинхронды түрде жүктелетін, iframe-дерді көбейтетін, браузердің негізгі ағынын (Main Thread) бұғаттайтын және контентті аяусыз жылжытатын (сәлем, Cumulative Layout Shift — CLS) басқа біреудің ауыр JavaScript кесектері.

Біз Next.js-те ірі жаңалықтар порталын әзірлеп жатырмыз. Бастапқыда біз барлық беттерді классикалық SSR (Server-Side Rendering) арқылы әлсіз серверде рендерледік. Сайт жарнамасыз болған кезде біздің PageSpeed жасыл 95 балл көрсетті. Бірақ біз AdSense-ті қосып, метрикаларды ілген бойда Lighthouse мобильді құрылғылар үшін қызыл 35 балл көрсетті. LCP (Largest Contentful Paint) 5 секундтан асып кетті, ал TBT (Total Blocking Time) шарықтап кетті. Сайт тежеле бастады, скролл қатып қалды, ал пайдаланушылар мақаланың жүктелуін күтпей шығып кетіп жатты.

Шұғыл түрде бірдеңе жасау керек болды. Біз жарнамадан бас тарта алмадық (бизнес ақша тапқысы келеді). Бөтен adsbygoogle.js скриптін оңтайландыра алмаймыз — бұл блэкбокс. Жалғыз жол — контентті беру архитектурасын түбегейлі өзгерту.

Ауырсыну анатомиясы: Жарнама SSR-ді неге бұзады

Стандартты SSR және жарнамамен бетті жүктеу кезінде не болатынын талдайық.

  1. Пайдаланушы серверге сұрау жасайды.
  2. Сервер (Node.js) HTML жинайды (дерекқордан деректерді тартады, React-компоненттерін рендерлейді). Нашар серверде жоғары жүктеме кезінде бұл 300-500 мс алуы мүмкін.
  3. Браузер HTML алып, оны парсингтей бастайды.
  4. Браузер <script src="...adsbygoogle.js"> тегтеріне тап болып, жарнаманы жүктей бастайды.
  5. Параллель түрде гидратация үшін біздің негізгі JS-бандл (React/Next.js) жүктеледі.
  6. Осы кезде Main Thread үшін шайқас басталады. Жарнаманың ауыр скрипттері гидратация процесін бұғаттап, орындала бастайды. Жарнама рендерленбейінше сайттағы түймелер басылмайды.

Мәселе мынада, біздің ескі сервер трафик ағынынан жиі свопқа кетіп қалатын (жаңалықтар — болжанбайтын нәрсе, біреу Telegram-каналда сілтеме жарияласа, бір минутта бізге 10 000 адам келіп түседі). Сервердің ұзақ жауабы (TTFB — Time to First Byte) салдарынан браузер жарнаманы кешірек жүктей бастады, демек, біз ақша жоғалттық, өйткені юзер баннер көрсетілгенге дейін шығып кетуі мүмкін еді.

Құтқару жоспары: Бұлттарға көшу және жалқау жүктеу (Lazy Loading)

Бізге екі нәрсе керек екенін түсіндік:

  1. Қуатты және авто-масштабталатын SSR. Браузер HTML-ді мүмкіндігінше тез алып, жарнама скрипттерін жүктей бастауы үшін бізге ең жоғары жүктемелер кезінде де 50-100 мс шамасында TTFB (бірінші байтқа дейінгі уақыт) қажет болды.
  2. Жарнаманы қулықпен жүктеу. Біз жарнаманың гидратация мен LCP-ді бұғаттауына жол бере алмаймыз.

Жаңа инфрақұрылым ретінде біз Google Cloud Run-ды таңдадық. Бұл контейнерлерге арналған serverless-платформа. Сіз оған жай ғана өзіңіздің Next.js/Node.js қолданбаңыздың Docker-бейнесін бересіз, ал ол трафикке байланысты оны бірнеше секунд ішінде 0-ден 1000 данаға дейін өзі масштабтайды (scale). Тек сұрауды өңдеу уақыты үшін ғана төлейсіз. Трафигі тұрақсыз жаңалықтар сайттары үшін мінсіз шешім.

1-кезең: Next.js-ті Google Cloud Run-ға көшіру

Көшіру өте аз уақыт алды. Cloud Run Node.js қолданбаларын нативті түрде қолдайды. Біз қарапайым Dockerfile жаздық:

# Cloud Run-дағы Next.js үшін Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000
CMD ["node", "server.js"]

Маңызды нюанс: артық node_modules жоқ минималды бейнені (image) жинау үшін біз next.config.js ішіндегі output: 'standalone' мүмкіндігін пайдаландық. Бұл Cloud Run-дағы жылдам суық старт (cold start) үшін өте маңызды.

Осының бәрін Cloud Run-ға деплой жасадық, статиканы және сирек өзгеретін беттерді (ISR - Incremental Static Regeneration) кэштеу үшін үстіне Google Cloud CDN ілдік. Нәтиже? TTFB 400 мс-тен 60 мс-ке дейін төмендеді. Сервер лезде жауап бере бастады. Біз кез келген DDoS-қа немесе вирустық трафикке дайынбыз.

2-кезең: Қыңыр жарнаманы бағындыру (Lazy Loading AdSense)

Жылдам сервер — жұмыстың жартысы ғана. Егер біз қазір жай ғана AdSense скрипттерін <head>-ке салсақ, бәрібір TBT-ні өлтіреміз. Сондықтан біз кейінге қалдырылған инициализация тактикасын қолдандық.

Біз пайдаланушы бетті скролл жасай бастағанша немесе кез келген басқа әрекет (басу, тышқанды жылжыту) жасағанша adsbygoogle.js-ті жүктемейміз. Алғашқы экранды жүктеу кезінде әрбір килобайт маңызды болатын мобильді құрылғылар үшін бұл өте маңызды.

React/Next.js-те жалқау жүктеуді (lazy loading) осылай жүзеге асырдық:

// AdSense скриптін жалқау жүктеуге арналған хук
import { useEffect, useState } from 'react';

const useLazyAdSense = () => {
  const [adLoaded, setAdLoaded] = useState(false);

  useEffect(() => {
    const loadAds = () => {
      if (adLoaded) return;
      
      const script = document.createElement('script');
      script.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
      script.async = true;
      script.crossOrigin = 'anonymous';
      script.dataset.adClient = 'ca-pub-XXXXXXXXXXXXXXX'; // Сіздің ID
      
      document.head.appendChild(script);
      setAdLoaded(true);
      
      // Скриптті екі рет жүктемеу үшін оқиғалардан бас тартамыз
      window.removeEventListener('scroll', loadAds);
      window.removeEventListener('mousemove', loadAds);
      window.removeEventListener('touchstart', loadAds);
    };

    // Пайдаланушының алғашқы әрекеттерін тыңдаймыз
    window.addEventListener('scroll', loadAds, { once: true, passive: true });
    window.addEventListener('mousemove', loadAds, { once: true, passive: true });
    window.addEventListener('touchstart', loadAds, { once: true, passive: true });

    return () => {
      window.removeEventListener('scroll', loadAds);
      window.removeEventListener('mousemove', loadAds);
      window.removeEventListener('touchstart', loadAds);
    };
  }, [adLoaded]);

  return adLoaded;
};

Инженерлік лайфхак: браузердің скролын бұғаттамау үшін event listeners үшін { passive: true } пайдаланыңыз. Бұл Core Web Vitals кармасына тағы бір плюс.

3-кезең: Cumulative Layout Shift (CLS)-ті жеңу

Жарнама жалқау түрде жүктеледі — керемет. Бірақ ол жүктелгенде, контентті төмен жылжытып, <ins> блогын бірден керіп жібереді. Юзер мәтінді оқып отырғанда, сарт етеді де — бәрі төмен түсіп кетеді. Бұл CLS метрикасын өлтіреді. Мұндай нәрсе үшін Google іздеу нәтижелерінде қатаң жазалайды.

Шешімі қарапайым, бірақ неге екені белгісіз, көп адам бұл туралы ұмытып кетеді. CSS көмегімен баннерлерге орын брондау керек. Біз AdSense блоктарының өлшемдерін алдын ала білеміз (мысалы, мобильді құрылғылар үшін 300x250 немесе адаптивті). Егер блок адаптивті болса, секірулердің алдын алу үшін біз оған минималды биіктік береміз.

/* CLS-ті болдырмауға арналған CSS */
.ad-container {
  min-height: 250px; /* Орынды брондаймыз */
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #f8f9fa; /* Баннер жүктелгенше плейсхолдер */
}

React-компонентінде бұл былай көрінеді:

const AdUnit = ({ slotId }) => {
  useEffect(() => {
    // Скрипт жүктелгеннен кейін нақты блокты инициализациялау
    if (window.adsbygoogle && window.adsbygoogle.loaded) {
      try {
        (window.adsbygoogle = window.adsbygoogle || []).push({});
      } catch (e) {
        console.error("AdSense Error:", e);
      }
    }
  }, []);

  return (
    &lt;div className="ad-container my-8"&gt;
      &lt;ins
        className="adsbygoogle"
        style={{ display: 'block', minHeight: '250px' }}
        data-ad-client="ca-pub-XXXXXXXXXXXXXXX"
        data-ad-slot={slotId}
        data-ad-format="auto"
        data-full-width-responsive="true"
      /&gt;
    &lt;/div&gt;
  );
};

Көшіруге дейінгі және кейінгі LCP (Largest Contentful Paint)

Ең үлкен контенттің сызылу уақытын секундпен салыстыру.

Нәтижелер: Жасыл Lighthouse және кірістің өсуі

Google Cloud Run қуаты мен компоненттерді жалқау жүктеуді біріктіру арқылы біз қандай нәтижеге қол жеткіздік?

  • Core Web Vitals жасыл аймақта. LCP 5.2 секундтан 1.8 секундқа дейін төмендеді. CLS нөлге тең болды. TBT 4 есе төмендеді, өйткені бірінші жүктеу кезінде негізгі ағын бос.
  • Сайт ұша бастады. Пайдаланушылар контентті бірден алады, жарнама скрипттері қандай баннер көрсету керектігін шешкенше күтпей-ақ сілтемелерді басып, мәзірді аша алады.
  • Google бақытты, SEO өсіп жатыр. Іздеу нәтижелеріндегі (SERP) позициялар жақсарды, өйткені Google енді біздің сайтты жылдам әрі ыңғайлы деп санайды.
  • AdSense-тен түсетін кірістер төмендеген жоқ, керісінше өсті! Сіз: «Жарнама кешірек жүктеледі ғой, біз көрсетілімдерді жоғалтпаймыз ба?» деп сұрайтын шығарсыз. Жоқ! Сервердің 60 мс-те жауап беретіндігінің арқасында (Cloud Run мықты) және юзердің ақ экранды күтпейтіндігінен, ол сайтты жылдамырақ скролдап, өзара әрекеттесе бастайды, осылайша жарнаманың көрсетілуін триггерлейді. Сонымен қатар, бізде Active View метрикасы (нақты көріну аймағында болған баннерлердің пайызы) жақсарды, бұл үшін жарнама берушілер көбірек төлейді.

Core Web Vitals (TBT және CLS)

Жалқау жүктеу мен CSS-брондаудың ағынды бұғаттауға және ығысуларға әсері.

Қатал инженерлерге арналған түйіндеме

Оңтайландырылмайтын нәрсені (бөтен JS-ті) оңтайландыруға тырыспаңыз. Ойын ережелерін өзгертіңіз. HTML-ді лезде беру үшін рендеринг бойынша ауыр жұмысты авто-масштабталатын бұлтқа (Cloud Run + CDN) көшіріңіз. Пайдаланушының алғашқы әрекетіне дейін маңызды емес скрипттердің барлығын «мұздатып» қойыңыз. Контент секірмеуі үшін CSS арқылы пиксельдерді брондаңыз.

Контент билік жүргізетін және жарнама оның ақысын төлейтін әлемде UX пен монетизация арасындағы теңгерім қатаң архитектуралық тәртіп арқылы ғана жүзеге асады. Және иә, Cloud Run — бұл нағыз пушка. Біз енді вирустық трафиктен серверіміз құлады деген алерттерден түнде оянбайтын болдық. Ол жай ғана масштабталады, сұрауларды шайнайды, HTML береді және қайтадан жиналады. Керемет!

Рустам Шарафутдинов

Рустам Шарафутдинов

Инженерлік блог авторы

Google Cloud архитектурасы саласындағы сарапшы және 15 жылдан астам тәжірибесі бар Senior Full-Stack әзірлеушісі. Ақауға төзімді архитектураларға, жоғары жүктемелі жобаларды оңтайландыруға және AI (Vertex AI) интеграциясына маманданған.

Сараптама: GCP, Kubernetes, Микросервистер, React, Node.js

Пікірлер (0)