
🔒 4 способа программно снять пост с публикации в WordPress
Сайт упал после обновления плагина, нужно срочно скрыть проблемный пост, пока не поздно. А через неделю, вернуть обратно, когда баг починят. Или клиент просит убрать устаревшую статью из выдачи, но не удалять навсегда.
Ручное переключение статуса через админку работает для одного-двух постов. Но когда таких десятки или логика должна срабатывать автоматически, нужен программный подход. WordPress даёт четыре способа снять пост с публикации через PHP: от безопасного черновика до полного удаления.
Ниже, каждый метод с готовым кодом, объяснением и подсказкой, когда какой применять.
💡 Быстрый обзор:
- Перевели пост в черновик через
wp_update_postсо статусомdraft, самый безопасный и обратимый способ - Сделали пост приватным (
private), виден только администраторам и редакторам - Отправили пост в будущее через
post_date, пост исчезает из выдачи до наступления заданной даты - Удалили пост навсегда через
wp_delete_post, крайняя мера с предупреждениями и бэкапом
Шаг 1. Черновик: снимаем пост с публикации без потери данных
Самый частый сценарий: нужно временно скрыть пост, но сохранить весь контент, URL и возможность вернуть обратно одним кликом. Перевод в черновик, идеальный вариант.
Меняется только поле post_status в таблице wp_posts. Сам пост, его мета-поля, вложения и URL остаются нетронутыми. Когда решите вернуть, меняете статус обратно на publish.
Код для смены статуса на draft. Добавьте в functions.php дочерней темы или через плагин Code Snippets:
1 /** 2 * Переводит пост в черновик по ID. 3 * 4 * @param int $post_id ID поста, который нужно снять с публикации. 5 */ 6 function sd_unpublish_to_draft( $post_id ) { 7 wp_update_post( array( 8 'ID' => $post_id, 9 'post_status' => 'draft', 10 ) ); 11 } 12 13 // Пример вызова: снимаем пост с ID = 42 14 sd_unpublish_to_draft( 42 );
wp_update_post() обновляет запись в БД. Мы передаём только ID и новое значение post_status, всё остальное WordPress подхватывает сам. Никакие другие поля не меняются.
Когда применять: временное скрытие поста на доработку, автоматическая деактивация постов с истёкшим сроком актуальности (например, акции), программная модерация пользовательского контента.
Шаг 2. Приватный пост: скрываем от посетителей, оставляем редакторам
Приватный статус — это середина между публичным и скрытым. Пост не виден обычным посетителям, но доступен администраторам и редакторам в админке. Удобно для внутренних материалов: инструкций для команды, черновиков клиентского контента, закрытых страниц.
Отличие от черновика: приватный пост технически «опубликован» и может иметь свой URL, но WordPress проверяет права пользователя перед показом. Посетитель без роли read_private_posts увидит 404.
Код аналогичен предыдущему, меняется только статус:
1 /** 2 * Делает пост приватным — видимым только админам и редакторам. 3 * 4 * @param int $post_id ID поста. 5 */ 6 function sd_unpublish_to_private( $post_id ) { 7 wp_update_post( array( 8 'ID' => $post_id, 9 'post_status' => 'private', 10 ) ); 11 } 12 13 // Пример вызова 14 sd_unpublish_to_private( 42 );
Обратите внимание: если на сайте есть пользовательские роли с кастомными правами, проверьте capabilities перед массовым использованием. По умолчанию приватные посты видят роли editor и administrator.
Когда применять: премиум-контент по подписке (в связке с membership-плагинами), внутренняя документация команды, скрытие постов для пересогласования с клиентом перед повторной публикацией.
Шаг 3. Будущая дата: отложенное снятие с публикации
Интересный приём: вместо смены статуса можно «отправить пост в будущее», задать дату публикации на 2050 год. Пост мгновенно исчезает из выдачи, потому что WordPress показывает только посты с датой ≤ текущему моменту.
Этот метод не меняет post_status: пост остаётся publish. Он просто «ещё не наступил» с точки зрения WordPress. Плюс, при необходимости можно вернуть реальную дату, и пост снова появится.
Код использует поля post_date и post_date_gmt:
1 /** 2 * Скрывает пост, устанавливая дату публикации в далёкое будущее. 3 * 4 * @param int $post_id ID поста. 5 */ 6 function sd_unpublish_to_future( $post_id ) { 7 $future_date = '2050-12-31 23:59:59'; 8 9 wp_update_post( array( 10 'ID' => $post_id, 11 'post_date' => $future_date, 12 'post_date_gmt' => get_gmt_from_date( $future_date ), 13 ) ); 14 } 15 16 // Пример вызова 17 sd_unpublish_to_future( 42 );
get_gmt_from_date() конвертирует локальное время в GMT, WordPress хранит обе версии даты. Не пренебрегайте GMT-полем: без него поведение при смене часового пояса сайта станет непредсказуемым.
Когда применять: «отложенная» публикация контента по расписанию, временное скрытие новостей без изменения статуса, сценарии где post_status должен остаться publish для обратной совместимости с другими плагинами.
Шаг 4. Удаление: когда пост не нужен совсем
wp_delete_post(), необратимая операция. Пост удаляется из базы, а вместе с ним, все мета-поля, связи с таксономиями и (опционально) вложения.
Это не «снятие с публикации» в строгом смысле. Но в контексте программного управления контентом удаление, четвёртый, самый жёсткий инструмент. И он требует предохранителей.
Перед запуском сделайте полный бэкап базы данных. Скрипт ниже сначала выводит список того, что будет удалено, и только потом, боевой вариант.
1 /** 2 * Удаляет пост. Сначала — dry-run с выводом информации, затем — боевое удаление. 3 * 4 * ВНИМАНИЕ: необратимая операция. Сделайте бэкап перед запуском. 5 * 6 * @param int $post_id ID поста. 7 * @param bool $force_delete true — удалить навсегда (минуя корзину), false — в корзину. 8 */ 9 function sd_delete_post_safe( $post_id, $force_delete = false ) { 10 $post = get_post( $post_id ); 11 12 if ( ! $post ) { 13 error_log( "Post with ID {$post_id} not found." ); 14 return; 15 } 16 17 // Dry-run: выводим информацию без удаления 18 error_log( sprintf( 19 'READY TO DELETE: ID=%d, title="%s", status=%s, attachments=%d', 20 $post->ID, 21 $post->post_title, 22 $post->post_status, 23 count( get_attached_media( '', $post_id ) ) 24 ) ); 25 26 // Раскомментируйте следующую строку для реального удаления: 27 // wp_delete_post( $post_id, $force_delete ); 28 } 29 30 // Dry-run: только выводит информацию 31 sd_delete_post_safe( 12341, false );
Флаг $force_delete:
false, пост попадает в корзину (Trash), его можно восстановить в течение 30 дней.true, удаление навсегда, восстановить нельзя даже через базу (без бэкапа).
Функция логирует через error_log(), сообщения появятся в wp-content/debug.log при включённом WP_DEBUG. В продакшене замените на свой механизм оповещений.
Когда применять: автоматическая очистка спам-постов, удаление просроченного контента (вакансии, мероприятия), программная ротация контента с полным удалением старых записей.
Сравнение четырёх методов
Метод | Статус поста | Обратимость | Видимость для читателей | Видимость в админке | Когда применять |
|---|---|---|---|---|---|
Черновик |
| Полная | Скрыт | Всем ролям с доступом к постам | Временное скрытие, доработка |
Приватный |
| Полная | Скрыт | Админам и редакторам | Внутренний контент, премиум |
Будущая дата |
| Полная | Скрыт до даты | Всем | Отложенная публикация, расписание |
Удаление | - | Только из корзины (30 дней) | - | Только админам | Полное удаление, очистка |
⁉️🤔 Частые вопросы
Чем отличается снятие с публикации от удаления?
Снятие с публикации (draft/private/future) сохраняет пост в базе: контент, URL, вложения и SEO-история остаются. Удаление (
wp_delete_post) стирает запись полностью. Для временного скрытия всегда используйте черновик — это безопасно и обратимо за секунду.
Какой метод не требует смены post_status?
Отправка в будущее через
post_date. Пост остаётсяpublish, но WordPress считает его «ещё не наступившим» и не показывает посетителям. Это может быть важно, если другие плагины или сниппеты завязаны на статусpublish.
Можно ли снять с публикации сразу несколько постов?
Да, оберните вызов функции в цикл по массиву ID. Добавьте
wp_die()или ограничение на количество постов за раз, чтобы не положить сайт при массовой операции:array_slice($post_ids, 0, 50)для батча по 50.
Нужно ли очищать кэш после программной смены статуса?
Обязательно. WordPress сбрасывает внутренний кэш поста при вызове
wp_update_post(), но внешний кэш (плагины вроде WP Rocket, серверный кэш, CDN) нужно сбрасывать отдельно. Добавьте вызовwp_cache_flush()или хукclean_post_cacheпосле смены статуса.
Безопасно ли запускать wp_delete_post в продакшене?
Только с предохранителями. Перед вызовом: (1) проверьте
current_user_can('delete_posts'), (2) запросите подтверждение через отдельный nonce-токен, (3) логируйте ID и заголовок удаляемого поста. И главное, бэкап. Даже в корзине пост живёт 30 дней, после чего WordPress удаляет его автоматически.
Что использовать в вашем случае: итоговый расклад
Четыре метода закрывают почти любой сценарий программного управления публикациями. Выбор сводится к одному вопросу: нужно ли сохранить пост?
- Если пост временно скрыть на доработку, берите черновик (
draft). Пара строк, ноль риска. - Если контент для ограниченного круга лиц, приватный статус (
private). Редакторы видят, посетители, нет. - Если нужно скрыть пост без смены статуса, будущая дата (
post_dateв 2050 год). Хитрый, но рабочий приём. - Если пост точно не пригодится, удаление (
wp_delete_post). Но сначала dry-run и полный бэкап.
Начните с обёртки в functions.php для одного метода, например, черновика. Когда поймёте логику wp_update_post(), остальные три способа соберутся за пять минут.
А каким методом вы пользуетесь для программного управления постами? Напишите в комментариях, интересно сравнить подходы.



