Кратко: пагинация в WordPress — это разбивка списка записей на страницы со ссылками «предыдущая», «следующая» и номерами. Выводится одной из трёх функций: paginate_links() даёт полный контроль над разметкой, the_posts_pagination() печатает готовый блок с номерами, next_posts_link() и previous_posts_link() — только ссылки «туда-обратно». Номера страниц работают лишь при постоянных ссылках, отличных от простых, и только там, где есть основной цикл с известным max_num_pages. Для своей выборки на WP_Query нужно передавать параметр paged и вызывать wp_reset_postdata(), а для кастомных типов записей — исправлять запрос через pre_get_posts. Три главные грабли: 404 на второй странице из-за несброшенных правил перезаписи, лишний подсчёт записей без no_found_rows и расхождение количества записей в запросе и в самой функции пагинации.
Зачем нужна пагинация и как её видит WordPress
Пагинация отвечает на простой вопрос: что делать, если записей больше, чем разумно показывать на одном экране. Вариантов три — выводить всё подряд, показывать по 10–20 записей со страницами или подгружать следующие порции по кнопке. Первый вариант убивает скорость и внимательность читателя, поэтому на живых сайтах остаются второй и третий, и оба в WordPress опираются на один механизм.
Механизм такой. Постоянная ссылка на вторую страницу архива формируется по шаблону /page/2/, и значение номера страницы WordPress передаёт в объект запроса как переменную paged. Дальше основной цикл получает уже готовый набор записей нужной страницы, а вам остаётся только напечатать ссылки навигации. Никакой отдельной «страницы архива» в базе не существует: это тот же шаблон, тот же цикл, но со сдвигом выборки.
Отсюда первое практическое требование: пагинация с номерами страниц работает только при постоянных ссылках, отличных от простых вида ?p=123. Если в настройках стоит простой формат, номера страниц не построятся — вместо них останутся только ссылки «следующая» и «предыдущая». Как переключить адреса и не потерять существующие ссылки, разобрано в статье про ЧПУ в WordPress и настройку ссылок.
Второй важный момент: пагинация бывает в трёх разных местах, и в каждом она настраивается по-своему. Это архив записей или рубрика, где есть основной цикл. Это отдельная запись, разбитая тегом <!--nextpage--> на несколько страниц. И это ваша собственная выборка на WP_Query — от блока «Похожие статьи» до каталога товаров. Путать эти случаи нельзя: функции вывода и параметры у них различаются.
Три функции вывода: что выбрать
В ядре WordPress есть несколько готовых решений. Они не конкурируют между собой, а закрывают разные задачи, поэтому выбор сводится к вопросу «сколько контроля нужно» и «нужны ли номера страниц».
Ветеран темы — paginate_links(). Эта функция принимает массив параметров и возвращает уже готовую разметку: либо строку ссылок, либо список, либо массив ссылок по одной. Именно она стоит внутри остальных функций вывода, поэтому если нужно изменить вид, классы или порядок элементов, работать надо с ней.
the_posts_pagination() — обёртка для главного цикла: она сама подставляет общее число страниц, оборачивает результат в <nav>, поддерживает заголовки для скринридеров и словари переводов. Её стоит брать, когда тема стандартная и требуется просто аккуратный блок с номерами.
next_posts_link() и previous_posts_link() печатают только две ссылки. Это вариант для блогов с последовательным чтением, где читатель листает ленту, а не прыгает на страницу 7.
| Функция | Что выводит | Где вызывать | Когда выбирать |
|---|---|---|---|
paginate_links() | Номера страниц, ссылки «назад» и «вперёд», любой формат разметки | Внутри шаблона, после цикла; для кастомных запросов требует переданного total | Нужен контроль над разметкой, классами, размером «окна» и текстами |
the_posts_pagination() | Готовый nav с номерами и заголовком | Только для главного запроса после the_post()-цикла | Стандартная тема, нужен быстрый результат без правок разметки |
next_posts_link() / previous_posts_link() | Две ссылки: следующие и предыдущие записи | Главный запрос, блоги и ленты | Последовательное чтение, где номера страниц не нужны |
wp_link_pages() | Разбивку одной записи на части по <!--nextpage--> | Внутри single.php, до endwhile | Длинная статья или пошаговая инструкция внутри одной записи |
Если тема нестандартная и вывод собирается с нуля, шаблон страницы придётся править в дочерней теме — о том, как её завести и не потерять правки при обновлении, рассказано в статье шаблоны страниц в WordPress.
paginate_links: разбор параметров на примере
Самый частый случай в теме — вывод пагинации в архиве с уточнёнными настройками: короткое «окно» вокруг текущей страницы, свои тексты ссылок и сохранение GET-параметров фильтра. Ниже рабочий пример для шаблона архива.
<?php
// Пагинация в archive.php / category.php — полный контроль над видом
$total = $wp_query->max_num_pages;
if ( $total > 1 ) {
$links = paginate_links( array(
'total' => $total,
'current' => max( 1, get_query_var( 'paged' ) ),
'type' => 'array',
'mid_size' => 1,
'end_size' => 1,
'prev_text' => 'Назад',
'next_text' => 'Вперёд',
// сохраняем параметры фильтра при переходе между страницами
'add_args' => array_filter( array(
'sort' => isset( $_GET['sort'] ) ? sanitize_key( $_GET['sort'] ) : '',
'price' => isset( $_GET['price'] ) ? sanitize_text_field( $_GET['price'] ) : '',
) ),
) );
if ( $links ) {
echo '<nav class="pager" aria-label="Навигация по страницам">';
foreach ( $links as $link ) {
echo $link; // строки уже экранированы ядром
}
echo '</nav>';
}
}
Разберём ключевое. Параметр total берётся из max_num_pages основного запроса — если подставить сюда произвольное число, ссылки начнут вести в никуда. type => 'array' отдаёт массив строк вместо списка, поэтому разметку и классы вы задаёте сами: это единственный способ добавить свои атрибуты, не переписывая CSS темы. mid_size и end_size управляют «окном» вокруг активной страницы: при 40 страницах архива без них пагинация растянется на две строки и начнёт мешать.
Отдельно про add_args. Если на сайте есть фильтр по метаполям или сортировке, без этого параметра переход на вторую страницу сбросит фильтр, и посетитель получит совсем другой список. Значения обязательно пропускайте через sanitize_key() и sanitize_text_field(), прежде чем отдавать в разметку. Если же вид пагинации нужно изменить глобально — например, добавить rel или обернуть каждую ссылку в свой контейнер, — удобнее подключиться к фильтрам paginate_links и paginate_links_output. Как устроены фильтры и чем они отличаются от действий, подробно разобрано в материале про хуки WordPress: actions и filters.
the_posts_pagination и страницы внутри записи
Когда разметка устраивает, достаточно одной строки. Функция сама получит общее число страниц основного запроса и напечатает блок с номерами, стрелками и скрытым заголовком для скринридеров.
<?php
// Стандартная пагинация основного цикла: 2 номера вокруг активной страницы
the_posts_pagination( array(
'mid_size' => 2,
'prev_text' => '← Предыдущая',
'next_text' => 'Следующая →',
'screen_reader_text' => 'Навигация по записям',
) );
Важное ограничение: функция работает только с главным запросом страницы и должна вызываться после цикла while ( have_posts() ). Для выборки, которую вы создали сами через new WP_Query, она возьмёт max_num_pages глобального объекта, а не вашего, — и ссылки будут вести не туда. Это самая частая причина «пагинация есть, но вторая страница пустая».
Другая задача — разбить длинную статью на части внутри одной записи. Здесь работает тег <!--nextpage--> в редакторе и функция wp_link_pages() в шаблоне. Полезно передать ей next_or_number => 'number', чтобы читатель видел номера частей, и before с after, чтобы обернуть вывод в свой контейнер. Тот же приём используют для пошаговых инструкций и больших обзоров: страница получает чистые адреса вида /page/2/ внутри записи, а не одну бесконечную простыню.
И ещё один слой, о котором вспоминают позже всего: если архив строится не по записям, а по своему типу контента — портфолио, отзывы, каталог услуг, — вывод и запрос придётся собирать вручную. Основа такого архива описана в статье про кастомные типы записей и таксономии.
Пагинация в своём WP_Query
Здесь ломается большинство самодельных выборок. Проблема не в функции вывода, а в том, что запрос и пагинация должны «договориться» об одних и тех же числах: сколько записей на странице и какая страница запрошена.
<?php
// 1. Своя выборка с корректной пагинацией
$paged = max( 1, (int) get_query_var( 'paged' ), (int) get_query_var( 'page' ) );
$news = new WP_Query( array(
'post_type' => 'post',
'posts_per_page' => 12,
'paged' => $paged,
'category_name' => 'news',
) );
if ( $news->have_posts() ) {
while ( $news->have_posts() ) {
$news->the_post();
the_title( '<h3><a href="' . esc_url( get_permalink() ) . '">', '</a></h3>' );
}
// 2. Печатаем пагинацию по данным ИМЕННО этого запроса
echo paginate_links( array(
'total' => $news->max_num_pages,
'current' => $paged,
'format' => '?paged=%#%',
) );
}
wp_reset_postdata(); // обязательно: возвращаем глобальный $post
Три детали в этом коде решают всё. Первая — paged вычисляется из get_query_var('paged'), а на статической главной странице ещё и из get_query_var('page'): там WordPress использует другую переменную. Если взять только одну, на главной пагинация будет всегда показывать первую страницу. Вторая — total берётся из $news->max_num_pages, а не из глобального объекта. Третья — wp_reset_postdata() после цикла: без него ломается всё, что идёт дальше на странице, включая хлебные крошки и связанные записи.
Обратная задача — переопределить количество записей в стандартных архивах. Правки в шаблоне здесь не работают: нужно вмешаться в запрос на раннем этапе.
<?php
// 3. Меняем число записей на странице архива своего типа контента
add_action( 'pre_get_posts', 'my_archive_per_page' );
function my_archive_per_page( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_post_type_archive( 'portfolio' ) ) {
$query->set( 'posts_per_page', 24 );
}
}
Проверки is_admin() и is_main_query() обязательны: без них правило доберётся до админки и до второстепенных выборок, и вы получите то пропавшие записи в списке правки, то сломанную сортировку виджетов. Тот же приём используют для таксономий и поиска, меняя posts_per_page точечно по условию.
ЧПУ, вторая страница и ошибка 404
Самая известная болезнь пагинации — 404 на /page/2/, при том что первая страница архива открывается нормально. Причина почти всегда одна: WordPress не знает, куда вести такой адрес, потому что правила перезаписи не соответствуют текущим настройкам.
Что проверять по порядку. Первое — формат постоянных ссылок: при простом формате номеров страниц не существует в принципе. Второе — сохранение настроек постоянных ссылок: WordPress пересобирает правила при открытии страницы «Постоянные ссылки», и если правила пересобирали вручную, а потом добавили новый тип контента, старые правила могут не знать о нём. Третье — код, который регистрирует свои правила через add_rewrite_rule(): при первом подключении их нужно один раз сбросить.
<?php
// 4. Однократный сброс правил перезаписи при активации темы
add_action( 'after_switch_theme', 'my_flush_rewrite_once' );
function my_flush_rewrite_once() {
// сначала регистрируем свои правила, потом сбрасываем ОДИН раз
add_rewrite_rule( '^news/page/([0-9]+)/?$', 'index.php?paged=$matches[1]', 'top' );
flush_rewrite_rules();
}
Вызывать flush_rewrite_rules() на каждом запросе нельзя: это тяжёлая операция, которая перестраивает таблицу правил и заметно замедляет сайт. Правильное место — активация темы или плагина, как в примере выше. И ещё одна тихая причина 404 — конфликт с плагинами, которые чистят адреса или режут запросы; если после отключения одного из них вторая страница открылась, ищите причину там. Общий разбор типовых сбоев с пошаговыми решениями собран в статье ошибки WordPress: 10 частых проблем и их решение.
Отдельно следите за тем, чтобы страницы пагинации не попадали в карту сайта как самостоятельные документы, но при этом оставались доступными для обхода. Как это устроено, описано в материале про XML-карту сайта в WordPress.
Архивы таксономий и каталог WooCommerce
В таксономиях пагинация ведёт себя так же, как в рубриках, но есть нюанс: шаблон вывода у них свой, и пагинацию туда часто просто забывают добавить. Второй нюанс — термины с большим количеством записей. Разбираться со структурой архивов терминов удобнее, когда понимаешь иерархию шаблонов: она разобрана в статье про шаблон таксономии и архив терминов.
В WooCommerce каталог на странице магазина использует собственную пагинацию, и правки стандартных функций там не сработают. Количество товаров на странице меняют фильтром, а саму навигацию — вызовом функции шаблона списка. Если каталог собирается вручную, а не через шорткод, настройки выборки описаны в статье про настройку каталога товаров WooCommerce.
Третий вариант, который часто просят «вместо пагинации», — кнопка «Показать ещё» с подгрузкой через AJAX или бесконечный скролл. У него есть цена, и её стоит понимать заранее.
| Критерий | Классическая пагинация | Кнопка «Показать ещё» и бесконечный скролл |
|---|---|---|
| Адреса страниц | Каждая страница имеет свой URL и может попасть в индекс | Адрес один, отдельные порции контента поисковику почти не видны |
| Поведение читателя | Читатель видит объём материала и осознанно уходит глубже | Прокрутка не заканчивается, вернуться к нужному месту сложнее |
| Возврат на позицию | Работает по ссылке и из истории браузера | Требует дополнительной логики, иначе позиция теряется |
| Скорость и запросы | Один запрос на страницу, нагрузка предсказуема | Много последовательных запросов, нагрузка растёт с глубиной прокрутки |
| Когда выбирать | Каталоги, архивы, рубрики, всё, что важно для поиска | Ленты и витрины внутри одной страницы, где SEO не главное |
Компромисс, который обычно и работает: визуально оставить кнопку подгрузки, но сохранить настоящие ссылки пагинации — скрытыми для мыши, зато доступными поисковику. Тогда читатель листает ленту, а обходчик видит обычную структуру страниц. Второй полезный элемент навигации, который стоит продумать вместе с пагинацией, — хлебные крошки в WordPress: они возвращают читателя на уровень выше с любой страницы.
Производительность: подсчёт записей и кэш
Пагинация — это дополнительная работа для базы. Чтобы узнать общее число страниц, WordPress выполняет запрос подсчёта найденных записей, а сам список запрашивает отдельно. На больших сайтах именно подсчёт становится узким местом.
| Что влияет | Как проявляется | Что делать |
|---|---|---|
| Подсчёт найденных записей | Второй тяжёлый запрос на каждой странице архива | Оставить подсчёт там, где нужны номера страниц; убрать no_found_rows нельзя, если пагинация есть, — вместо этого ограничить условия выборки |
| Сложные метазапросы и таксономии | Медленный ответ при фильтрации и сортировке | Упростить условия, убрать сортировку по случайному полю, кэшировать список |
| Выборка полных объектов | Лишние поля и запросы к метаданным | Брать fields => 'ids', если нужны только ссылки и заголовки |
| Отсутствие кэша | Один и тот же запрос повторяется при каждом заходе | Кэшировать список через транзиент на несколько часов |
Практический вывод прост: пагинация сама по себе недорога, дорого — сложная выборка под ней. Если архив фильтруется по десятку метаполей, никакая функция вывода не спасёт; сначала упрощают запрос, потом кэшируют результат. Как это работает целиком, разобрано в статье про кэширование WordPress для новичков, а готовые приёмы оптимизации собраны в подборке полезные сниппеты functions.php.
И одна вещь, которую проверяют в последнюю очередь, хотя стоило бы в первую: количество записей на странице должно совпадать в трёх местах — в настройках чтения, в параметре posts_per_page вашей выборки и в ожиданиях пагинации. Расхождение на единицу даёт «пропавшие» записи: они есть в базе, но не показываются ни на одной странице, потому что попали в промежуток между страницами.
Частые ошибки и порядок отладки
Практика показывает, что девять из десяти проблем с пагинацией — это не сломанная функция, а рассинхронизация данных. Поэтому отладку стоит вести по одному и тому же порядку, а не наугад менять код.
Шаг первый: убедиться, что включены постоянные ссылки, отличные от простых. Шаг второй: открыть страницу /page/2/ вручную и посмотреть, что отвечает сервер. Шаг третий: сравнить max_num_pages вашего запроса с тем, что реально выводится, — для этого удобно временно вывести var_dump при включённой отладке. Шаг четвёртый: проверить, вызывается ли wp_reset_postdata(), и не переопределяет ли кто-то запрос через pre_get_posts без проверки главного запроса. Пятый шаг: пересобрать правила перезаписи один раз и убедиться, что 404 исчез.
<?php
// 5. Диагностика: что происходит на второй странице
add_action( 'wp_footer', 'my_pager_debug' );
function my_pager_debug() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
global $wp_query;
printf(
'<!-- paged=%d, page=%d, max_num_pages=%d, found=%d -->',
(int) get_query_var( 'paged' ),
(int) get_query_var( 'page' ),
(int) $wp_query->max_num_pages,
(int) $wp_query->found_posts
);
}
Такой комментарий в коде страницы виден только администратору и сразу отвечает на главные вопросы: какая страница запрошена, сколько их всего и сколько записей нашлось. Если max_num_pages равно единице при живом архиве — проблема в запросе, а не в выводе. Если номер страницы всегда равен единице — теряется переменная paged, и возвращаться надо к своему WP_Query.
Заодно проверьте мелочи, которые портят впечатление: ссылки пагинации без осмысленных текстов, отсутствие aria-label у блока навигации, кнопки размером меньше пальца на телефоне. Если меню и ссылки в теме ведут себя непредсказуемо, полезно сначала навести порядок в навигации как таковой — об этом статья про меню WordPress и вывод навигации, а блоки похожих материалов рядом с пагинацией разобраны в публикации про связанные записи в WordPress. И обязательно работайте в дочерней теме WordPress, иначе первое же обновление темы снесёт все правки шаблонов.

Частые вопросы про пагинацию в WordPress
Почему на второй странице пагинации появляется 404?
Чаще всего потому, что постоянные ссылки стоят в простом формате либо правила перезаписи не соответствуют текущим настройкам. Проверьте формат ссылок, откройте страницу настроек «Постоянные ссылки» один раз, чтобы WordPress пересобрал правила, и убедитесь, что свои правила через add_rewrite_rule сбрасываются только при активации темы или плагина, а не на каждом запросе.
Чем paginate_links отличается от the_posts_pagination?
paginate_links принимает массив параметров и возвращает разметку, которую вы полностью контролируете: классы, тексты, размер окна вокруг активной страницы. the_posts_pagination — готовая обёртка для главного запроса: она сама подставляет общее число страниц и печатает nav с заголовком для скринридеров. Для кастомного WP_Query берите paginate_links и передавайте total из max_num_pages этого запроса.
Как вывести пагинацию для своего WP_Query?
Передайте в запрос параметр paged со значением get_query_var, а на статической главной дополнительно учитывайте get_query_var page. Общее число страниц берите из max_num_pages своего объекта запроса, а не из глобального. После цикла обязательно вызывайте wp_reset_postdata, иначе пострадают все блоки ниже на странице.
Можно ли использовать пагинацию без ЧПУ?
Да, но без номеров страниц. При простом формате постоянных ссылок останутся только ссылки «следующая» и «предыдущая», а переходы будут выглядеть как параметры в адресе. Если архив важен для поиска, лучше включить человекопонятные адреса — это одно переключение в настройках, но существующие ссылки стоит проверить после переключения.
Как изменить количество записей на странице архива?
Не через шаблон, а через запрос: подключитесь к pre_get_posts, проверьте is_admin и is_main_query, а затем задайте posts_per_page через set. Проверки обязательны, иначе правило сработает в админке и в побочных выборках. Число записей в запросе и в функции пагинации должно совпадать, иначе часть записей не покажется ни на одной странице.
Стоит ли заменять пагинацию на бесконечный скролл?
Для каталогов и архивов, которые должны попадать в поиск, нет: отдельные страницы с адресами видны поисковику, а подгружаемые порции почти нет. Компромисс — оставить настоящие ссылки пагинации в разметке и добавить кнопку подгрузки поверх них. Если лента не важна для поиска, бесконечный скролл допустим, но продумайте возврат к прежней позиции.
Вывод
Пагинация в WordPress — задача на согласование чисел, а не на разметку. Основной цикл знает номер страницы и общее количество страниц; ваша выборка на WP_Query должна получить те же значения явно, иначе ссылки будут вести в пустоту. Порядок работы такой: включить человекопонятные адреса, выбрать функцию вывода под задачу, для своей выборки передать paged и взять total из max_num_pages, для кастомных архивов поправить запрос через pre_get_posts, а правила перезаписи сбрасывать один раз при активации.
Дальше остаётся производительность и аккуратность: не считать лишние записи, кэшировать сложные выборки, не забывать wp_reset_postdata и проверять, что количество записей на странице совпадает во всех местах. Если нужна разметка под дизайн — берите paginate_links и меняйте её через фильтры, а не через CSS-костыли. И проверяйте вторую страницу вручную после каждой правки шаблона: именно там всплывают и 404, и потерянные записи.
Если разбираться с пагинацией, шаблонами и выводом архивов не хочется — эту работу можно передать команде. Мы правим темы и дочерние темы, настраиваем вывод архивов, каталогов и таксономий, чиним 404 на страницах пагинации и ускоряем тяжёлые выборки. Посмотрите услуги по разработке и доработке сайтов, изучите портфолио выполненных проектов и почитайте блог о WordPress и продвижении сайтов. Опишите задачу через форму контактов — предложим решение и рассчитаем стоимость.



