Pull to refresh

Comments 75

Любопытненько, а вы ведь правы. Интересно, что разумнее отдать вместо 444? 403?
Ну при 403 хром вроде не переспрашивает…
Поведение такое давно, но вроде только на «сетевых» ошибках.
… с другой стороны, у меня в 444 отправляются только явные, 100% боты, а не браузеры. Потому надо внимательно подумать.
Если ожидается небраузерный ботнет, то лучше по-прежнему отдавать 444.
Для 50X Chrome в каких-то сборках также использовал перезапросы. Не исключено, что вернутся к этой практике. В версии 67.0 использует только для 444-го.
Вероятно, 403, в данный момент оптимальное решение для браузерных запросов.
Не только 444, я замечал что еще ERR_CONN_REFUSED и прочие ERR_* перезапрашивает.
Да, Chrome делает перезагрузки и в иных случаях, например, ERR_CONNECTION_RESET проявляется при использовании прокси, но эти ошибки не являются следствием на ответы web-сервера, поэтому со стороны Nginx(пока он не получил запроса) действия браузера меня не так волнуют.
Я отдаю код 204. В этом случае, вместе с хэдерами, размер ответа от Nginx получается где-то 100 байт. И до этого момента я не встречался с ситуацией, чтобы браузер делал несколько запросов на такой ответ.

location @204 {
    keepalive_timeout 0;
    return 204;
}

А почему бы не следовать протоколу и не отдавать 403?


Я вот совсем не удивлюсь если Safari при таких разрывах начнет автоматически iCloud Private Relay включать, для благо пользователя, потому что (с точки зрения браузера) явно ж проблемы с сетью.

Expected code 444
Это неправильный ответ?
return 200 "Expected code 444";

Это 200-й ответ, хотя ожидался 444-й

Ну, для браузера — поведение в рамках ожидаемого. Пользователь пытается зайти на сайт, мы не получили никакого ответа — очевидно, соединение потеряно, будем ломиться дальше, вдруг получится. Браузеру надо осмысленный отлуп давать.
С реферером удивительно, конечно — почему он его теряет.

Точно так. Странно ждать адекватной реакции от собеседника, которого вы в ответ на вопрос даже не потрудились уведомить, что не хотите с ним говорить. Он и интересуется — вдруг у вас в ушах бананы, или у него что-то не то с голосом...


А насчёт реферера — поведение конечно спорное, но люди делают точно так же — после невнятных сбоев при попытке кому-то позвонить пытаются набрать номер в другом формате, а то и позвонить с другого аппарата ))

Имеет ли смысл возвращать 200-й с поясняющим сообщением, если этот трафик мы считаем некашерным? Тогда браузеры не будут переспрашивать.
Мне видится проблема в том, что 4XX-й код — это ошибка клиента, а повторно задать запрос в таком случае — это адекватное поведение (в большинстве вариантов). По ходу, надо вводить код 'Отстань' с принципиально отличающимся номером.

Да, но мы будем тратить ресурсы на ответ, что в случае DDOS может быть не очень хорошо…

Тут или шашечки или ехать. И комфорт пользователя имхо все таки приоритетнее

По ходу, надо вводить код 'Отстань' с принципиально отличающимся номером

204 No Content?

Любые 4хх и 5хх означают, что рыба тут в принципе бывает, но в данный момент что-то пошло не так, и можно попробовать зайти попозже. Поэтому чтобы клиент гарантированно успокоился, надо отвечать что-то из 2хх.
> Любые 4хх и 5хх означают, что рыба тут в принципе бывает

Ну уж нет. 403 говорит, что рыба тут не про твою честь, 401, что рыба тут для тех у кого ксива есть, 418 говорит, что вы попросили кофе у чайника, 451, что рыба тут для жителей других стран, а жителям твоей рыбу есть запретили власти.
Это вот так сходу.
204 = спасибо, ваш звонок очень важен для нашей рыбы. мы перезвоним.
По-моему, 404 говорит именно про то, что рыбы тут нет (и не предполагается).
Нет и не предполагается — это 410.
410 — раньше тут был документ, но больше его нет и никогда не будет.

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 обрабатывает ответ? Возможно, что он ведет себя не «очень хорошо» с точки зрения браузера. Эту версию можно проверить, взяв любой другой веб сервер и ответить 444.

Глупость сморозил, нет такого кода, проверять нечего.
444-е это уникальный код ответа Nginx, в других веб-серверах нет 444-кода.
И на деле он ничего не отвечает — он просто закрывает соединение.

А 444 код есть в стандартах? Судя по вашему комменту — нет. Тогда это самодеятельность nginx, и нет ни одного аргумента, почему клиент должен поступать иначе

Нет никакого 444, это просто волшебное значение, сообщающее nginx, что ничего (даже http код) клиенту возвращать не нужно, просто разорвать соединение.

UFO just landed and posted this here
А почему заминусован? Что не так? (Я честно спрашиваю, я сварщик не настоящий)
Хороший пример: кто-то ломиться в phpMyAdmin на Вашем сайте. А у вас сайт написан на Java. И там уж точно нет phpMyAdmin. Надо отдавать 444.

А кто сказал, что туда придут только небраузерные боты?
И раз считаем, что боты пришли браузерные, то чем ситуация с 444-м ответом отличается от описанной в статье?
UFO just landed and posted this here
Неужели мир сошел с ума и теперь используются браузерные боты, чтобы проверить уязвимости 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.
UFO just landed and posted this here
Я требую что бы вы уважали права ботом и не фильтровали доступ к вашим сайтам из-за того, что боту удобнее посещать curl'ом!
image
Такое поведение наблюдается уже достаточно давно, но когда именно Chrome и Firefox перешли на данный режим работы сказать не могу

Мне кажется, даже IE 3.0 перезапрашивал неотвечающую страницу по несколько раз.
А вот еще проблема с которой я лично столкнулся. С внедрением ssl и http2 код 444 теперь стал очень неоднозначным. Вместо разрыва соединения браузеры определяют 444 как ERR_SPDY_PROTOCOL_ERROR, потому как для чтения запроса от клиента мы сначала переходим на защищенное соединение (обмениваемся ключами/сертификатами) а потом внезапно обрываем связь.
Если блокировка требуется в контексте сервера, то по идее должно помочь «ssl_ciphers aNULL;» в нужном блоке server{}
Во вложениях уровня ниже «server» уже не удастся.
Не проверяли как реагируют браузеры если им ответить 418?
В принципе ответ ничем не хуже несуществующего в стандарте 444.
Вы невнимательно читали пост. return 444 говорит nginx не «возврати клиенту HTTP CODE 444». Он говорит ему немедленно закрыть соединение, не посылая никакого ответа вообще.
Вы не понимаете мой совет. Я рекомендую попробовать для теста отдать браузеру 418.
Тогда можно и обычный 403 возвращать. 418 — это чисто поприкалываться.
418 не «поприкалываться», а официальный код ответа предусмотренный RFC 2324. То что RFC выпущен 1 апреля не означает, что он «поприкалываться». Официальный код в спецификации. Точка.
Ну как бы…

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 от "ошибки сети" (с точки зрения браузера) невозможно

Напишите об этом статью. А, погодите, вот же она. ;)
Если бы у мобильного приложения хабры UI вменяемый был бы, то я бы еще и комментарием в правильную ветку наверное попал бы :-)

А статья у нас не об особенностях работы 444, а о том, что для автора оказалось неожиданным поведение браузеров. Я бы переформулировал вывод в примерно такое: не используйте 444 в nginx для ответа реальным пользователям, даже если вам нечего им ответить. Пользователю лучше отдать 403, 404 или другой подходящий по смыслу код. 444 придумали для борьбы с «плохими» ботами и применять его нужно только после взвешивания всех «за» и «против», и желательно как временную меру (то есть например собирать 444 из лога и банить этих ботов в фаерволе или что-то типа того).
Отличный вывод, во введении подобное описано. Все же в посте что примечательно, так это потеря контекста запроса браузерами (минус реферер).
Я так и не понял, зачем вообще, что то возвращать вместо просто молчать.
И nginx не забыть удалить.

444 — это и есть "просто молчать" вообще-то!

Ладно, зачем вообще закрывать соединение, вместо того чтобы просто молчать, браузер или будет ждать таймаут 30 сек, или сразу поймет что там нафиг шлют, разница есть.

Затем что спросят по новой, и не один раз.

Так и не понял — почему не отправлять 444ю… Она ведь призвана спасать нас от DDoS и прочих совсем уж неадекватных юзеров, а если в процессе нормальной работы с сайтом часть юзеров наткрётся на ссылку, приводящую к 444й и подоблится 3-20 раз — сайт от этого не ляжет.
Вне связи с конкретным кодом 444 — похоже, Гугл таки добьет HTTP… Пытаемся реализовать некую систему, имеющую полноэкранный Chromium в качестве фронтенда — ловим кучу глюков с каждым новым сервисом. Посмотрел код CEF и JxBrowser — они там половину Chromium переписывают чтобы его поведение сделать вменяемым.
Так ведь 444 код ответа не стандартизирован. Поэтому и не стоит полагаться на те действия, которые не описаны, по сути. Разве я не прав?
Так 444 браузеру и не идёт. Его видит nginx и молча (без любых данных, даже без HTTP кода) рвёт соединение.

Другое дело, что браузеру не нравится, что с ним так (может пакет где потерялся, кабель выдернулся, ещё что), вот он и переспрашивает.
Дело не в том, что браузерам это не нравится, они, как раз, ведут себя корректно в этой ситуации. Дело в отсутствии спецификации для этого кода и как ведет себя nginx. В статье упор сделан именно на неочевидное для автора поведение браузеров.
Я к тому, что есть некие правила, которые регламентируют взаимодействие и поведение, и, если этих правил нет, то о чем здесь можно говорить?
Скажем, есть ГОСТ для дорожной разметки на автомобильных дорогах. Насколько помню, для краски этой разметки видимость должна составлять не менее 50%. И что делать в этой ситуации водителям, которые еле видят остатки белых линий? Кто-то может всё-таки придерживаться её, а я, допустим, отступаю от требований разметки, ведь она не отвечает требованиям, а может, уже и безопасности. Абсолютно такая же ситуация с 444 кодом. Его попросту нет. Nginx рвет соединение, так зачем тогда озадачиваться разным поведениям браузеров? Мне именно это не понятно.
Нет соглашения не про код ответа (как я уже сказал, он и не доходит к браузеру), а про действия при соединении без данных. Автор поста и многие другие ожидают, что браузер сочтёт этот ответ валидным и не будет пытаться дальше. Разработчики хрома и лисы думают иначе: соединение без контента считают ошибкой сети и переспрашивают. В этом соглашения и нет, только ожидания (которые, как видно в посте, не оправдались).

P.S.: а вообще нужен схожий код ответа (именно ответа, не молчаливого закрытия соединения), при котором только код (без тела и других заголовков) отсылается браузеру. Типа `419 Connection Filtred`
Пустой набор заголовков всего на 2 байта больше чем их отсутствие.
Ты меня не слышишь и продолжаешь минусить. Речь в статье как раз про 444 и поведение браузеров, а я комментирую статью. Разработчики браузеров сами решают, как вести себя в той или иной ситуации, если это ничем не регламентировано. Кода 444 нет, а на молчание каждый реагирует по-своему.

Чисто для справки: я не минусю (и не минусил). Без понятия, кто это делает.


Я пояснил лишь, что ты только что написал:


Кода 444 нет, а на молчание каждый реагирует по-своему.

Теперь всё верно. Именно это я и написал.

Я тут полистал rfc2616 (HTTP/1.1) и на мой скромный взгляд действие браузера выглядит вполне логично.

Во первых в разделе 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 запрос не идемпотентным, это конкретный программист в конкретном месте нарушил стандарт и поэтому сам себе злобный буратино.
В комментариях несколько раз подразумевалось что на 444 будут напарываться боты-сканеры. Как правило оно так, но к нам приходят реальные пользователи сайта, который раньше сидел на нашем IP-адресе. Его хозяева зачем-то продлевают домен, и он по-прежнему указывает на наш сервер, и к нам по старым ссылкам приходят люди.

Так что я сейчас семь раз меряю перед тем как принять решение всех банить. Пока не решился

А банить-то зачем? Надо 404 отдать и всё. Или 410 если вы злой. Ну или 301 если хитрый сеошник...

Если в конфиге Nginx написать вот так, то сразу всё станет работать как ожидается:

return 444 "No Response";

При

return 444;

ожидается, что сервер закроет соединение без отправки данных. А в варианте

return 444 "No Response";

как раз произойдет их отправка с неизвестным кодом ответа. Так что всё будет работать совсем не так как ожидается.

Зависит от ваших ожиданий. В статье вы ожидаете, что браузер придёт 1 раз - именно это и случится.

При этом я не хочу ломать задуманный функционал 444-го "ответа", получая сложнопрогнозируемое поведение клиента встретившегося с нестандартным кодом ошибки.

Sign up to leave a comment.

Articles