Вот какое дело: как показывают мои изыскания, большинство веб-разработчиков поразительно мало знают об 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)


  1. tempick
    30.01.2023 12:24
    +13

    Если про Retry-After я узнал далеко не сразу, то Authorization встречается довольно часто, и странно слышать, что про него может не знать миддл-бэкенд разраб. Даже если вы сами не пишете апи, то тот же Bearer-токен встречается относительно часто в сторонних апишках, который как раз в этом заголовке и передается

    UPD: да Basic тоже встречается не то чтобы редко


    1. Heggi
      30.01.2023 14:29
      -1

      И тем не менее, согласен с автором статьи, что многие API (в том числе и в банках), используют какие-то свои заголовки для передачи авторизационных параметров.


      1. vvzvlad
        30.01.2023 22:56

        И в чем же это так ужасно?


        1. Heggi
          31.01.2023 03:18
          +1

          Да, в целом, не сказал бы что это прям ужасно, но все-таки хочется видеть какое-то единообразие.
          Плюс в некоторых языках (библиотеках) есть готовые хелперы для работы с заголовком Authorization (и не только), что упрощает код и минимизирует шанс ошибиться как в формате, так и в названии самого заголовка. Когда же работаем с кастомным заголовком, то ничего подобного, естественно, нет.


        1. Vest
          01.02.2023 00:15

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


  1. RikoSaGe
    30.01.2023 13:15
    +26

    Статья написана исключительно с целью прорекламировать свою контору. Ценность если и несёт, то для совсем зелёного джуна. Засилье корпоративных аккаунтов на Хабре в последние годы делает совершенно невозможным его чтение, всем нужно рекламировать и продавать какую-нибудь дрянь.


    1. sim2q
      30.01.2023 15:15
      +1

      Спасибо, не обратил внимание.
      С какого-то времени стараюсь не читать копроблоги даже если интересный заголовок, потому как там какой-то прямо специальный шаблон - налить воды до середины, потом крупицу заинтересовать и мгновенно после этого - всё...


  1. baldr
    30.01.2023 13:27
    +7

    А ещё, видимо, мало кто знает что заголовки могут дублироваться по имени. Часто вижу как заголовки собирают в dict..

    И иметь два заголовка Authorization - это нормально. Например, Basic может на nginx отсекать ботов, а в приложении уже логика проверяет пользователя по базе.


    1. Sigest
      31.01.2023 10:49

      Коммент оказался для меня полезнее статьи. Не знал о таком трюке для отсекания ботов. Идея классная


  1. Hekikai
    30.01.2023 13:32
    +2

    IMO, веб-разработчики не знают про заголовки HTTP потому, что в принципе не понимают, что такое HTTP. Если изучить не отправление токенов в заголовках для решения тестовых заданий, а саму суть HTTP-протокола, зачем он нужен и с чем его едят, то большинство вопросов исчезнут (= эти вопросы будут выглядеть как гугл необходимых хедеров в твой запрос).


  1. p07a1330
    30.01.2023 18:43
    +8

    Т.е. вся статья рассказывает о 2х заголовках и помещаеться на лист А4?
    Мощно однако..


  1. pda0
    30.01.2023 19:50
    -1

    Было бы неплохо услышать про полезность Cache-Control: public в современном вебе, где всё за tls...


    1. vvzvlad
      30.01.2023 22:56
      +1

      До https может быть инфраструктура балансировщиков.


  1. fransua
    30.01.2023 21:03
    -1

    Только вот хедеры не работают с web-socket и приходится этот токен добавлять в параметры


    1. AstarothAst
      31.01.2023 13:35

      У вебсокетов тоже есть хидеры.


  1. rutexd
    31.01.2023 01:52

    О чем статья, если не секрет? Откровенно ожидал узнать для себя что то новое. Не вышло.

    Заголовки авторизации используются сейчас очень часто. Кеш - аналогично гуглится первой ссылкой на какой нибудь so.

    Единственное что хорошо - accept. С другой стороны - а как часто он на самом деле потребовался? При работе с некоторым апи мы точно знаем что нам для него надо и можем легко подстроиться под него заранее. Любой адекватный апи давно работает с джсоном и только с ним. Да даже всякие ардруины, которым заголовок может реально понадобится, например для какого нибудь XML(предположим) - умеют в json и с огромным успехом чудес перезагрузок операторов умеют в большое удобство при работе с json.


  1. ftc
    31.01.2023 09:05

    Про Retry-After давно не слышал. Хотя в университете про HTTP и его заголовки очень даже рассказывали (и даже просили написать HTTP-сервер).

    А вот про Authorization не со всем соглашусь - бывает, что нужно всю информацию, требуемую для запроса, положить в URL. Например - токены доступа по ссылке (когда нужно что-то расшарить людям, которым известна ссылка, по типу документов в Google Drive). Потому в каждом случае надо отдельно разбираться.

    А ещё, раз уж про авторизацию заговорили, можно вспомнить Basic HTTP Auth, для которого в частности имя пользователя и пароль можно задавать прямо в URL: <scheme>://<user>:<password>@<host>/<path>. И это даже вполне себе работает.


  1. alcanoid
    31.01.2023 09:15

    (там, где я учился, определённо, так и было)

    Учился, ага. Автор оригинала — женщина.


    1. VADemon
      01.02.2023 09:07

      Зря минусуете человека, я автору на день раньше отписался и по этому поводу тоже -- ноль реакции.