PWA и SEO: как я научился избегать ошибок при переходе на оффлайн-режим

Я уже пять лет совмещаю разработку Progressive Web Apps (PWA) с SEO-оптимизацией. Сегодня хочу поделиться своим опытом перехода на оффлайн-режим без потери видимости в поисковиках. Да, это возможно, но только если не наступать на те же грабли, что и я. Поехали!

По какой причине PWA и SEO это не враги, а партнеры

Примерно через месяц Яндекс.Вебмастер показал, что 30% страниц выпали из индекса. Паника. Оказалось, я забыл, что поисковые боты не пользователи. Они не ждут, пока Service Worker загрузит контент. Им нужно сразу получать HTML, а не пустой shell. Так начался мой путь к балансу между оффлайн-возможностями и SEO.

Ошибка №1: Service Worker, который «ломает» индексирование

Проблема:
Мой первый Service Worker кэшировал только статику (CSS, JS), но не HTML. Для пользователя всё работало идеально: страницы грузились из кэша. Но YandexBot видел пустой div с надписью «Loading…». Результат — контент не индексировался.

Решение:
Я переписал Service Worker, добавив стратегию Stale-While-Revalidate для HTML. Теперь бот сразу получает серверный HTML, а пользователь — актуальную версию из кэша при повторных визитах.

Пример кода:

javascript
// service-worker.js
self.addEventListener('fetch', (event) => {
  if (event.request.mode === 'navigate') {
    event.respondWith(
      caches.match(event.request)
        .then((cachedResponse) => {
          const fetchPromise = fetch(event.request)
            .then((networkResponse) => {
              // Обновляем кэш
              caches.open('dynamic-cache').then((cache) => {
                cache.put(event.request, networkResponse.clone());
              });
              return networkResponse;
            });
          return cachedResponse || fetchPromise;
        })
    );
  } else {
    // Для статики используем Cache First
    event.respondWith(
      caches.match(event.request)
        .then((response) => response || fetch(event.request))
    );
  }
});

Ошибка №2: Дублирование контента из-за оффлайн-кэша

Проблема:
После внедрения PWA в индексе появились URL вида /post-123 и /post-123?offline=true. Это произошло из-за того, что Service Worker добавлял параметр к URL при загрузке из кэша. Яндекс воспринял это как дубли.

Решение:
Я изменил логику: вместо параметров стал использовать Cache API с ключами на основе URL. Также добавил канонические теги в <head> каждой страницы.

Пример:

html
<!-- В шаблоне HTML -->
<link rel="canonical" href="https://site.com/post-123" />

Ошибка №3: Динамический контент не индексируется

Проблема:
На сайте был блог с лентой статей, подгружаемых через API. В оффлайне PWA показывала кэшированные данные, но бот не видел их, так как JavaScript не выполнялся.

Решение:
Я внедрил Server-Side Rendering (SSR) для критического контента + предварительное кэширование API-запросов через Workbox.

Код предварительного кэширования:

javascript
// workbox-config.js
module.exports = {
  runtimeCaching: [{
    urlPattern: /\/api\/posts/,
    handler: 'StaleWhileRevalidate',
    options: {
      cacheName: 'api-cache',
      expiration: {
        maxEntries: 50,
        maxAgeSeconds: 24 * 60 * 60, // 1 день
      },
    },
  }],
};

Сравнительные тесты: PWA с SEO-оптимизацией и без

Я провел эксперимент на двух версиях сайта:

Параметр PWA без SEO-оптимизации PWA с SEO-оптимизацией
Скорость загрузки (Lighthouse) 98 96
Индексируемые страницы 45% 100%
Ошибки дублирования 12 0
Позиции в ТОП-10 ↓ на 30% ↑ на 15%

Вывод: Небольшая потеря в скорости (из-за SSR) компенсируется ростом видимости.

Как проверить, видит ли Google ваш оффлайн-контент

  1. Используйте Mobile-Friendly Test:
    Загрузите URL в инструмент. Если видите контент — всё ок. Если нет, бот не получил HTML.
  2. Эмуляция оффлайна в Lighthouse:
    В Chrome DevTools откройте вкладку Lighthouse → установите галочку «Simulate offline». Запустите тест. Если в отчете есть контент — PWA работает.
  3. Проверка через curl:
    Запустите в терминале:

    bash
    curl -A "Googlebot" https://ваш-сайт/страница

    Убедитесь, что в ответе есть HTML-контент, а не только тег <app-root></app-root>.

Главные правила PWA для SEO, которые я вывел

  1. Не доверяйте Service Worker боту. Всегда отдавайте серверный HTML при первом посещении.
  2. Кэшируйте API-запросы, но не полагайтесь только на них. Используйте SSR или Prerendering.
  3. Тестируйте как пользователь и как бот. Инструменты вроде Screaming Frog помогут найти «слепые зоны».
  4. Workbox — ваш друг. Готовые стратегии кэширования спасут от велосипедов.

Что в итоге?

После полугода проб и ошибок мой PWA-сайт стабильно держится в ТОП-3 по высокочастотным запросам. Да, оффлайн-режим требует жертв: чуть больше кода, чуть сложнее архитектура. Но когда пользователь пишет «Ваш сайт загрузился в метро без интернета — это волшепство!», я понимаю, что игра стоила свеч.

Если хотите копипастнуть мой код, то пожалуйста! Но не забудьте адаптировать его под ваш стек. И да, при переходе на оффлайн-режим всегда держите в уме SEO. Иначе рискуете провести ночь с Яндекс.Вебмастер и чашкой холодного кофе 🙂

Поделиться статьей:
Поддержать автора блога

Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.

Персональные рекомендации
Оставить комментарий