Организация кнопки «Назад» на PHP: варианты реализации и код

Раздел: Разработка веб-страниц -> Навигация

Основные способы возврата на предыдущую страницу в PHP

При разработке веб-приложений часто возникает необходимость предоставить пользователю возможность вернуться на предыдущую страницу после выполнения какого-либо действия (отправка формы, авторизация, редактирование). В PHP существует несколько подходов для реализации этой навигации, каждый из которых имеет свои особенности и области применения. Ниже представлено наиболее эффективное решение, а также альтернативные варианты с примерами кода и разбором возможных проблем.

Как сохранить и восстановить предыдущий URL с помощью сессий?

Самым надёжным способом является хранение стека просмотренных страниц в сессии. Этот метод гарантирует доступ к предыдущему адресу даже при отключённом HTTP_REFERER и позволяет избежать случайного возврата на внешние сайты.

Шаг 1. Инициализация сессии и сохранение текущего URL. Код должен выполняться на каждой странице, где требуется отслеживать навигацию:


<?php
session_start();

// Функция для получения текущего URL
function getCurrentUrl() {
    $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://';
    return $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
}

// Инициализация массива истории, если его нет
if (!isset($_SESSION['history'])) {
    $_SESSION['history'] = [];
}

// Добавляем текущий URL в историю, если он отличается от последнего (чтобы избежать дублирования при обновлении)
$current = getCurrentUrl();
$last = end($_SESSION['history']);
if ($_SESSION['history'] === [] || $current !== $last) {
    $_SESSION['history'][] = $current;
}

// Ограничим размер истории (например, 10 шагов)
if (count($_SESSION['history']) > 10) {
    array_shift($_SESSION['history']);
}
?>

Php первая страница (первая страница в php)

Шаг 2. Вывод ссылки для возврата на предыдущую страницу. Предпоследний элемент массива - это предыдущий URL:


<?php
$prevUrl = null;
if (count($_SESSION['history']) >= 2) {
    $prevUrl = $_SESSION['history'][count($_SESSION['history']) - 2];
}
?>
<a href="<?= $prevUrl ?? 'javascript:history.back()' ?>">Назад</a>

Php предыдущая страница (предыдущая страница в php)

Возможные проблемы и их решения:

  • При использовании AJAX-запросов или фоновых скриптов не следует добавлять их URL в историю. Решение: проверять, не является ли запрос AJAX через $_SERVER['HTTP_X_REQUESTED_WITH'] или наличие специального параметра.
  • При редиректе (header Location) история может быть повреждена. Решение: перед редиректом удалять последний элемент, если это технический редирект, или не добавлять промежуточный URL в историю.
  • Сессия может быть недоступна при высокой нагрузке или при использовании файлового хранилища. Рекомендуется использовать надёжное хранилище (Memcached, Redis) для сессий.

Как получить URL предыдущей страницы через HTTP_REFERER?

Простейший способ - использовать суперглобальный массив $_SERVER['HTTP_REFERER'], который содержит адрес, с которого пользователь перешёл на текущую страницу. Однако этот заголовок не всегда передаётся браузером и может быть подделан.


<?php
$referer = $_SERVER['HTTP_REFERER'] ?? null;
if ($referer) {
    // Дополнительная проверка, что реферер принадлежит тому же домену
    $refererHost = parse_url($referer, PHP_URL_HOST);
    if ($refererHost === $_SERVER['HTTP_HOST']) {
        echo '<a href="' . htmlspecialchars($referer) . '">Назад</a>';
    } else {
        echo '<a href="/">На главную</a>';
    }
} else {
    echo 'История перехода недоступна.';
}
?>

Проблемы и решения:

  • Заголовок HTTP_REFERER пуст или отсутствует, если пользователь ввёл адрес вручную или перешёл по закладке. Решение: использовать резервный механизм (например, сессию).
  • Реферер может быть подделан клиентом. Решение: проверять домен реферера и дополнительно сверять с разрешёнными источниками.

Как передать предыдущий URL через GET-параметр?

Можно явно передавать адрес предыдущей страницы в строке запроса при переходе с одной страницы на другую. Этот метод удобен для одноразовых действий, например, после авторизации.


<?php
// На странице, с которой мы хотим вернуться, формируем ссылку с параметром from
$currentUrl = urlencode('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
echo '<a href="/login.php?from=' . $currentUrl . '">Войти</a>';
?>

На странице логина после успешного входа обрабатываем параметр from:


<?php
if (isset($_GET['from']) && !empty($_GET['from'])) {
    $redirectTo = urldecode($_GET['from']);
    // Валидация: убедиться, что URL ведёт на тот же сайт
    $redirectHost = parse_url($redirectTo, PHP_URL_HOST);
    if ($redirectHost === $_SERVER['HTTP_HOST']) {
        header('Location: ' . $redirectTo);
        exit;
    }
}
// перенаправление на главную, если параметр недействителен
header('Location: /');
?>

Проблемы и решения:

  • Параметр from может быть изменён пользователем. Решение: проверять, что URL однозначно принадлежит тому же домену и не указывает на внешние ресурсы. Дополнительно можно подписывать URL хэшем или шифровать.
  • При длинной цепочке переходов параметр нужно каждый раз передавать, что загромождает URL. Решение: комбинировать с сессией для хранения последней «важной» страницы.

Как использовать куки для хранения предыдущей страницы?

Хранение предыдущего URL в куки позволяет сохранять информацию между сессиями, но имеет ограничения по размеру (не более 4 КБ) и не поддерживает хранение сложной структуры (стек).


<?php
$current = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
// Сохраняем текущий URL как 'prev' на 1 час
setcookie('prev', $current, time() + 3600, '/');

// Для вывода ссылки на предыдущую страницу (которая была установлена ранее)
if (isset($_COOKIE['prev'])) {
    $prev = $_COOKIE['prev'];
    echo '<a href="' . htmlspecialchars($prev) . '">Назад</a>';
}
?>

Проблемы и решения:

  • Куки могут быть отключены в браузере. Решение: предусмотреть альтернативный способ (сессия).
  • При каждом обновлении страницы кука обновляется, и предыдущий URL теряется. Для хранения стека потребуется несколько кук или сериализация массива в JSON. Лучше использовать сессии.

В каждом проекте следует выбирать метод исходя из требований к безопасности, надёжности и сложности реализации. Комбинация сессий с резервным использованием HTTP_REFERER или GET-параметров даст максимальную гибкость.

Расширенные примеры реализации возврата на предыдущую страницу

Пример 1. Стек истории в сессии с возможностью отката на несколько шагов

Этот пример показывает, как создать полноценную навигацию «вперёд/назад» в пределах сайта. Код отслеживает посещённые страницы и позволяет пользователю вернуться на 2-3 шага назад.

Код:

Пример

<?php
session_start();

class NavigationHistory {
    private $maxSize;

    public function __construct($maxSize = 10) {
        $this->maxSize = $maxSize;
        if (!isset($_SESSION['nav'])) {
            $_SESSION['nav'] = [];
        }
    }

    public function addPage($url) {
        // Удаляем дубликаты подряд
        $last = end($_SESSION['nav']);
        if ($url === $last) {
            return;
        }
        array_push($_SESSION['nav'], $url);
        if (count($_SESSION['nav']) > $this->maxSize) {
            array_shift($_SESSION['nav']);
        }
    }

    public function getPrevious($steps = 1) {
        $index = count($_SESSION['nav']) - 1 - $steps;
        if ($index < 0) {
            return null;
        }
        return $_SESSION['nav'][$index] ?? null;
    }

    public function getHistory() {
        return $_SESSION['nav'];
    }
}

$nav = new NavigationHistory();
$currentUrl = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$nav->addPage($currentUrl);

// Использование
echo 'История: ';
print_r($nav->getHistory());
echo '<br>';
echo 'Предыдущая страница (на 1 шаг): ' . ($nav->getPrevious(1) ?? 'нет') . '<br>';
echo 'Страница два шага назад: ' . ($nav->getPrevious(2) ?? 'нет') . '<br>';
?>

Результат (после посещения страниц /, /about, /contact):

История: Array
(
    [0] => http://example.com/
    [1] => http://example.com/about
    [2] => http://example.com/contact
)
Предыдущая страница (на 1 шаг): http://example.com/about
Страница два шага назад: http://example.com/

Пример 2. Использование HTTP_REFERER с проверкой домена и подменой

Данный код проверяет, что реферер является внутренней страницей того же сайта, иначе предлагает перейти на главную. Это предотвращает использование внешних ссылок в качестве предыдущей страницы.

Пример

<?php
function sanitizeReferer($referer) {
    if (!$referer) {
        return null;
    }
    $host = parse_url($referer, PHP_URL_HOST);
    // Разрешаем только основной домен и поддомены
    if ($host === $_SERVER['HTTP_HOST'] || strpos($host, '.' . $_SERVER['HTTP_HOST']) !== false) {
        return $referer;
    }
    return null;
}

$prev = sanitizeReferer($_SERVER['HTTP_REFERER'] ?? '');
if ($prev) {
    echo '<a href="' . htmlspecialchars($prev) . '" class="btn-back">Вернуться</a>';
} else {
    echo '<a href="/" class="btn-back">На главную</a>';
}
?>

Результат: при переходе с http://example.com/page1 на http://example.com/page2 реферер будет http://example.com/page1 - ссылка сработает. Если пользователь пришёл с google.com, вернётся ссылка на главную.

Пример 3. Передача параметра from с подписью для защиты

Чтобы исключить подмену параметра from, данные можно подписать секретным ключом (HMAC). На странице назначения подпись проверяется.

Пример

<?php
$secret = 'mySecretKey123';

// Формирование ссылки с подписью
$redirectAfterLogin = 'http://example.com/dashboard';
$signature = hash_hmac('sha256', $redirectAfterLogin, $secret);
$link = '/login.php?from=' . urlencode($redirectAfterLogin) . '&sign=' . $signature;
echo '<a href="' . $link . '">Войти, чтобы вернуться в дашборд</a>';

// На странице login.php проверка
if (isset($_GET['from'], $_GET['sign'])) {
    $expectedSign = hash_hmac('sha256', $_GET['from'], $secret);
    if (hash_equals($expectedSign, $_GET['sign'])) {
        // подпись верна, можно редиректить
        $redirectTo = urldecode($_GET['from']);
        // дополнительная проверка домена
        if (parse_url($redirectTo, PHP_URL_HOST) === $_SERVER['HTTP_HOST']) {
            header('Location: ' . $redirectTo);
            exit;
        }
    }
}
// Если проверка не прошла, перенаправление на главную
header('Location: /');
?>

Результат: подпись гарантирует, что параметр from не был изменён злоумышленником. Если кто-то попытается заменить URL, хэш не совпадёт, и перенаправление не выполнится.

Пример 4. Логирование и показ списка последних страниц с помощью кук

Хранение в куках сериализованного JSON-массива последних посещённых страниц (до 5). Такой подход может использоваться для виджета «Вы недавно смотрели».

Пример

<?php
$currentUrl = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$history = [];
if (isset($_COOKIE['page_history'])) {
    $history = json_decode($_COOKIE['page_history'], true);
    if (!is_array($history)) {
        $history = [];
    }
}
// Добавляем новый URL, если он не последний (чтобы не дублировать)
if (end($history) !== $currentUrl) {
    $history[] = $currentUrl;
    if (count($history) > 5) {
        array_shift($history);
    }
    setcookie('page_history', json_encode($history), time() + 86400, '/');
}

// Вывод списка последних страниц
if (!empty($history)) {
    echo '<h4>Недавние страницы:</h4><ul>';
    foreach (array_reverse($history) as $url) {
        echo '<li><a href="' . htmlspecialchars($url) . '">' . htmlspecialchars($url) . '</a></li>';
    }
    echo '</ul>';
}
?>

Результат: в куке сохраняется массив из 5 последних посещённых страниц. При каждом обновлении страницы массив обновляется. Список выводится в обратном порядке (сначала последняя).

Предыдущая страница в PHP - comments

En
Php предыдущая страница (php)