Last active
February 6, 2026 12:03
-
-
Save andrewinsidelazarev/a154843e447333fd2247585d74c9cea3 to your computer and use it in GitHub Desktop.
Advanced 404-page snippet for WordPress
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function handle_custom_404() { | |
| // Получаем запрашиваемый путь URL | |
| $request_uri = $_SERVER['REQUEST_URI']; | |
| $request_path = trim(urldecode($request_uri), '/'); | |
| // Извлекаем только последний сегмент пути | |
| $last_segment = basename($request_path); | |
| // Функция для поиска существующего пути в других папках | |
| function find_existing_path($path) { | |
| global $wpdb; | |
| // Выполняем объединенный запрос для поиска слага в постах и тегах без ограничения LIMIT 1 | |
| $results = $wpdb->get_results($wpdb->prepare( | |
| "(SELECT ID AS item_id, 'post' AS item_type FROM {$wpdb->posts} | |
| WHERE post_name = %s AND post_status = 'publish') | |
| UNION ALL | |
| (SELECT t.term_id AS item_id, 'tag' AS item_type FROM {$wpdb->terms} t | |
| INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id | |
| WHERE t.slug = %s AND tt.taxonomy = 'post_tag')", | |
| $path, $path | |
| )); | |
| // Если найдена ровно одна запись, возвращаем её URL | |
| if (count($results) === 1) { | |
| $result = $results[0]; | |
| if ($result->item_type === 'post') { | |
| return get_permalink($result->item_id); | |
| } elseif ($result->item_type === 'tag') { | |
| return get_tag_link($result->item_id); | |
| } | |
| } | |
| // Если результатов несколько или ничего не найдено, используем старую логику | |
| $paths_to_check = ['blog', 'category', 'news', 'tag', 'news/news-photo']; | |
| foreach ($paths_to_check as $folder) { | |
| $potential_path = $folder . '/' . $path; | |
| // Проверяем, существует ли страница по этому пути или термин в нужной таксономии | |
| if (url_to_postid(site_url($potential_path)) != 0 || term_exists($path, $folder)) { | |
| return site_url($potential_path); | |
| } | |
| } | |
| return false; | |
| } | |
| // Проверка, существует ли похожий путь в других папках | |
| $existing_path = find_existing_path($last_segment); | |
| if ($existing_path) { | |
| // Если найден похожий путь, выполняем редирект | |
| wp_redirect($existing_path, 301); | |
| exit; | |
| } | |
| // Поиск по ключевым словам, заменяя "-" и "_" на плюсы | |
| $search_query = str_replace(['-', '_', '%20'], '+', $last_segment); | |
| // Выполняем поиск по ключевым словам | |
| $search = new WP_Query([ | |
| 's' => $search_query, | |
| 'posts_per_page' => 5 | |
| ]); | |
| // Выводим результаты, если они найдены | |
| if ($search->have_posts()) { | |
| echo '</br>'; | |
| $locale = get_locale(); | |
| if ($locale === 'ru_RU') { | |
| echo '<h2>Возможно, Вы искали это:</h2>'; | |
| } elseif ($locale === 'it_IT') { | |
| echo '<h2>Forse stavi cercando questo:</h2>'; | |
| } else { | |
| echo '<h2>Perhaps you were looking for this:</h2>'; | |
| } | |
| echo '</br>'; | |
| echo '<div>'; | |
| // Вместо вывода ссылок, выводим миниатюры постов | |
| while ($search->have_posts()) : | |
| $search->the_post(); | |
| echo '<div class="post-thumbnail">'; | |
| get_template_part('content', get_post_format()); | |
| echo '</div>'; | |
| endwhile; | |
| // Выводим пагинацию | |
| // the_posts_pagination(); | |
| echo '</div>'; | |
| wp_reset_postdata(); | |
| } | |
| } | |
Author
Author
<?php // Вызов функции для проверки и вывода предложений handle_custom_404(); ?>
Author
function handle_custom_404() {
// 1. ПОЛУЧАЕМ И ЧИСТИМ URI
// urldecode превращает %2F -> / и %3F -> ?
$request_uri = urldecode($_SERVER['REQUEST_URI']);
// Отрезаем GET-параметры (все что после ?)
$clean_uri = strtok($request_uri, '?');
// Отрезаем слеши и получаем только "хвост" (слаг)
$request_path = trim($clean_uri, '/');
$last_segment = basename($request_path);
// Если мы на главной или путь пустой — выходим
if (empty($last_segment)) return;
// 2. ФУНКЦИЯ ПОИСКА (Безопасное объявление)
if (!function_exists('find_existing_path_logic')) {
function find_existing_path_logic($path) {
global $wpdb;
// Поиск в базе (Посты, Страницы или Теги)
$results = $wpdb->get_results($wpdb->prepare(
"(SELECT ID AS item_id, 'post' AS item_type FROM {$wpdb->posts}
WHERE post_name = %s AND post_status = 'publish' AND post_type NOT IN ('revision', 'attachment'))
UNION ALL
(SELECT t.term_id AS item_id, 'tag' AS item_type FROM {$wpdb->terms} t
INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
WHERE t.slug = %s AND tt.taxonomy = 'post_tag')",
$path, $path
));
if (count($results) === 1) {
$result = $results[0];
return ($result->item_type === 'post') ? get_permalink($result->item_id) : get_tag_link($result->item_id);
}
// Проверка жестко заданных папок
$paths_to_check = ['blog', 'category', 'news', 'tag', 'news/news-photo'];
foreach ($paths_to_check as $folder) {
$potential_url = home_url($folder . '/' . $path);
// Проверяем, существует ли пост по этому адресу
if (url_to_postid($potential_url) != 0 || term_exists($path, $folder)) {
return $potential_url;
}
}
return false;
}
}
// 3. РЕДИРЕКТ
$existing_path = find_existing_path_logic($last_segment);
if ($existing_path) {
// Проверяем, чтобы не редиректить на тот же самый URL (защита от петли)
$current_url = home_url(add_query_arg([], $GLOBALS['wp']->request));
if (untrailingslashit($existing_path) !== untrailingslashit($current_url)) {
wp_redirect($existing_path, 301);
exit;
}
}
// 4. ВЫВОД ПОИСКОВОЙ ВЫДАЧИ (Если редирект не удался)
$search_query = str_replace(['-', '_', '%20'], ' ', $last_segment);
$search = new WP_Query([
's' => $search_query,
'posts_per_page' => 5,
'post_status' => 'publish'
]);
if ($search->have_posts()) {
$locale = get_locale();
$titles = [
'ru_RU' => 'Возможно, Вы искали это:',
'it_IT' => 'Forse stavi cercando questo:',
'default' => 'Perhaps you were looking for this:'
];
$title = $titles[$locale] ?? $titles['default'];
echo '<div class="custom-404-suggestions">';
echo '<h2>' . esc_html($title) . '</h2><br/>';
while ($search->have_posts()) : $search->the_post();
echo '<div class="post-suggestion-item">';
if (locate_template('content.php')) {
get_template_part('content', get_post_format());
} else {
echo '<h3><a href="' . get_permalink() . '">' . get_the_title() . '</a></h3>';
}
echo '</div>';
endwhile;
echo '</div>';
wp_reset_postdata();
}
}
Author
Главные изменения в финальной версии:
Защита от циклического редиректа: Добавлено сравнение $existing_path с текущим URL. Если они совпадут (например, из-за настроек плагинов), сайт не уйдет в бесконечную перезагрузку.
Исключение вложений: В SQL-запрос добавлено post_type NOT IN ('revision', 'attachment'). Чтобы при поиске по слагу «apple» сайт не редиректил случайно на картинку apple.jpg вместо страницы.
Безопасный вывод: Добавлен esc_html() для заголовков — это базовое правило безопасности WordPress.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
В файл 404.php в теме вставить: