Редиректы

Редирект — способ сказать клиенту: «этого больше здесь нет, иди туда». HTTP имеет несколько кодов, отличающихся в двух измерениях: постоянный/временный и сохраняет ли метод.

Матрица редиректов

КодНазваниеПостоянныйСохраняет методКогда использовать
301Moved PermanentlyДаИсторически нет (POST→GET), но противоречивоСмена URL сайта навсегда
302FoundНетИсторически нетLegacy, часто заменяется на 303 или 307
303See OtherНетВсегда меняет на GETПосле POST → GET (PRG pattern)
307Temporary RedirectНетДа, всегда сохраняетВременный редирект с POST/PUT/DELETE
308Permanent RedirectДаДа, всегда сохраняетПостоянный редирект современных API

Историческая путаница 301/302

По RFC 7231 формально 301/302 не должны менять метод. Но браузеры массово это делали ещё с Netscape — POST превращали в GET. Чтобы разрешить путаницу, ввели новые коды:

Для новых сервисов используй 303/307/308 — их поведение чётко определено.

Пример 301

HTTP/1.1 301 Moved Permanently
Location: https://new.example.com/page
Cache-Control: max-age=31536000

Браузер запомнит редирект надолго. Осторожно с 301 — если ошибся, откатить сложно (клиент не будет спрашивать снова).

PRG pattern (Post/Redirect/Get)

Классический паттерн для веб-форм:

  1. User сабмитит форму — POST /submit.
  2. Сервер обрабатывает, отвечает 303 See Other, Location: /success.
  3. Браузер идёт GET /success.
  4. User видит success-страницу.

Плюс: нажатие F5 не делает повторный POST (не будет «отправить форму ещё раз?»).

Meta refresh

<meta http-equiv="refresh" content="5; url=/new-page">

HTML-редирект через 5 секунд. Устарело, плохо для SEO, но иногда используется для «спасибо, вас перенаправляем...» страниц.

JS-редирект

window.location.href = '/new-page';
window.location.replace('/new-page');  // без записи в history

Работает всегда, но требует JS. Не индексируется поисковиками так же хорошо как 301.

Циклические редиректы

/a/b/a — цикл. Браузеры детектят и показывают ERR_TOO_MANY_REDIRECTS обычно после 20 переходов.

Nginx имеет отдельную защиту: internal redirect ограничен 10 хопами (server_names_hash_bucket_size).

SEO и редиректы

Как понимает Google
301Полностью переносит ссылочный вес на новый URL
302Раньше почти не переносил, с 2016 — тоже полностью
307/308Аналогично 302/301
meta refresh с 0 секКак 301
meta refresh с задержкойИгнорируется
JS-редиректПонимается, но медленно

Тонкости

Настройка

# nginx
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

# Apache .htaccess
Redirect 301 /old-page /new-page
RewriteRule ^old/(.*)$ /new/$1 [R=301,L]

# Express.js
res.redirect(301, '/new-page');
res.redirect(303, '/success');

См. также