Организация кнопки «Назад» на 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 последних посещённых страниц. При каждом обновлении страницы массив обновляется. Список выводится в обратном порядке (сначала последняя).