WPには「redirect_canonical」という filter hook があり、よしなにリダイレクトして URL を正規化している。

カスタムポストタイプの一覧ページは

  • https://example.com/custom/ (1ページ目)
  • https://example.com/custom/page/2/ (2ページ目)
  • https://example.com/custom/page/3/ (3ページ目)
  • https://example.com/custom/page/4/ (4ページ目)

とページネーションされており、「redirect_canonical」によって
「https://example.com/custom/page/1/」は「https://example.com/custom/」にリダイレクトされる。

今回は、「https://example.com/custom/」と「https://example.com/custom/page/1/」ページを混在させる方法を紹介する。

まず、「redirect_canonical」によるリダイレクトを解除する。

add_filter( 'redirect_canonical', function( $redirect_url ) {
  global $paged;
  if ( is_paged() || ( isset( $paged ) && $paged === 1 ) ) {
    return false;
  }
  return $redirect_url;
} );

その後、「template_redirect」の action hook などでテンプレートを分岐する。

global $paged;
if ( !is_paged() || ( isset( $paged ) && $paged === 1 ) ) {
 // https://example.com/custom/page/1/ など paged クエリバーに値がある場合
} else {
  // https://example.com/custom/
}


そして、最後にページネーションのリンクで「https://example.com/custom/」と書かれているものを「https://example.com/custom/page/1/」に書き直すだけだ。

// https://developer.wordpress.org/reference/functions/get_pagenum_link/
// のソースコードそのまま。長いコードがいやなら正規表現などで書き換える
add_filter( 'get_pagenum_link', function( $result, $pagenum ) {
	global $wp_rewrite;
 
	$pagenum = (int) $pagenum;

	$request = remove_query_arg( 'paged' );

	$home_root = parse_url( home_url() );
	$home_root = ( isset( $home_root['path'] ) ) ? $home_root['path'] : '';
	$home_root = preg_quote( $home_root, '|' );

	$request = preg_replace( '|^' . $home_root . '|i', '', $request );
	$request = preg_replace( '|^/+|', '', $request );

	if ( ! $wp_rewrite->using_permalinks() || is_admin() ) {
		$base = trailingslashit( get_bloginfo( 'url' ) );

		// if ( $pagenum > 1 ) {
		$result = add_query_arg( 'paged', $pagenum, $base . $request );
		// } else {
		// 	$result = $base . $request;
		// }
	} else {
		$qs_regex = '|\?.*?$|';
		preg_match( $qs_regex, $request, $qs_match );

		if ( ! empty( $qs_match[0] ) ) {
			$query_string = $qs_match[0];
			$request      = preg_replace( $qs_regex, '', $request );
		} else {
			$query_string = '';
		}

		$request = preg_replace( "|$wp_rewrite->pagination_base/\d+/?$|", '', $request );
		$request = preg_replace( '|^' . preg_quote( $wp_rewrite->index, '|' ) . '|i', '', $request );
		$request = ltrim( $request, '/' );

		$base = trailingslashit( get_bloginfo( 'url' ) );

		if ( $wp_rewrite->using_index_permalinks() && ( $pagenum > 1 || '' !== $request ) ) {
			$base .= $wp_rewrite->index . '/';
		}

		// if ( $pagenum > 1 ) {
			$request = ( ( ! empty( $request ) ) ? trailingslashit( $request ) : $request ) . user_trailingslashit( $wp_rewrite->pagination_base . '/' . $pagenum, 'paged' );
		// }

		$result = $base . $request . $query_string;
	}
	return $result;
}, 10, 2);

以上、複雑になるだけで使い所はほとんどなく、このあとにいろいろ問題が出てくると予測されるが、一度現場で必要だったので、備忘録としてここに残しておく。