303 See Other: редирект после POST-отправки формы
303 See Other сообщает клиенту, что запрос обработан, а результат находится по другому URL, который следует запросить методом GET. Это правильный ответ на отправку формы, после которой посетитель должен оказаться на странице результата или подтверждения, а не оставаться на POST.
Для чего нужен 303
303 на самом деле не про перенос ресурса. Он про отделение действия от его результата. Клиент отправляет POST,
сервер выполняет действие, а затем вместо того чтобы вернуть результат прямо в ответе на POST, отвечает кодом 303
с заголовком Location. Клиент делает новый GET по этому адресу, и в адресной строке браузера оказывается
обычный URL, который можно добавить в закладки и перезагрузить.
Определяющее правило - метод: каким бы ни был исходный метод запроса, повторный запрос к URL из Location
всегда GET. Это прямая противоположность 307 и 308, которые требуют сохранения исходного метода и тела запроса.
Post/Redirect/Get и какую проблему он решает
Без редиректа после POST страница результата принадлежит самому POST-запросу, что вызывает знакомые проблемы:
- Нажатие "обновить" повторно отправляет форму, поэтому заказ оформляется или комментарий публикуется дважды. Браузеры предупреждают об этом диалогом "подтвердите повторную отправку формы", что само по себе неприятный опыт.
- Страницу нельзя добавить в закладки или поделиться ею, потому что URL не описывает содержимое.
- Кнопка "назад" приводит к запросу на повторную отправку вместо предыдущей страницы.
Post/Redirect/Get решает все эти проблемы разом. POST выполняет действие и отвечает 303; браузер делает GET на URL результата; повторная загрузка теперь просто заново получает безобидную страницу. Именно поэтому почти в каждом фреймворке есть хелпер "редирект после сохранения".
303 против 302 против 307
- 302 Found - временный редирект, чья обработка метода исторически неоднозначна. Большинство клиентов при следовании ему превращают POST в GET, что обычно и нужно, но стандарт этого не требует. Многие фреймворки до сих пор отдают 302 для схемы post-затем-редирект, и на практике это работает.
- 303 See Other делает это поведение явным и обязательным. Если ваше намерение - "не повторяй мой POST, вместо этого сделай GET сюда", 303 выражает это однозначно.
- 307 Temporary Redirect - как раз тот код, которого здесь стоит избегать. Он требует повторить POST на новый URL, что для отправки формы означает выполнение действия дважды.
Одна практическая оговорка: 303 появился в HTTP/1.1, поэтому клиент, говорящий на HTTP/1.0, может его не понять. Некоторые фреймворки по этой причине по умолчанию используют 302. В современном вебе это редко становится реальным ограничением, но именно этим объясняется разница в поведении по умолчанию между языками.
Где еще встречается 303
- API, запускающие долгую работу. POST запускает задачу и отвечает 303 с URL статуса этой задачи, либо 202 с URL статуса в теле ответа. Используются оба паттерна; у 303 есть преимущество в том, что браузер следует по нему автоматически.
- Потоки OAuth и входа в систему. Некоторые серверы авторизации перенаправляют обратно в клиентское приложение кодом 303, чтобы колбэк запрашивался методом GET независимо от того, как пришел исходный запрос.
- Настройки связанных данных и согласования содержимого, различающие реальный объект от документа, его описывающего, и использующие 303, чтобы направить клиентов от идентификатора к документу.
Как использовать и проверить
- После успешного POST возвращайте 303 с абсолютным
Location, указывающим на страницу результата. Не отдавайте результат прямо в теле ответа. - Сделайте адрес назначения идемпотентным. Он будет запрашиваться заново при каждой перезагрузке и каждом переходе назад, поэтому его должно быть безопасно получать повторно методом GET.
- Любое одноразовое сообщение, например "изменения сохранены", храните в состоянии сессии или параметре запроса, а не в теле редиректа. Тело ответа 303 обычно не отображается.
- Проверьте код и адрес назначения:
Вы должны увидетьcurl -si -X POST -d "name=test" https://example.com/submit | grep -Ei "^(HTTP|location)"HTTP/1.1 303 See Otherи URL результата. - Перейдите по нему и убедитесь, что второй запрос - это GET, возвращающий 200. Если в логах сервера виден повторный POST, значит используется правило 307, и его нужно менять.
- Проверьте, что адрес назначения сам не является редиректом. 303, ведущий в 301, а тот - на настоящую страницу, это три перехода ради одной отправки формы.
Мониторинг эндпоинтов за формой
Сломанный поток post-затем-редирект невидим для проверки, которая обращается только к главной странице. Сайт работает, форма отображается, и только те, кто ее отправляет, натыкаются на сбой. Ловите это заранее, наблюдая за эндпоинтами за действием - страницей результата, URL подтверждения и маршрутом статуса задачи, - и проверяя ожидаемый код состояния и содержимое для каждого из них. HostTracker предлагает 13 типов мониторов и оповещает по email, SMS, голосовым звонком, в Slack, Telegram и другими способами. Проверьте, что возвращает любой URL, с помощью инструмента HTTP-проверки, а о соседних кодах читайте в 301 против 302 и в полном руководстве по семейству перенаправлений 3xx.