Дубли в WordPress обычно появляются не из-за одной ошибки, а из-за набора мелких причин: импорт повторно создал записи, редактор сохранил похожие slug, плагин синхронизации продублировал метаполя, а старые тестовые записи так и остались в базе. В итоге в админке видно несколько почти одинаковых сущностей, а на фронтенде — путаница в поиске, каноникалях и внутренней перелинковке.
Ниже — рабочий сценарий: как сначала понять, что именно у вас дублируется, затем безопасно найти повторяющиеся записи по post_name и метаданным, удалить лишнее и проверить, что сайт не потерял нужный контент.
Когда проблема действительно в дублях, а не в кэше или индексации
Сначала важно не перепутать дубли с похожими симптомами. Если страница открывается дважды из-за кэша, редиректа или неверного canonical, удаление записей не поможет. Если же в базе реально есть несколько одинаковых сущностей, это видно в админке и в результатах поиска по сайту.
Признаки, что дубли уже есть в базе
- в списке записей или товаров есть одинаковые заголовки с разными датами публикации;
- slug отличается только суффиксом
-2,-3и так далее; - после импорта CSV/JSON появляются повторяющиеся записи с теми же метаполями;
- поиск в админке показывает несколько почти одинаковых объектов;
- внутренние ссылки ведут на старую и новую версию одной сущности.
Что проверить до удаления
- есть ли у дубля другой автор, статус или дата обновления;
- используется ли запись в меню, блоках, виджетах, шаблонах или в связанных полях;
- не является ли «дубль» переводом, версией для другого языка или отдельной карточкой товара;
- не создаёт ли повторение сам плагин импорта или синхронизации.
Как найти дубли по slug и заголовкам через SQL
Если у вас есть доступ к базе, самый быстрый способ — посмотреть повторяющиеся post_name и одинаковые заголовки. Это не удаляет данные, а только помогает понять масштаб проблемы.
SELECT post_name, COUNT(*) AS cnt
FROM wp_posts
WHERE post_status NOT IN ('auto-draft', 'trash')
AND post_type IN ('post', 'page', 'product')
GROUP BY post_name
HAVING cnt > 1
ORDER BY cnt DESC;Этот запрос покажет одинаковые slug. Для заголовков можно использовать отдельную выборку:
SELECT post_title, post_type, COUNT(*) AS cnt
FROM wp_posts
WHERE post_status NOT IN ('auto-draft', 'trash')
AND post_type IN ('post', 'page', 'product')
GROUP BY post_title, post_type
HAVING cnt > 1
ORDER BY cnt DESC;Но одинаковый заголовок сам по себе ещё не дубль. Для практической проверки лучше смотреть связку: заголовок, slug, тип записи, мета-данные и дату изменения.
Поиск повторяющихся мета-данных
Если дубли появляются после импорта или интеграции, часто повторяются не только записи, но и ключевые мета-поля. Например, внешний ID, артикул, source_id или старый URL. По ним удобно искать повторения:
SELECT meta_key, meta_value, COUNT(*) AS cnt
FROM wp_postmeta
WHERE meta_key IN ('external_id', 'source_id', '_sku')
GROUP BY meta_key, meta_value
HAVING cnt > 1
ORDER BY cnt DESC;Если у товара или записи есть внешний идентификатор, именно он обычно помогает отличить настоящий дубль от похожего контента.
Пошаговое удаление дублей без потери нужных записей
Удалять всё подряд нельзя. Сначала определите «главную» запись: обычно это та, у которой есть входящие ссылки, комментарии, история правок, корректный slug и актуальные метаданные. Остальные дубли переводите в корзину только после проверки.
Шаг 1. Сделайте резервную копию базы
Перед массовым удалением нужен бэкап. Если работаете на продакшене, лучше сначала прогнать процедуру на staging-копии. Это особенно важно, если дубли связаны с импортом или нестандартными полями.
Шаг 2. Отберите кандидатов на удаление
В админке удобно фильтровать по типу записи, дате и автору. Если дублей много, используйте SQL-выборку только для анализа, а удаление делайте через интерфейс или через аккуратный скрипт с проверкой ID.
Шаг 3. Удаляйте только лишние ID
Если вы уже точно знаете список дублей, можно удалить их программно. Ниже пример для разового запуска в functions.php дочерней темы или в небольшом mu-plugin. Он удаляет только конкретные ID, которые вы сами передали в массив.
add_action('admin_init', function () {
if ( ! current_user_can('manage_options') ) {
return;
}
if ( empty($_GET['remove_duplicate_ids']) ) {
return;
}
$duplicate_ids = array(123, 124, 125); // замените на свои ID
foreach ( $duplicate_ids as $post_id ) {
$post_id = absint($post_id);
if ( $post_id ) {
wp_delete_post($post_id, true);
}
}
wp_die('Дубли удалены.');
});Такой подход не ищет дубли автоматически, зато исключает случайное удаление лишнего. Для массовой чистки это безопаснее, чем писать «умное» удаление без ручной проверки.
Шаг 4. Если дубли создаёт импорт, исправьте источник
Иначе проблема вернётся. Проверьте, не импортирует ли плагин каждый раз новые записи вместо обновления существующих. Обычно нужно сопоставлять записи по стабильному ключу: внешнему ID, SKU, старому slug или другому уникальному полю.
| Подход | Когда подходит | Риск |
|---|---|---|
| Удаление вручную в админке | Несколько десятков дублей | Можно пропустить скрытые связи |
| SQL только для поиска | Нужно быстро понять масштаб | Нельзя удалять без проверки |
| Скрипт с явным списком ID | Есть подтверждённые кандидаты | Требует аккуратности |
Как найти дубли по мета-данным через WP_Query
Если нужен более «wordpress-way» подход, можно собрать записи с одинаковым мета-значением через WP_Query и затем сравнить результаты. Это полезно, когда дубли связаны не с заголовком, а с внешним идентификатором.
$query = new WP_Query(array(
'post_type' => array('post', 'page', 'product'),
'post_status' => array('publish', 'draft', 'pending'),
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'external_id',
'compare' => 'EXISTS',
),
),
));
$groups = array();
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
$external_id = get_post_meta(get_the_ID(), 'external_id', true);
if ( $external_id ) {
$groups[$external_id][] = get_the_ID();
}
}
wp_reset_postdata();
}
foreach ( $groups as $external_id => $ids ) {
if ( count($ids) > 1 ) {
// Здесь уже можно вручную решить, какой ID оставить.
}
}Этот способ удобен, если у вас есть стабильное поле для сопоставления. Без него автоматическое удаление легко ошибётся.
Проверка результата после очистки
После удаления дублей не ограничивайтесь тем, что «в админке стало чище». Нужно проверить, что остались правильные URL, не сломались связи и не появились новые ошибки в индексации.
Что проверить сразу
- открываются ли нужные записи и товары по основным URL;
- не ведут ли старые ссылки на 404;
- сохранились ли метаданные, изображения и таксономии у оставшихся записей;
- не изменился ли canonical у важных страниц;
- не появились ли дубли в XML-карте сайта.
Если удалённая запись уже была в индексе, настройте 301-редирект на актуальную версию. Это особенно важно для старых URL с трафиком и внешними ссылками.
Мини-проверка через SQL
После чистки можно ещё раз запустить запрос на повторяющиеся slug. Если он ничего не возвращает, это хороший знак, но не финальная гарантия. Проверьте и мета-данные, если именно они были источником дублей.
SELECT post_name, COUNT(*) AS cnt
FROM wp_posts
WHERE post_status NOT IN ('auto-draft', 'trash')
AND post_type IN ('post', 'page', 'product')
GROUP BY post_name
HAVING cnt > 1;Частые ошибки и как их исправить
Удаляют по одинаковому заголовку
Это самая частая ошибка. Заголовки могут совпадать у разных материалов, особенно если это шаблонные страницы, версии для разных регионов или похожие карточки. Смотрите на slug, мета-данные, тип записи и историю изменений.
Стирают всё через поиск в базе
Опасно удалять записи по одному только SQL-запросу без списка ID. Запрос на поиск и запрос на удаление — это разные задачи. Сначала получите кандидатов, потом удаляйте точечно.
Не исправляют источник дублей
Если дубль создаёт импорт, cron-задача или интеграция с внешним сервисом, он появится снова. Проверьте, по какому полю система определяет «уже существующую запись».
Забывают про редиректы
После удаления старых URL часть трафика может уйти в 404. Если у дубля был индекс и ссылки, настройте перенаправление на оставшуюся запись.
Практические советы по безопасности и производительности
Массовая чистка базы всегда нагружает сайт сильнее обычного редактирования. На больших проектах лучше выполнять её в низкую нагрузку и не запускать тяжёлые выборки на каждом хите фронтенда. Если пишете свой инструмент для поиска дублей, ограничьте его доступ только администраторам и не оставляйте без проверки в публичной части.
Если задача повторяется регулярно, удобнее вынести её в отдельный служебный скрипт или mu-plugin, а не держать в теме. Так вы не потеряете инструмент при смене шаблона и снизите риск случайного запуска.
Для сайтов с частыми дублями из-за контентных процессов полезно дополнительно проверить внутреннюю SEO-гигиену: канонические URL, карту сайта, правила индексации технических страниц и логику генерации slug. В некоторых случаях проще сначала закрыть источник дублей, а уже потом чистить базу.
Если вам нужен более широкий набор инструментов для чистки сайта и удаления технических дублей, можно посмотреть Clearfy Pro, но он не заменяет ручную проверку данных и не снимает ответственность за удаление записей.
Главный принцип здесь простой: сначала найти источник повторения, потом удалить только подтверждённые дубли, и только после этого проверять индексацию и ссылки. Тогда чистка не превратится в потерю нужного контента.