Кратко

  • RFC 1945 мая 1996 года называл 302 «Moved Temporarily» и запрещал автоматически перенаправлять POST без возможности подтверждения пользователем. RFC 2068 января 1997 года сохранил требование, отметил ошибочное переключение POST на GET некоторыми клиентами и определил 303 «See Other» для получения результата методом GET.
  • RFC 2616 июня 1999 года переименовал 302 в «Found» и сообщил, что большинство действующих клиентов обрабатывали его как 303: запрашивали адрес Location методом GET независимо от исходного метода. В этом документе появился 307 «Temporary Redirect» для сохранения метода; 303 существовал с 1997 года.
  • RFC 9110 июня 2022 года по историческим причинам разрешает клиенту заменить POST на GET после 302. При автоматическом следовании 307 менять метод нельзя. От этого выбора зависит, уйдёт ли тело отправленной формы по новому адресу.

Переезд адреса меняет ещё и действие

RFC 1945, опубликованный в мае 1996 года, описывал код 302 как временное перемещение ресурса. Сервер возвращает новый адрес в Location; клиент может обратиться туда, но в дальнейшем продолжает пользоваться прежним адресом. Для обычного чтения страницы схема кажется простой: запросить документ, получить указатель, открыть его в другом месте.

У формы другая цена. Пользователь нажал кнопку отправки, браузер послал POST с данными, а сервер ответил 302. Если браузер повторит POST по адресу из Location, туда же может уйти тело запроса. Если он переключится на GET, новая точка получит запрос на чтение, а отправленное действие там не повторится. Один и тот же адрес перенаправления поэтому скрывает два разных решения: куда идти и что именно там делать.

Текст 1996 года ставил тормоз на автоматический шаг. После ответа 302 на POST пользовательский агент не должен был автоматически выполнять перенаправление, если пользователь не мог его подтвердить. Оговорка касалась не красоты интерфейса: повторить отправку данных по другому адресу — существенное действие. Решение о новом адресе не передавало автоматически согласия на повторную отправку.

У серверов при этом был обычный рабочий сценарий. Сначала обработать форму, затем показать страницу с итогом. Такая страница запрашивается методом GET, и браузеру не нужно посылать форму повторно. Раннее правило берегло отправку данных, но оставляло напряжение между осторожным обращением с POST и желанием немедленно показать результат. Браузеры решили вопрос собственным способом раньше, чем обозначение получило устойчивое место в протоколе.

В 1997 году разрыв уже записали

RFC 2068 января 1997 года сохранил для 302 название «Moved Temporarily». Автоматическое перенаправление запроса, отличного от GET или HEAD, по-прежнему требовало подтверждения пользователя. В том же разделе авторы отметили: некоторые действовавшие клиенты HTTP/1.0 ошибочно меняли POST на GET, следуя 302. Это не реконструкция задним числом. Разница между правилом и поведением уже была видна авторам спецификации.

Документ предложил ясный ответ для ситуации «форма обработана, покажи другую страницу»: 303 «See Other». Сервер мог указать на иной ресурс, который клиент должен получить методом GET. В числе типичных случаев был результат работы программы, запущенной POST-запросом. Отправка и чтение результата становились разными этапами, каждый со своим методом.

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

Важно не сдвигать даты, чтобы история звучала гладко. В RFC 1945 есть отдельное замечание об ошибочном переключении POST на GET после 301. Для 302 его правило — подтверждение перед автоматическим перенаправлением POST. Прямое замечание о клиентах, менявших POST на GET после 302, появляется в тексте 1997 года. Разница важна: конфликт развивался в документах, а не в поздней легенде о браузерах.

1999 год: стандарт уступил работающим клиентам

RFC 2616 июня 1999 года назвал 302 «Found» и сформулировал расхождение прямо. Документ отметил, что предыдущие версии спецификации не разрешали менять метод при перенаправлении, но большинство существовавших пользовательских агентов обрабатывали 302 как 303: делали GET по адресу Location независимо от метода исходного запроса. «Большинство» — оценка самого стандарта на тот момент; это не подсчёт версий браузеров в данной статье.

Авторы оставили 303 для перехода к другому ресурсу после действия и добавили 307 «Temporary Redirect». Новый код явно обозначал временный переход с сохранением метода запроса. Если серверу нужно, чтобы POST остался POST, у него появился способ сказать именно это. Если операция уже сделана и клиенту следует получить страницу результата, для этого служит 303. Сохранение старого 302 отражало уже работающую сеть, а не чистую таблицу смыслов.

Особенно показателен совет о совместимости в RFC 2616. Старые клиенты могли не понимать 303; для таких клиентов серверу предлагалось использовать 302, потому что большинство и так реагировали на него как на 303. Протокол описал чистый вариант и тут же признал, что для общения с установленными программами иногда приходится говорить на их языке. Это был не спор терминов: речь шла о том, какой запрос реально придёт на второй адрес.

При переходе к GET тело первоначального POST не воспроизводится в новой точке. Это удобно, когда POST уже совершил действие и новая страница лишь сообщает результат. Но сервер, который хотел перенести саму операцию, получит чтение вместо продолжения отправки. При 307 картина обратная: метод сохраняется, вместе с ним к новому URI может отправиться и тело. Администратору недостаточно решить, что «перенаправить» звучит правильно. Нужно выбрать судьбу отправленного действия.

Историческая привычка стала разрешённым поведением

RFC 9110 июня 2022 года до сих пор разрешает пользовательскому агенту по историческим причинам менять POST на GET при последующем запросе после 302. Слово «может» оставляет выбор клиенту, а не приказывает всегда менять метод. Если серверу этот переход не подходит, спецификация указывает на 307. При автоматическом следовании 307 клиент не должен менять исходный метод.

303 остаётся другим сигналом: ответ можно увидеть через получение иного ресурса, обычно с помощью GET или HEAD. Три кода дают серверу три способа обратиться к установленным клиентам, но 302 сохранил след старого компромисса. Название «Found» само по себе не сообщает, пойдёт ли исходный POST дальше. Его фактический смысл складывался из текста стандарта и того, что уже делали программы.

Сохранить POST — не универсальная победа над старым поведением. Если Location ведёт к иной точке, повторное отправление перенесёт туда и данные запроса. Бывает, что это именно нужный ход; бывает, что сервер сначала должен обработать действие, а затем указать страницу результата. Метод нельзя выбирать по принципу «новее значит лучше». На границе перенаправления он определяет, будет ли операция повторена или заменена чтением.

Первичные стандарты не дают перечня выпусков браузеров, в которых впервые появилось то или иное поведение. Для такой хронологии потребовались бы датированные записи реализации. Но ключевую последовательность они фиксируют сами: ограничение 1996 года, расхождение, записанное в 1997-м, признание практики большинства и новый 307 в 1999-м, сохранённое разрешение в 2022-м.