IndexNow и WooCommerce: как отправлять URL при изменении цен и наличия товаров

В WooCommerce чаще всего ломается не сама индексация, а логика обновления. Цена поменялась, остаток ушёл в ноль, товар снова появился в наличии — а поисковик ещё какое-то время видит старую версию страницы. Если на сайте уже настроен IndexNow, это можно использовать точечно: отправлять URL только в тех сценариях, где изменился коммерческий сигнал, а не при каждом сохранении карточки.

Ниже — рабочая схема для магазина, где важны цена, наличие и варианты товара. Без лишней автоматизации, но с нормальной диагностикой, проверкой результата и типичными ошибками, которые обычно всплывают на реальных проектах.

Когда проблема действительно в обновлении URL, а не в кэше

Перед тем как трогать код, стоит понять, где именно задержка. В WooCommerce часто путают три разных слоя: кэш страницы, кэш объекта и фактическую отправку сигнала в поисковые системы. IndexNow не исправляет кэш сам по себе — он только сообщает о смене URL. Если страница на фронте уже обновилась, а в поиске ещё старая цена, значит проблема не в шаблоне, а в скорости переобхода.

Признаки, что нужен именно IndexNow

  • цена или наличие меняются часто, особенно в акциях и распродажах;
  • карточки товаров индексируются, но обновления подтягиваются с задержкой;
  • есть вариативные товары, где меняются остатки у конкретных вариаций;
  • на сайте много товаров, и вручную отправлять URL через панель неудобно;
  • нужно отправлять не весь сайт, а только изменившиеся карточки.

Что проверить до внедрения

  • товар реально меняет публичный URL или только метаданные;
  • на сайте нет агрессивного кэша, который скрывает обновление на фронте;
  • ключ IndexNow уже размещён и доступен по ожидаемому адресу;
  • сервер не блокирует исходящие запросы к API поисковиков;
  • обновление цены и остатка происходит через стандартные хуки WooCommerce, а не через кастомный импорт без событий.

Какой сценарий отправки выбрать: плагин, код или гибрид

Если магазин небольшой, проще использовать готовое решение. Если изменения идут массово или есть нестандартная логика остатков, лучше оставить отправку в коде и ограничить её нужными событиями. Для больших каталогов гибридный вариант обычно практичнее: код фиксирует изменения, а очередь или cron отправляет URL пакетами.

ПодходКогда подходитКомпромисс
ПлагинНебольшой магазин, стандартные товарыМеньше контроля над условиями отправки
Код в теме или мини-плагинеНужна точная логика по цене и наличиюНужно поддерживать код при обновлениях
Гибрид: код + очередьМного товаров, массовые изменения, импортСложнее настройка, но меньше нагрузка

Пошаговое решение: отправлять URL только при изменении цены и остатка

Ниже пример, который можно положить в мини-плагин или в functions.php дочерней темы. Он отслеживает сохранение товара, сравнивает старые и новые значения цены и наличия, и только потом вызывает отправку URL в IndexNow через HTTP-запрос.

Важно: код ниже не привязан к вымышленному API. Он использует обычный wp_remote_post() и формат JSON, который ожидают сервисы IndexNow. Перед использованием проверьте актуальный endpoint и требования конкретной поисковой системы, если вы отправляете не только в общий IndexNow-совместимый поток.

<?php
add_action( 'woocommerce_update_product', 'ixn_send_product_url_on_stock_or_price_change', 10, 1 );

function ixn_send_product_url_on_stock_or_price_change( $product_id ) {
    $product = wc_get_product( $product_id );
    if ( ! $product ) {
        return;
    }

    $old_price = get_post_meta( $product_id, '_ixn_old_price', true );
    $old_stock = get_post_meta( $product_id, '_ixn_old_stock_status', true );

    $new_price = $product->get_price();
    $new_stock = $product->get_stock_status();

    $changed = false;
    if ( (string) $old_price !== (string) $new_price ) {
        $changed = true;
    }
    if ( (string) $old_stock !== (string) $new_stock ) {
        $changed = true;
    }

    update_post_meta( $product_id, '_ixn_old_price', $new_price );
    update_post_meta( $product_id, '_ixn_old_stock_status', $new_stock );

    if ( ! $changed ) {
        return;
    }

    $url = get_permalink( $product_id );
    $endpoint = 'https://api.indexnow.org/indexnow';

    $body = array(
        'host'        => wp_parse_url( home_url(), PHP_URL_HOST ),
        'key'         => 'YOUR_INDEXNOW_KEY',
        'keyLocation' => home_url( '/YOUR_INDEXNOW_KEY.txt' ),
        'urlList'     => array( $url ),
    );

    $response = wp_remote_post( $endpoint, array(
        'timeout' => 15,
        'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
        'body'    => wp_json_encode( $body ),
    ) );

    if ( is_wp_error( $response ) ) {
        error_log( 'IndexNow error for product ' . $product_id . ': ' . $response->get_error_message() );
        return;
    }

    $code = wp_remote_retrieve_response_code( $response );
    if ( $code < 200 || $code >= 300 ) {
        error_log( 'IndexNow HTTP ' . $code . ' for product ' . $product_id );
    }
}

Этот вариант годится как отправная точка, но у него есть слабое место: он хранит старое значение в метаполях и сравнивает его при следующем сохранении. На проектах с импортом и массовыми обновлениями лучше не полагаться только на это. Надёжнее сравнивать исходные данные до и после сохранения или складывать URL в очередь.

Если нужно учитывать вариации товара

У вариативных товаров остаток и цена часто живут не на родительской записи, а на вариациях. В таком случае отправлять нужно не только URL родителя, но и проверять, менялась ли конкретная вариация. Если на витрине используется одна карточка с переключением вариаций, обычно достаточно отправить URL родителя, потому что именно он индексируется как основная страница товара.

<?php
add_action( 'woocommerce_save_product_variation', 'ixn_send_parent_url_on_variation_change', 10, 2 );

function ixn_send_parent_url_on_variation_change( $variation_id, $i ) {
    $variation = wc_get_product( $variation_id );
    if ( ! $variation ) {
        return;
    }

    $parent_id = $variation->get_parent_id();
    if ( ! $parent_id ) {
        return;
    }

    $parent_url = get_permalink( $parent_id );
    // Здесь можно вызвать общую функцию отправки URL в IndexNow.
    // Отправляйте URL родителя, если именно он является канонической страницей товара.
}

Как не перегрузить сайт при массовом обновлении цен

Если цены меняются импортом или через прайс-лист поставщика, отправлять запрос на каждый товар сразу — плохая идея. На живом магазине это создаёт лишнюю нагрузку и может упереться в лимиты внешнего API. Лучше складывать URL в очередь и отправлять пакетами через wp_cron или системный cron. Для IndexNow это особенно полезно, когда за один импорт обновляются сотни карточек.

Практически это выглядит так: при сохранении товара вы не вызываете внешний запрос сразу, а записываете URL в массив или transient, затем отдельная задача раз в минуту отправляет пачку из нескольких URL. Такой подход проще контролировать и легче отлаживать.

Минимальная схема очереди

<?php
add_action( 'save_post_product', 'ixn_queue_product_url', 10, 3 );

function ixn_queue_product_url( $post_id, $post, $update ) {
    if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
        return;
    }

    $queue = get_option( 'ixn_url_queue', array() );
    $queue[] = get_permalink( $post_id );
    $queue   = array_values( array_unique( array_filter( $queue ) ) );

    update_option( 'ixn_url_queue', $queue, false );
}

add_action( 'ixn_send_queue_event', 'ixn_send_queue_batch' );

function ixn_send_queue_batch() {
    $queue = get_option( 'ixn_url_queue', array() );
    if ( empty( $queue ) ) {
        return;
    }

    $batch = array_slice( $queue, 0, 10 );
    $remaining = array_slice( $queue, 10 );

    $body = array(
        'host'        => wp_parse_url( home_url(), PHP_URL_HOST ),
        'key'         => 'YOUR_INDEXNOW_KEY',
        'keyLocation' => home_url( '/YOUR_INDEXNOW_KEY.txt' ),
        'urlList'     => $batch,
    );

    $response = wp_remote_post( 'https://api.indexnow.org/indexnow', array(
        'timeout' => 15,
        'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
        'body'    => wp_json_encode( $body ),
    ) );

    if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) >= 200 && wp_remote_retrieve_response_code( $response ) < 300 ) {
        update_option( 'ixn_url_queue', $remaining, false );
    }
}

Если у вас уже есть очередь в другом плагине или в собственной системе импорта, не дублируйте логику. Достаточно одного места, где URL собираются, и одного места, где они уходят наружу.

Проверка результата после внедрения

Проверять нужно не только факт HTTP-ответа, но и то, что URL действительно попал в нужный сценарий. На практике я смотрю на три уровня: лог отправки, ответ сервера и поведение страницы после изменения.

  • после изменения цены в логах появляется запись об отправке;
  • HTTP-ответ от endpoint не содержит ошибки сети или 4xx/5xx;
  • URL карточки остаётся тем же, если не менялся slug;
  • страница товара обновляется на фронте без задержки кэша;
  • при повторном сохранении без изменений запрос не уходит.

Если хотите быстро проверить руками, временно добавьте логирование в отправляющую функцию и посмотрите debug.log. Для этого в wp-config.php должны быть включены WP_DEBUG и WP_DEBUG_LOG. После теста логирование лучше оставить только для ошибок, а не для каждого товара.

Что считать успешным результатом

Успех — это не только 200 OK от внешнего API. Важнее, чтобы отправка происходила именно в момент изменения цены или остатка, а не при каждом обновлении записи. Если товар обновляется без изменения коммерческих данных, запрос не должен уходить. Если меняется вариация, а карточка родителя остаётся той же, отправка должна быть привязана к каноническому URL товара.

Частые ошибки и как их исправить

Отправка идёт при каждом сохранении товара

Обычно причина в том, что код сравнивает не старое и новое значение, а просто реагирует на хук сохранения. Исправление простое: сравнивайте цену и статус наличия до и после обновления, либо используйте очередь с дедупликацией URL.

URL уходит, но в поиске ничего не меняется

IndexNow не гарантирует мгновенную переиндексацию. Если страница закрыта от индексации, имеет неправильный canonical или отдаёт ошибку, сигнал не поможет. Сначала проверьте, что товар доступен для обхода, а потом уже смотрите на скорость обновления.

Сыпятся ошибки 403 или 404 на keyLocation

Частая причина — файл с ключом лежит не в корне сайта или закрыт правилами сервера. Проверьте, что файл доступен по адресу, который указан в запросе, и что он отдается без редиректов и авторизации.

Массовый импорт подвешивает сайт

Значит, запрос отправляется синхронно в момент сохранения каждого товара. Переводите отправку в очередь, ограничивайте размер пачки и не делайте внешний HTTP-запрос внутри тяжёлого цикла импорта.

У вариаций меняется остаток, но отправляется не тот URL

Вариации часто имеют собственные ID, но в индексе важен URL родительской карточки. Если отправлять URL вариации, это может быть бесполезно. Проверьте, какая страница реально индексируется и какая является канонической.

Безопасность и производительность: что не стоит игнорировать

Ключ IndexNow не является секретом уровня пароля администратора, но его всё равно не стоит разбрасывать по публичным репозиториям и логам. Если вы храните ключ в коде, убедитесь, что файл не попадает в git-репозиторий без необходимости. Для production-проектов лучше держать его в конфиге или в отдельном мини-плагине с ограниченным доступом.

По производительности важнее другое: не отправляйте запросы без фильтрации. Если товар меняет только служебное поле, а цена и остаток не тронуты, внешний запрос не нужен. Это особенно заметно на магазинах с частыми правками контента, где лишние вызовы просто засоряют логи и увеличивают время сохранения записи.

Если на сайте уже есть плагин для чистки дублей и технической оптимизации, например Clearfy Pro, проверьте, не конфликтует ли он с вашей логикой canonical и robots.txt. Для IndexNow это не обязательное условие, но на реальных магазинах такие настройки часто влияют на то, как быстро и корректно поисковик воспринимает обновлённую карточку товара.

Короткий чек-лист перед запуском в продакшн

  • ключ IndexNow доступен по keyLocation без редиректов;
  • отправка привязана к изменению цены или статуса наличия, а не к любому сохранению;
  • для вариативных товаров выбрана правильная каноническая страница;
  • массовые обновления идут через очередь, а не синхронно;
  • включено логирование ошибок, но не избыточный debug для каждого запроса;
  • страницы товаров не закрыты от индексации и не ломаются кэшем.

Если после внедрения карточки товаров начинают обновляться в поиске заметно быстрее, значит схема собрана правильно. Если нет — сначала ищите проблему в доступности страницы, canonical и кэше, а уже потом в самом IndexNow. В магазинах это почти всегда быстрее приводит к результату, чем попытка «усилить» отправку дополнительными запросами.

Добавь в закладки и поделись с друзьями:

⭐⭐⭐⭐⭐
IndexNow и оптимизация индексации изображений в WordPress
02.01.2026
WordPress автоматическое удаление устаревших страниц из индекса с помощью IndexNow
07.11.2025
Решение проблем индексации товаров и страниц WooCommerce
25.12.2025
Решение проблем отправки URL при масштабных изменениях
21.04.2026
Оптимизация файла robots.txt для IndexNow и WordPress: практические советы и примеры
21.11.2025
×

AI-плагин

WPGPT
Сам создает статьи для вашего сайта WordPress

SEO и мета-теги

Парсинг конкурентов

Изображения

Комментарии

Подробнее