Comments 75
Поведение такое давно, но вроде только на «сетевых» ошибках.
Для 50X Chrome в каких-то сборках также использовал перезапросы. Не исключено, что вернутся к этой практике. В версии 67.0 использует только для 444-го.
Вероятно, 403, в данный момент оптимальное решение для браузерных запросов.
location @204 {
keepalive_timeout 0;
return 204;
}
А почему бы не следовать протоколу и не отдавать 403?
Я вот совсем не удивлюсь если Safari при таких разрывах начнет автоматически iCloud Private Relay включать, для благо пользователя, потому что (с точки зрения браузера) явно ж проблемы с сетью.
Expected code 444Это неправильный ответ?
Ну, для браузера — поведение в рамках ожидаемого. Пользователь пытается зайти на сайт, мы не получили никакого ответа — очевидно, соединение потеряно, будем ломиться дальше, вдруг получится. Браузеру надо осмысленный отлуп давать.
С реферером удивительно, конечно — почему он его теряет.
Точно так. Странно ждать адекватной реакции от собеседника, которого вы в ответ на вопрос даже не потрудились уведомить, что не хотите с ним говорить. Он и интересуется — вдруг у вас в ушах бананы, или у него что-то не то с голосом...
А насчёт реферера — поведение конечно спорное, но люди делают точно так же — после невнятных сбоев при попытке кому-то позвонить пытаются набрать номер в другом формате, а то и позвонить с другого аппарата ))
Имеет ли смысл возвращать 200-й с поясняющим сообщением, если этот трафик мы считаем некашерным? Тогда браузеры не будут переспрашивать.
Мне видится проблема в том, что 4XX-й код — это ошибка клиента, а повторно задать запрос в таком случае — это адекватное поведение (в большинстве вариантов). По ходу, надо вводить код 'Отстань' с принципиально отличающимся номером.
По ходу, надо вводить код 'Отстань' с принципиально отличающимся номером
204 No Content?
Любые 4хх и 5хх означают, что рыба тут в принципе бывает, но в данный момент что-то пошло не так, и можно попробовать зайти попозже. Поэтому чтобы клиент гарантированно успокоился, надо отвечать что-то из 2хх.
Ну уж нет. 403 говорит, что рыба тут не про твою честь, 401, что рыба тут для тех у кого ксива есть, 418 говорит, что вы попросили кофе у чайника, 451, что рыба тут для жителей других стран, а жителям твоей рыбу есть запретили власти.
Это вот так сходу.
404 Not Found[19] — самая распространённая ошибка при пользовании Интернетом, основная причина — ошибка в написании адреса Web-страницы. Сервер понял запрос, но не нашёл соответствующего ресурса по указанному URL. Если серверу известно, что по этому адресу был документ, то ему желательно использовать код 410. Ответ 404 может использоваться вместо 403, если требуется тщательно скрыть от посторонних глаз определённые ресурсы. Появился в HTTP/1.0.
…
410 Gone — такой ответ сервер посылает, если ресурс раньше был по указанному URL, но был удалён и теперь недоступен. Серверу в этом случае неизвестно и местоположение альтернативного документа (например копии). Если у сервера есть подозрение, что документ в ближайшее время может быть восстановлен, то лучше клиенту передать код 404. Появился в HTTP/1.1.
wikipedia
В настоящем оба кода говорят об отсутствии документа. Про будущее 404 ничего не говорит, а 410 тоже заявляет об отсутствии документа.
Если где-то в прошлом я нашел какую-то ссылку и запомнил ее, а сейчас перехожу по ней и там нет того документа, который я ожидал увидеть, то я предпочту получить внятную информацию о том, что именно случилось: это я ссылку неправильно запомнил и документ действительно был тут раньше но теперь удален.
Есть стандарт, в котором про 410 код написано следующее:
10.4.11 410 Gone
The requested resource is no longer available at the server and no forwarding address is known. This condition is expected to be considered permanent. Clients with link editing capabilities SHOULD delete references to the Request-URI after user approval. If the server does not know, or has no facility to determine, whether or not the condition is permanent, the status code 404 (Not Found) SHOULD be used instead. This response is cacheable unless indicated otherwise.
The 410 response is primarily intended to assist the task of web maintenance by notifying the recipient that the resource is intentionally unavailable and that the server owners desire that remote links to that resource be removed. Such an event is common for limited-time, promotional services and for resources belonging to individuals no longer working at the server's site. It is not necessary to mark all permanently unavailable resources as «gone» or to keep the mark for any length of time — that is left to the discretion of the server owner.
Вы конечно можете брать эти же самые коды ответа, но интерпретировать их по своему, но особо надеется на то, что вас поймут когда вы отдаете 410 вместо 404 не стоит.
Глупость сморозил, нет такого кода, проверять нечего.
И на деле он ничего не отвечает — он просто закрывает соединение.
А 444 код есть в стандартах? Судя по вашему комменту — нет. Тогда это самодеятельность nginx, и нет ни одного аргумента, почему клиент должен поступать иначе
Хороший пример: кто-то ломиться в phpMyAdmin на Вашем сайте. А у вас сайт написан на Java. И там уж точно нет phpMyAdmin. Надо отдавать 444.
А кто сказал, что туда придут только небраузерные боты?
И раз считаем, что боты пришли браузерные, то чем ситуация с 444-м ответом отличается от описанной в статье?
Неужели мир сошел с ума и теперь используются браузерные боты, чтобы проверить уязвимости phpMyAdmin?
Сейчас как раз браузерных ботов стало меньше, вот года 4-е назад был действительно «сильный» браузерный ботнет на 300k+ ботов. Последние годы таких крупных не встречал, но помельче попадаются регулярно. В мае 17-го (при поддержке одного белорусского провайдера) нашли очень интересный интегратор браузерного бота в «ломалке» от популярной IDE.
И это может быть не обязательно ботнет, а просто ссылка на популярной странице какой-нибудь соцсети.
Поэтому делать 444-е просто по какому-то локейшину неправильно. Нужны более явные критерии, навскидку:
map $http_user_agent $sign_http_ua
{
"" 1;
"~curl/.+" 1
"~*WordPress.+" 2;
default 0;
}
...
map $request_method $method_post_get
{
"POST" 1;
"GET" 1;
default 0;
}
...
if ( $sign_http_ua = 1 ) {return 444;}
if ( $sign_http_ua = 2 ) {return 444;}
if ( $method_post_get = 0 ) {return 444;}
Какие здесь критерии используем:
1) user-agent пустой "" или явно консольный "~curl/.+". Раз клиент сам признал, что он небраузер, то можем с ним обращаться как с небраузером.
2) "~*WordPress.+" — ping bot wordpress однозначно осуществляется с серверов.
3) Если нашему сайте не нужны методы отличные от POST/GET им также можем возвращать 444.
А вот по локейшину всё-таки лучше отдавать 403.
Такое поведение наблюдается уже достаточно давно, но когда именно Chrome и Firefox перешли на данный режим работы сказать не могу
Мне кажется, даже IE 3.0 перезапрашивал неотвечающую страницу по несколько раз.
В принципе ответ ничем не хуже несуществующего в стандарте 444.
Status of this Memo
This memo provides information for the Internet community. It does
not specify an Internet standard of any kind. Distribution of this
memo is unlimited
К тому же 418 описан в этом разделе:
2.3 HTCPCP return codes
Normal HTTP return codes are used to indicate difficulties of the
HTCPCP server. This section identifies special interpretations and
new return codes.
и вот:
2.3.2 418 I'm a teapot
Any attempt to brew coffee with a teapot should result in the error
code «418 I'm a teapot». The resulting entity body MAY be short and
stout.
Так как большинство серверов не поддерживают HTCPCP и не являются чайниками, они не должны отвечать кодом 418.
Вот из-за таких как вы и приходится ставить ESB на чайники вместо нормальных серверов…
418 "лучше" чем 444. 418 дойдет до браузера, отличить же 444 от "ошибки сети" (с точки зрения браузера) невозможно
А статья у нас не об особенностях работы 444, а о том, что для автора оказалось неожиданным поведение браузеров. Я бы переформулировал вывод в примерно такое: не используйте 444 в nginx для ответа реальным пользователям, даже если вам нечего им ответить. Пользователю лучше отдать 403, 404 или другой подходящий по смыслу код. 444 придумали для борьбы с «плохими» ботами и применять его нужно только после взвешивания всех «за» и «против», и желательно как временную меру (то есть например собирать 444 из лога и банить этих ботов в фаерволе или что-то типа того).
Другое дело, что браузеру не нравится, что с ним так (может пакет где потерялся, кабель выдернулся, ещё что), вот он и переспрашивает.
Я к тому, что есть некие правила, которые регламентируют взаимодействие и поведение, и, если этих правил нет, то о чем здесь можно говорить?
Скажем, есть ГОСТ для дорожной разметки на автомобильных дорогах. Насколько помню, для краски этой разметки видимость должна составлять не менее 50%. И что делать в этой ситуации водителям, которые еле видят остатки белых линий? Кто-то может всё-таки придерживаться её, а я, допустим, отступаю от требований разметки, ведь она не отвечает требованиям, а может, уже и безопасности. Абсолютно такая же ситуация с 444 кодом. Его попросту нет. Nginx рвет соединение, так зачем тогда озадачиваться разным поведениям браузеров? Мне именно это не понятно.
P.S.: а вообще нужен схожий код ответа (именно ответа, не молчаливого закрытия соединения), при котором только код (без тела и других заголовков) отсылается браузеру. Типа `419 Connection Filtred`
Во первых в разделе 8.1.4 rfc2616 написанно:
8.1.4 Practical Considerations
…
Client software SHOULD reopen the transport connection and retransmit the aborted sequence of requests without user interaction so long as the request sequence is idempotent (see section 9.1.2).
...
А во вторых в том же разделе 8.1.4:
…
Servers SHOULD always respond to at least one request per connection, if at all possible.
...
То есть повтор браузером идемпотентных запросов — это именно то, что рекомендует стандарт и именно это поведение для браузеров выглядит логичным. При этом использование 444 кода в nginx в некоторых случаях (конфиг из статьи, первый запрос мы делаем сразу на локейшен, прикрытый 444) может быть нарушением стандарта, поскольку в этом случае nginx закроет tcp-соединение до того, как даст первый ответ (а может и не быть нарушением стандарта, если браузер использует Keep-Alive соединение и недавно делал запрос в другим локейшенам).
Зы честно говоря раньше я думал что nginx «забывает» соединение (примерно как DROP в iptables), но оказывается что он не «забывает» соединение, а корректно закрывает его отправляя FIN браузеру, дожидается от него FIN, и подтверждает его(если задуматься, то это тоже логично, ведь tcp управляется ядром, а не самим nginx'ом). То есть использовать код 444 для борьбы с условным DDOS'ом не так выгодно, как использовать тот же DROP в iptables (при использовании DROP в iptables мы просто выкидываем входящий пакет и ничего не отправляем в ответ, в случае же 444 мы сначала вынуждены правильно установить tcp-соединение, а потом корректно его завершить). Но при этом анализировать http-трафик «конфигом» nginx намного удобнее, чем делать тоже самое «конфигом iptables» (или любого другого фаервола), поэтому вполне логично при DDOS'ах и прочей нехорошей активности ботов собирать их адреса с помощью 444, а потом выбирать эти адреса из лога nginx и банить их в том же iptables (через ipset если их много). Дамп трафика в формате wireshark можно посмотреть тут
Зыы все тот же rfc2616 не требует передачу реферала. Передача этого заголовка прямо запрещена при переходе с https ресурса на http, но нигде не сказано что в других случаях этот заголовок обязателен, а например в 15.1.2 написанно
…Но видимо разработчики браузеров считают что иногда можно и «просто так» удалить этот заголовок, видимо пытаясь решить какие-то возможные зацикливания на стороне неадекватно-ведущего (с точки зрения стандарта) себя сервера. Хотя нельзя и исключать того, что это баг, а не фича браузеров.
We suggest, though do not require, that a convenient toggle interface be provided for the user to enable or disable the sending of From and Referer information.
...
То есть повтор браузером идемпотентных запросов — это именно то, что рекомендует стандарт и именно это поведение для браузеров выглядит логичным.
…
нигде не сказано что в других случаях этот заголовок обязателен
Отнюдь. Запрос с отбрасыванием реферера никак нельзя считать идемпотентным. И в том же разделе 8.1.4 сказано:
Non-idempotent methods or sequences MUST NOT be automatically retried, although user agents MAY offer a human operator the choice of retrying the request(s).
Ну и там же об автоматизации перезапросов:
The automatic retry SHOULD NOT be repeated if the second sequence of requests fails.
То есть в сухой выжимке — браузер (самостоятельно) может выполнить:
— максимум два запроса;
— эти запросы должны быть абсолютно идентичны.
Отнюдь. Запрос с отбрасыванием реферера никак нельзя считать идемпотентным.«идемпотентность» это не возможность сказать «точно такой же» с умным видом. В контексте нашего разговора смысл слова «идемпотентность» отличается от изначального. Идемпотентность это свойство метода, а не всего запроса. Именно это сказано в 9.1.2 все того же rfc2616 и в 4.2.2 rfc7231, а в 8.1.3 rfc7231 даже есть таблица, и в ней методы DELETE, GET, HEAD, OPTIONS, PUT и TRACE обозначены как идемпотентные, а CONNECT и POST нет.
В вышеназванных документах пару раз встречается словосочетание «idempotent request», но в моем понимании это «запрос, использующий идемпотентный метод», а не «запрос неотличимый от другого», на это намекает хотя бы то, что речь идет о запросе в единственном числе.
Ну и там же об автоматизации перезапросов:Вы трактуете «SHOULD NOT» слишком строго, как будто там написано «MUST NOT». В rfc2119 (на который есть ссылка 1.2 rfc2616 и 1.1 rfc7231) сказано:
4. SHOULD NOT This phrase, or the phrase «NOT RECOMMENDED» mean that there may exist valid reasons in particular circumstances when the particular behavior is acceptable or even useful, but the full implications should be understood and the case carefully weighed before implementing any behavior described with this label.
Резюмируя
То есть в сухой выжимке — браузер (самостоятельно) может выполнить:
— максимум два запроса;
— эти запросы должны быть абсолютно идентичны.
— Да, максимум 2 запроса, но если сильно хочется, то ладно уж, делайте сколько хотите, стандарт позволяет.
— абсолютная идентичность запросов не требуется стандартом, требуется идемпотентность используемых в запросе методов.
«идемпотентность» это не возможность сказать «точно такой же» с умным видом. В контексте нашего разговора смысл слова «идемпотентность» отличается от изначального. Идемпотентность это свойство метода, а не всего запроса. Именно это сказано в 9.1.2 все того же rfc2616 и в 4.2.2 rfc7231, а в 8.1.3 rfc7231 даже есть таблица, и в ней методы DELETE, GET, HEAD, OPTIONS, PUT и TRACE обозначены как идемпотентные, а CONNECT и POST нет.
В вышеназванных документах пару раз встречается словосочетание «idempotent request», но в моем понимании это «запрос, использующий идемпотентный метод», а не «запрос неотличимый от другого», на это намекает хотя бы то, что речь идет о запросе в единственном числе.
В моём понимании в конексте вопроса идемпотентность определяется результатом. То есть, если в результате последовательного повтора запросов браузером мы получим один и тот же ответ от сервера, то можем их считать идемпотентными.
Считаете, что запросы «с» и «без» реферера гарантированно вернут одинаковые результаты?
Я нет.
Проведем мысленный эксперимент: пишем генератор случайных чисел на php, выполняем 2 совершенно идентичных GET запроса и получаем разные ответы. Запросы у нас идемпотентные (потому что используют идемпотентный метод GET), а ответы сервера у нас разные.
Второй мысленный эксперимент: отправляем методом POST на форум условный комментарий и получаем в ответ «Ваш комментарий опубликован, через 5 секунд вас автоматически переадресует на страницу обсуждения, если не переадресовало нажмите на ссылку». Потом повторяем этот запрос и получаем точно такой же ответ. В результате мы отправили 2 комментария, использовали не идемпотентный метод POST но при этом получили одинаковые ответы.
То есть идемпотентность в терминологии HTTP это не одинаковость запросов и не одинаковость ответов, это отсутствие намерения клиента своим действием изменить что-либо на стороне сервера (Логи, аналитика и т.д. не в счет, речь о объекте, указанном в URI запроса).
В реальном мире конечно возможно, что где-то вы найдете какую нить ссылку типа example.com/object/delete, при переходе по которой object удалиться, то есть случатся те самые «side effects», но это не сделает данный конкретный GET запрос не идемпотентным, это конкретный программист в конкретном месте нарушил стандарт и поэтому сам себе злобный буратино.
Так что я сейчас семь раз меряю перед тем как принять решение всех банить. Пока не решился
Если в конфиге Nginx написать вот так, то сразу всё станет работать как ожидается:
return 444 "No Response";
При
return 444;
ожидается, что сервер закроет соединение без отправки данных. А в варианте
return 444 "No Response";
как раз произойдет их отправка с неизвестным кодом ответа. Так что всё будет работать совсем не так как ожидается.
Почему (сегодня) return 444 не всегда полезен