Вот какое дело: как показывают мои изыскания, большинство веб-разработчиков поразительно мало знают об HTTP-заголовках или, в целом, о стандарте HTTP. Я имею в виду, что я понимаю то, что во многих университетах и школах, вероятно, этому не учат (там, где я учился, определённо, так и было). А кто будет, сидя дома праздным воскресным утром, думать о том, чтобы развлечь себя чтением стандарта HTTP? Да никто. Знаю, но… есть одно важное обстоятельство: отсутствие хоть какого-то понимания стандарта HTTP — это зияющая дыра в знаниях веб-разработчиков, в знаниях многих из тех, кто это читает. Предлагаю это исправить.
Что представляют собой HTTP-заголовки
HTTP-заголовки предназначены для организации обмена метаданными о запросах и ответах между клиентами и серверами.
Клиент и сервер передают друг другу в заголовках дополнительную информацию. Заголовки имеются и в запросах, и в ответах.
Клиент (браузер, команда curl
и так далее) может отправить серверу заголовок Accept: text/html
для того чтобы указать на то, что ему хотелось бы, чтобы запрос оформлялся бы в виде HTML-документа. Или заголовок может быть таким: Accept: /
. Тем самым клиент сообщит серверу о том, что сервер может отправить ему всё что угодно, так как клиент способен разобраться с любыми данными, которые получит от сервера.
Сервер может отправить клиенту ответ с заголовком Cache-Control: public, max-age=600
. Фрагмент public
этого заголовка указывает на то, что возвращённый документ может быть кеширован в любом кеше. Ответ сервера не предназначен лишь для одного конкретного пользователя. А фрагмент max-age=600
говорит о том, что через 600 секунд материал необходимо из кеша удалить.
В результате мы можем сказать, что HTTP-заголовки состоят из двух частей, разделённых двоеточием. Это — имя заголовка, за которым следует значение. Имена заголовков нечувствительны к регистру.
Изучение существующих заголовков
Лучший способ изучения HTTP-заголовков, у которых уже имеются определённые имена, которым уже назначены роли в клиент-серверном взаимодействии, заключается в посещении соответствующего раздела сайта MDN.
Дело в том, что сервер и клиент могут договориться об использовании любого количества HTTP-заголовков, имеющих произвольные имена и значения. Но я полагаю, что нет смысла в изобретении новых HTTP-заголовков для своего приложения до проверки официального списка существующих заголовков. Высока вероятность того, что кому-то уже был нужен заголовок для решения точно такой же задачи, которую нужно решить «изобретателю».
Главная причина, по которой нежелательно «изобретать колесо» в сфере HTTP-заголовков, используемых неким приложением, заключается в том, чтобы не усложнять жизнь пользователям этого приложения. Есть и ещё одна причина — к существующим HTTP-заголовкам уже имеется документация.
Я сейчас работаю над приложением, которому нужно обращаться к множеству различных API. И я могу сказать, что я возблагодарил бы судьбу, если бы все разработчики всех приложений могли бы в один прекрасный день начать пользоваться одними и теми же заголовками. Многие из этих API придерживаются стандартов. На самом деле — большая их часть. Их, определённо, больше 50%. Но, по моим скромным оценкам, около 30% API стандартов не придерживается. Один из трёх API, который я интегрировал в свой проект, вынуждает меня использовать заголовки, созданные специально для него его разработчиками, а документация подобных API никогда в полной мере не объясняет особенности поведения таких заголовков.
Необоснованно сложным оказывается написание кода обработки заголовков для API, когда для работы с разными API требуется отправлять одну и ту же информацию, оформленную множеством различных способов. Особенно — если все эти «различные способы» как следует не документированы. А дело тут в том, что, будем откровенны, документацию мы писать не умеем. Поэтому предлагаю сделать жизнь пользователей наших систем чуть лучше и просто использовать заголовки, которые уже хорошо документированы.
Первый HTTP-заголовок, о котором знает слишком мало разработчиков: Authorization
Не знаю, как ещё это выразить, но… заголовок Authorization
предназначен для авторизации. Знаю — это шокирующая правда. А если серьёзно, то, если вы ожидаете запросов к серверу, в которых клиент должен отправлять какую-то аутентификационную информацию, вроде ключей API, то, пожалуйста, используйте этот заголовок. Давайте просто все решим, что с этого момента мы будем помещать данные для авторизации во всех API в заголовок Authorization
. Это позволит нам сделать нашу жизнь намного проще.
Давайте не будем изобретать собственные заголовки вроде PRIVATE-TOKEN
, или DD-API-KEY
, или X-SF-Token
и не будем запихивать токены авторизации в параметры GET
или POST
. Давайте просто остановимся на заголовке Authorization
. Пользоваться им очень просто. Данные в нём передаются так:
Authorization: <type> <authorisation-parameters>
Так как существует множество способов аутентификации на сервере, заголовок Authorization
поддерживает несколько стандартных типов аутентификации. Именно по этой причине тут имеется фрагмент <type>
. Самыми распространёнными типами аутентификации являются Basic
и Bearer
. Последний используется для OAuth2
.
Фрагмент authorisation-parameters
— это конкретные данные, имеющие отношение к авторизации (имя пользователя и пароль, токены и так далее), закодированные в соответствии с правилами выбранного типа авторизации.
Второй HTTP-заголовок, о котором знает слишком мало разработчиков: Retry-After
Когда вы реализуете в своём API систему ограничения частоты запросов, используйте этот заголовок для того чтобы сообщить вызывающей стороне о том, сколько времени она должна ждать прежде чем делать новый запрос к вашему API.
Вообще — заголовок Retry-After
был спроектирован для использования в следующих ответах:
503
—Service Unavailable
.429
—Too Many Requests
. Этот код ответа используется для ограничения частоты запросов.Очень редко им пользуются в ответе с кодом
301
—Moved Permanently
.
В этом заголовке сервер должен вернуть либо целое число, представляющее количество секунд, которое должно пройти перед повторным выполнением запроса, либо значение, содержащее дату и время, после наступления которых можно выполнить повторный запрос.
Retry-After: <http-date>
Retry-After: <delay-seconds>
Всё это значит, что для решения подобных задач не нужно изобретать собственные заголовки, которые могут называться X-Rate-Limit-Remaining-Seconds
или X-RateLimit-Reset
. Ведь тот, кому это нужно, всегда может воспользоваться заголовком Retry-After
.
Итоги
Итак, теперь вы кое-что знаете об HTTP-заголовках, и о том, как с ними разумнее всего обращаться. А теперь расскажите об этом другим. И, пожалуйста, пользуйтесь описанными тут двумя заголовками для решения задач авторизации и ограничения частоты запросов к серверу, вместо того, чтобы изобретать что-то своё.
О, а приходите к нам работать? ???? ????
Мы в wunderfund.io занимаемся высокочастотной алготорговлей с 2014 года. Высокочастотная торговля — это непрерывное соревнование лучших программистов и математиков всего мира. Присоединившись к нам, вы станете частью этой увлекательной схватки.
Мы предлагаем интересные и сложные задачи по анализу данных и low latency разработке для увлеченных исследователей и программистов. Гибкий график и никакой бюрократии, решения быстро принимаются и воплощаются в жизнь.
Сейчас мы ищем плюсовиков, питонистов, дата-инженеров и мл-рисерчеров.
Комментарии (19)
RikoSaGe
30.01.2023 13:15+26Статья написана исключительно с целью прорекламировать свою контору. Ценность если и несёт, то для совсем зелёного джуна. Засилье корпоративных аккаунтов на Хабре в последние годы делает совершенно невозможным его чтение, всем нужно рекламировать и продавать какую-нибудь дрянь.
sim2q
30.01.2023 15:15+1Спасибо, не обратил внимание.
С какого-то времени стараюсь не читать копроблоги даже если интересный заголовок, потому как там какой-то прямо специальный шаблон - налить воды до середины, потом крупицу заинтересовать и мгновенно после этого - всё...
baldr
30.01.2023 13:27+7А ещё, видимо, мало кто знает что заголовки могут дублироваться по имени. Часто вижу как заголовки собирают в dict..
И иметь два заголовка Authorization - это нормально. Например, Basic может на nginx отсекать ботов, а в приложении уже логика проверяет пользователя по базе.
Sigest
31.01.2023 10:49Коммент оказался для меня полезнее статьи. Не знал о таком трюке для отсекания ботов. Идея классная
Hekikai
30.01.2023 13:32+2IMO, веб-разработчики не знают про заголовки HTTP потому, что в принципе не понимают, что такое HTTP. Если изучить не отправление токенов в заголовках для решения тестовых заданий, а саму суть HTTP-протокола, зачем он нужен и с чем его едят, то большинство вопросов исчезнут (= эти вопросы будут выглядеть как гугл необходимых хедеров в твой запрос).
p07a1330
30.01.2023 18:43+8Т.е. вся статья рассказывает о 2х заголовках и помещаеться на лист А4?
Мощно однако..
fransua
30.01.2023 21:03-1Только вот хедеры не работают с web-socket и приходится этот токен добавлять в параметры
rutexd
31.01.2023 01:52О чем статья, если не секрет? Откровенно ожидал узнать для себя что то новое. Не вышло.
Заголовки авторизации используются сейчас очень часто. Кеш - аналогично гуглится первой ссылкой на какой нибудь so.
Единственное что хорошо - accept. С другой стороны - а как часто он на самом деле потребовался? При работе с некоторым апи мы точно знаем что нам для него надо и можем легко подстроиться под него заранее. Любой адекватный апи давно работает с джсоном и только с ним. Да даже всякие ардруины, которым заголовок может реально понадобится, например для какого нибудь XML(предположим) - умеют в json и с огромным успехом чудес перезагрузок операторов умеют в большое удобство при работе с json.
ftc
31.01.2023 09:05Про Retry-After давно не слышал. Хотя в университете про HTTP и его заголовки очень даже рассказывали (и даже просили написать HTTP-сервер).
А вот про Authorization не со всем соглашусь - бывает, что нужно всю информацию, требуемую для запроса, положить в URL. Например - токены доступа по ссылке (когда нужно что-то расшарить людям, которым известна ссылка, по типу документов в Google Drive). Потому в каждом случае надо отдельно разбираться.
А ещё, раз уж про авторизацию заговорили, можно вспомнить Basic HTTP Auth, для которого в частности имя пользователя и пароль можно задавать прямо в URL: <scheme>://<user>:<password>@<host>/<path>. И это даже вполне себе работает.
tempick
Если про Retry-After я узнал далеко не сразу, то Authorization встречается довольно часто, и странно слышать, что про него может не знать миддл-бэкенд разраб. Даже если вы сами не пишете апи, то тот же Bearer-токен встречается относительно часто в сторонних апишках, который как раз в этом заголовке и передается
UPD: да Basic тоже встречается не то чтобы редко
Heggi
И тем не менее, согласен с автором статьи, что многие API (в том числе и в банках), используют какие-то свои заголовки для передачи авторизационных параметров.
vvzvlad
И в чем же это так ужасно?
Heggi
Да, в целом, не сказал бы что это прям ужасно, но все-таки хочется видеть какое-то единообразие.
Плюс в некоторых языках (библиотеках) есть готовые хелперы для работы с заголовком Authorization (и не только), что упрощает код и минимизирует шанс ошибиться как в формате, так и в названии самого заголовка. Когда же работаем с кастомным заголовком, то ничего подобного, естественно, нет.
Vest
Ужасно это может быть только, если вы сохраняете трафик каким-нибудь HttpWatch для коллеги, и он увидит ваш пароль вместо звёздочек. Просто потому, что ваш инструмент не знал, какой заголовок оказался секретным.