Меню Закрыть

Content length что это

Содержание

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

Как известно, в HTTP-протоколе есть три способа сообщить клиенту о том, что передача завершена. Первый, самый старый, — разорвать соединение после того, как весь контент передан. Он же самый медленный, так как при этом не работает механизм Keep-Alive, и для следующего запроса клиент должен установить новое соединение. Второй — это передача в заголовке Content-Length длины (в байтах) передаваемого контента. И наконец, третий — передача контента блоками, перед каждым из которых передается его длина в шестнадцатеричном виде, а завершение передачи обозначается передачей блока с нулевой длиной (так наказываемый chunked transfer, возможно, о нем я напишу со временем отдельную запись).

По каким-то причинам я считал, что в случае использования PHP используется chunked transfer, причем длину chunks определяет и выдает либо сам Apache, либо модуль PHP. Увы, это оказалось не так, и на моем сайте использовался самый медленный первый способ — закрытие соединения (причем не сразу, а после какого-то тайматуа, что и вызывало задержки).

Возник вопрос, как выводить Content-Length. На первый взгляд, все просто: собрать весь выводимый HTML-код в строку-буфер, посчитать ее длину и выдать. Но во-первых, в некоторых скриптах выдача делалась сразу, а не через буфер, а во-вторых, если включено сжатие GZip, реальная длина отдаваемого контента меньше переданной в Content-Length, и клиент ждет оставшейся части до тех пор, пока не истечет таймаут соединения. (Особенно плохо к этому относятся поисковики, от такого сайт может даже выпасть из выдачи.)

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

В последнее время я все более часто наблюдаю в основном форуме РНРClub вопросы на тему создания POST и GET запросов, а так же вопросы на тему: "Как мне посредством функции header сформировать POST запрос". Я считаю, что уже давно назрела необходимость расставить точки над "и" в использовании данной технологии, поскольку начинающие программисты просто не понимают принципов работы веба, как такового. Итак, начнем наше путешествие по миру протокола HTTP.

1. Протокол HTTP. Введение

Сразу хочу уточнить одну маленькую вещь. Страшное слово протокол есть не что иное, как соглашение множества людей, просто в один прекрасный момент люди решили: "Давайте будем делать так, и тогда все будет в порядке". Бояться нечего, все просто до безобразия и это безобразие мы сейчас будем вскрывать. Итак, что же это такое протокол HTTP и с чем его едят?

1.1 Клиент и сервер

Чудес в мире, а тем более в мире программизма и интернета не бывает! Усвойте это как незыблемую истину. И, если программа не работает или работает не так как хочется, то, значит, скорее всего, она либо написана не правильно, либо содержит ошибки. Итак, как же все-таки браузер просит сервер прислать ему хоть что-нибудь? Да очень просто! Надо только немного расслабиться и начать получать удовольствие от процесса 🙂

1.2. Пишем наш первый HTTP запрос

Если Вы думаете, что все слишком сложно, то Вы ошибаетесь. Человек так устроен, что просто не способен создавать что-то сложное, иначе он сам в этом запутается 🙂 Итак, есть браузер и есть Web-сервер. Инициатором обмена данными всегда выступает браузер. Web-сервер никому, никогда просто так ничего не пошлет, чтобы он что-нибудь отправил браузеру — надо, чтобы браузер об этом попросил. Простейший HTTP запрос моет выглядеть, например, так:

GET http://www.php.net/ HTTP/1.0

  • GET (В переводе с английского означает "получить") — тип запроса, тип запроса может быть разным, например POST, HEAD, PUT, DELETE (часть из них мы рассмотрим ниже).
  • http://www.php.net/ — URI (адрес) от которого мы хотим получить хоть какую-нибудь информацию (естественно мы надеемся подучить HTML страницу).
  • HTTP/1.0 — тип и версия протокола, который мы будем использовать в процессе общения с сервером.
  • — конец строки, который необходимо повторить два раза, зачем, станет понятно немного позднее.

Вы можете выполнить данный запрос очень просто. Запустите программу telnet.exe, введите в качестве хоста www.php.net, укажите порт 80, и просто наберите данный запрос, нажав два раза Enter в качестве

. В ответ вы получите HTML код главной страницы сайта www.php.net.

1.3 Структура запроса

Рассмотрим, из чего состоит HTTP запрос. Все достаточно просто. Начнем с того, что HTTP запрос — это вполне осмысленный текст. Из чего же он состоит в общем случае? Будем рассматривать протокол HTTP 1.0. Итак:

Request-Line [ General-Header | Request-Header | Entity-Header ]
[ Entity-Body ]

    Request-Line — строка запроса

Формат: "Method Request-URI HTTP-Version
"

  • Method — метод, которым будет обрабатываться ресурс Request-URI, может быть GET, POST, PUT, DELETE или HEAD.
  • Request-URI — относительная или абсолютная ссылка на страницу с набором параметров, например, /index.html или http://www.myhost.ru/index.html или /index.html?a=1&b=qq. В последнем случае серверу будет передан запрос с набором переменных a и b с соответствующими значениями, а знак "&" — амперсант служит разделителем между параметрами.
  • HTTP-Version — версия HTTP протокола, в нашем случае "HTTP/1.0".
  • Нам крайне интересны методы обработки GET и POST. Методом GET можно просто передать параметры в скрипт, а методом POST можно эмулировать submit формы.

    Для метода GET, Request-URI может выглядеть, например, так: "/index.html?param1=1&param2=2".

      General-Header — главная часть заголовка.
      Формат: [Date: value
      | Pragma: no-cache
      ]
      Может иметь только два параметра: Date или Pragma. Date — дата по Гринвичу в формате "День недели, Число Месяц Год ЧЧ:ММ:СС GMT", например, "Tue, 15 Nov 1994 08:12:31 GMT" — дата создания запроса. Pragma может иметь одно значение no-cache, которое запрещает кэширование страницы.

    Request-Header — часть заголовка, описывающая запрос.

    Request-Header может иметь следующие параметры: Allow, Authorization, From, If-Modified-Since, Referer, User-Agent.
    В данной главе мы не будем рассматривать параметр Autorization, так как он используется для доступа к закрытым ресурсам, что требуется не так уж часто. Вы можете самостоятельно изучить формирование заголовка для авторизованного доступа на сайте www.w3c.org.

    Allow — задает допустимые методы обработки.
    Формат: "Allow: GET | HEAD
    ".
    Параметр игнорируется при указании метода обработки POST в Request-Line. Задает допустимые методы обработки запроса. Прокси сервера не модифицируют параметр Allow и он в неизменном виде доходит до сервера.

    Читайте также:  Установить adobe flash плеер бесплатно

    From — e-mail адрес, пославшего запрос.
    Формат: "From: adderss
    ".
    Например, "From: [email protected]
    ".

    If-Modified-Since — указывает, что запрос не модифицировался с такого-то времени.
    Формат: "If-Modified-Since: date
    "
    Используется только для метода обработки GET. Дата указывается по Гринвичу в таком же формате, как и для параметра Date в General-Header.

  • Referrer — абсолютная ссылка на страницу, с которой произошла инициация запроса, т. е. ссылка на страницу, с которой пользователь перешел на нашу.
    Формат: "Referrer: url
    ".
    Пример: "Referrer: www.host.ru/index.html
    ".
  • User-Agent — тип браузера.
    Например: "User-Agent: Mozilla/4.0
    "
  • Entity-Header — часть заголовка, описывающая данные Entity-Body.
    В данной части запроса задаются параметры, которые описывают тело страницы. Entity-Header может содержать следующие параметры: Allow, Content-Encoding, Content-Length, Content-Type, Expires, Last-Modified, extension-header.

    Allow — параметр аналогичный Allow из General-Header.

    Content-Encoding — тип кодирования данных Entity-Body.
    Формат: "Сontent-Encoding: x-gzip | x-compress | другой тип
    ".
    Пример: "Сontent-Encoding: x-gzip
    ". Символ "|" означает слово "или", то есть то или то или то и.т.д.
    Другой тип может указывать на способ кодирования данных, например, для метода POST: "Сontent-Encoding: application/x-www-form-urlencoded
    ".

    Content-Length — количество байт, пересылаемых в Entity-Body. Значение Content-Length имеет совсем другой смысл для данных, пересылаемых в формате MIME, где он выступает как параметр описания части данных — "external/entity-body". Допустимыми являются целые числа от нуля и больше.
    Пример: "Content-Length: 26457
    ".

    Content-Type — тип передаваемых данных.
    Например: "Content-Type: text/html
    ".

    Expires — Время, когда страница должна быть удалена из кэша браузера.
    Формат: "Expires: date
    ". Формат даты алогичен формату даты для параметра Date из General-Header.

    Last-Modified — время последнего изменения пересылаемых данных.
    Формат: "Last-Modified: date
    ". Формат даты алогичен формату даты для параметра Date из General-Header.

  • Extention-header — часть заголовка, которая может предназначаться, например, для обработки браузером, или другой программой, которая принимает документ. В данной части можно описывать свои параметры в формате "ParameterName: parametervalue
    ". Данные параметры будут игнорироваться, если программа-клиент не знает, как их обработать.
    Например: "Cookie: r=1
    " — устанавливает всем известные печеньки для страницы.
  • А теперь после таких страшных слов давайте попробуем немного успокоиться и понять, что же нам надо? Понимать мы естественно будем на примерах.

    Давайте представим, что нам надо получить страницу с сайта, передав Cookies (Печеньки), иначе нас просто пошлют как незванных гостей, и более того, известно, что на данную страницу пускают только после того, как Вы побывали на главной странице сайта.

    2 Метод GET

    Напишем наш запрос.

    GET http://www.site.ru/news.html HTTP/1.0

    Host: www.site.ru

    Referer: http://www.site.ru/index.html

    Cookie: income=1

    Данный запрос говорит нам о том, что мы хотим получить содержимое страницы по адресу http://www.site.ru/news.html, использую метод GET. Поле Host говорит о том, что данная страница находится на сервере www.site.ru, поле Referer говорит о том, что за новостями мы пришли с главной страницы сайта, а поле Cookie говорит о том, что нам была присвоена такая-то кука. Почему так важны поля Host, Referer и Сookie? Потому что нормальные программисты при создании динамических сайтов проверяют данные поля, которые появляются в скриптах (РНР в том числе) в виде переменных. Для чего это надо? Для того, например, чтобы сайт не грабили, т.е. не натравливали на него программу для автоматического скачивания, или для того, чтобы зашедший на сайт человек всегда попадал бы на него только с главной страницы и.т.д.

    Теперь давайте представим, что нам надо заполнить поля формы на странице и отправить запрос из формы, пусть в данной форме будет два поля: login и password (логин и пароль),- и, мы естественно знаем логин и пароль.

    GET http://www.site.ru/news.html?login=Petya%20Vasechkin&password=qq HTTP/1.0

    Host: www.site.ru

    Referer: http://www.site.ru/index.html

    Cookie: income=1

    Логин у нас "Petya Vasechkin" Почему же мы должны писать Petya%20Vasechkin? Это из=за того, что специальные символы могут быть распознаны сервером, как признаки наличия нового параметра или конца запроса и.т.д. Поэтому существует алгоритм кодирования имен параметров и их значений, во избежание оштбочных ситуаций в запросе. Полное описание данного алгоритма можно найти здесь, а в PHP есть функции rawurlencode и rawurldecode для кодирования и декодирования соответственно. Хочу отметеить, что декодирование РНР делает сам, если в запросе были переданы закодированные параметры. На этом я закону первую главу знакомства c протоколом HTTP. В следуючей главе мы рассмотрим построение запросов типа POST (в переводе с английского — "отправить"), что будет гораздо интереснее, т.к. именно данный тип запросов используется при отправке данных из HTML форм.

    3. Метод POST.

    В случае HTTP запроса типа POST существует два варианта передачи полей из HTML форм, а именно, используя алгоритм application/x-www-form-urlencoded и multipart/form-data. Различия между данными алгоритмами весьма существенные. Дело в том, что алгоритм первого типа создавался давным-давно, когда в языке HTML еще не предусматривали возможность передачи файлов через HTML формы. Итак, давайте рассмотрим эти алгоритмы на примерах.

    3.1 Content-Type: application/x-www-form-urlencoded.

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

    POST http://www.site.ru/news.html HTTP/1.0

    Host: www.site.ru

    Referer: http://www.site.ru/index.html

    Cookie: income=1

    Content-Type: application/x-www-form-urlencoded

    Content-Length: 35

    login=Petya%20Vasechkin&password=qq

    Здесь мы видим пример использования Content-Type и Content-Length полей заголовка. Content-Length говорит, сколько байт будет занимать область данных, которая отделяется от заголовка еще одним переводом строки
    . А вот параметры, которые раньше для запроса GET помещались в Request-URI, теперь находятся в Entity-Body. Видно, что они формируются точно также, просто надо написать их после заголовка. Хочу отметить еще один важный момент, ничто не мешает, одновременно с набором параметров в Entity-Body, помещать параметры с другими именами в Request-URI, например:

    POST http://www.site.ru/news.html?type=user HTTP/1.0

    .

    login=Petya%20Vasechkin&password=qq

    3.2 Content-Type: multipart/form-data

    Как только интернет мир понял, что неплохо бы было через формы отсылать еще и файлы, так W3C консорциум взялся за доработку формата POST запроса. К тому времени уже достаточно широко применялся формат MIME (Multipurpose Internet Mail Extensions — многоцелевые расширения протокола для формирования Mail сообщений), поэтому, чтобы не изобретать велосипед заново, решили использовать часть данного формата формирования сообщений для создания POST запросов в протоколе HTTP.

    Каковы же основные отличия этого формата от типа application/x-www-form-urlencoded?

    Главное отличие в том, что Entity-Body теперь можно поделить на разделы, которые разделяются границами (boundary). Что самое интересное — каждый раздел может иметь свой собственный заголовок для описания данных, которые в нем хранятся, т.е. в одном запросе можно передавать данные различных типов (как в Mail письме Вы одновременно с текстом можете передавать файлы).

    Итак, приступим. Рассмотрим опять все тот же пример с передачей логина и пароля, но теперь в новом формате.

    Читайте также:  Видеокарта nvidia geforce 9600m gt

    POST http://www.site.ru/news.html HTTP/1.0

    Host: www.site.ru

    Referer: http://www.site.ru/index.html

    Cookie: income=1

    Content-Type: multipart/form-data; boundary=1BEF0A57BE110FD467A

    Content-Length: 209

    —1BEF0A57BE110FD467A

    Content-Disposition: form-data; name="login"



    Petya Vasechkin

    —1BEF0A57BE110FD467A

    Content-Disposition: form-data; name="password"



    qq

    —1BEF0A57BE110FD467A—

    Теперь давайте разбираться в том что написано. 🙂 Я специально выделил некоторые символы
    жирным, чтобы они не сливались с данными. Присмотревшись внимательно можно заметить поле boundary после Content-Type. Это поле задает разделитель разделов — границу. В качестве границы может быть использована строка, состоящая из латинских букв и цифр, а так же из еще некоторых символов (к сожалению, не помню каких еще). В теле запроса в начало границы добавляется ‘—‘, а заканчивается запрос — границей, к которой символы ‘—‘ добавляются еще и в конец. В нашем запросе два раздела, первый описывает поле login, а второй поле password. Content-Disposition (тип данных в разделе) говорит, что это будут данные из формы, а в поле name задается имя поля. На этом заголовок раздела заканчивается и далее следует область данных раздела, в котором помещается значение поля (кодировать значение не требуется!).

    Хочу обратить Ваше внимание та то, что в заголовках разделов не надо использовать Content-Length, а вот в заголовке запроса надо и его значение является размером всего Entity-Body, стоящего после второго
    , следующего за Content-Length: 209
    . Т.е. Entity-Body отделяется от заголовка дополнительным переводом строки (что можно заметить и в разделах).

    А теперь давайте напишем запрос для передачи файла.

    POST http://www.site.ru/postnews.html HTTP/1.0

    Host: www.site.ru

    Referer: http://www.site.ru/news.html

    Cookie: income=1

    Content-Type: multipart/form-data; boundary=1BEF0A57BE110FD467A

    Content-Length: 491

    —1BEF0A57BE110FD467A

    Content-Disposition: form-data; name="news_header"



    Пример новости

    —1BEF0A57BE110FD467A

    Content-Disposition: form-data; name="news_file"; filename="news.txt"

    Content-Type: application/octet-stream

    Content-Transfer-Encoding: binary



    А вот такая новость, которая лежит в файле news.txt

    —1BEF0A57BE110FD467A—

    В данном примере в первом разделе пересылается заголовок новости, а во втором разделе пересылается файл news.txt. Внимательный да увидит поля filename и Content-Type во втором разделе. Поле filename задает имя пересылаемого файла, а поле Content-Type — тип данного файла. Application/octet-stream говорит о том, что это стандартный поток данных, а Content-Transfer-Encoding: binary говорит на о том, что это бинарные данные, ничем не закодированные.

    Очень важный момент. Большинство CGI скриптов написано умными людьми, поэтому они любят проверять тип пришедшего файла, который стоит в Content-Type. Зачем? Чаще всего закачка файлов на сайтах используется для получения картинок от посетителя. Так вот, браузер сам пытается определить что за файл посетитель хочет отправить и вставляет соответствующий Content-Type в запрос. Скрипт его проверяет при получении, и, например, если это не gif или не jpeg игнорирует данный файл. Поэтому при "ручном" формировании запроса позаботьтесь о значении Content-Type, чтобы оно было наиболее близким к формату передаваемого файла.

    image/gif для gif
    image/jpeg для jpeg
    image/png для png
    image/tiff для tiff (что используется крайне редко, уж больно емкий формат)

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

    4. Постскриптум.

    Думаю, что о передаче запросов на сервер не стоит рассказывать подробно. Это уже дело чисто РНР техники :-). Достаточно внимательно прочитать раздел о функциях работы с сокетами, или о функциях модуля CURL в официальной документации РНР.

    Из выше сказанного, надеюсь теперь понятно, почему вопрос: "Как мне сформировать POST запрос, используя функцию header?" — бессмысленен. Функция header(string) добавляет запись только в заголовок запроса, но никак не в тело запроса.

    Есть еще один тип запросов — Content-Type: multipart/mixed, надеюсь после прочтения данной статьи Вы легко разберетесь с данным типом сами. Подробно изучить его можно Здесь

    О запросах других типов можно прочитать в официальной спецификации протокола HTTP 1.0 здесь.

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

    Большинство разработчиков знают о важных и нужных HTTP-заголовках. Самые известные — Content-Type и Content-Length , это почти универсальные хедеры. Но в последнее время для повышения безопасности начали использоваться заголовки вроде Content-Security-Policy и Strict-Transport-Security , а для повышения производительности — Link rel=preload . Несмотря на широкую поддержку в браузерах, лишь немногие их используют.

    В то же время есть много чрезвычайно популярных заголовков, которые вообще не новые и не очень полезные. Мы можем это доказать с помощью HTTP Archive, проекта под управлением Google и спонсируемого Fastly, который каждый месяц при помощи WebPageTest скачивает 500 000 сайтов и выкладывает результаты в BigQuery.

    Вот 30 самых популярных заголовков ответов по данным HTTP Archive (на основе количества доменов в архиве, которые выдают каждый заголовок), и примерная оценка полезности каждого из них:

    Заголовок Запросов Доменов Статус
    date 48779277 535621 Требуется по протоколу
    content-type 47185627 533636 Обычно требуется браузером
    server 43057807 519663 Необязателен
    content-length 42388435 519118 Полезен
    last-modified 34424562 480294 Полезен
    cache-control 36490878 412943 Полезен
    etag 23620444 412370 Полезен
    content-encoding 16194121 409159 Требуется для сжатого контента
    expires 29869228 360311 Необязателен
    x-powered-by 4883204 211409 Необязателен
    pragma 7641647 188784 Необязателен
    x-frame-options 3670032 105846 Необязателен
    access-control-allow-origin 11335681 103596 Полезен
    x-content-type-options 11071560 94590 Полезен
    link 1212329 87475 Полезен
    age 7401415 59242 Полезен
    x-cache 5275343 56889 Необязателен
    x-xss-protection 9773906 51810 Полезен
    strict-transport-security 4259121 51283 Полезен
    via 4020117 47102 Необязателен
    p3p 8282840 44308 Необязателен
    expect-ct 2685280 40465 Полезен
    content-language 334081 37927 Спорно
    x-aspnet-version 676128 33473 Необязателен
    access-control-allow-credentials 2804382 30346 Полезен
    x-robots-tag 179177 24911 Не имеет значения для браузеров
    x-ua-compatible 489056 24811 Необязателен
    access-control-allow-methods 1626129 20791 Полезен
    access-control-allow-headers 1205735 19120 Полезен

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

    Тщеславие (server, x-powered-by, via)

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

    Server: apache
    X-Powered-By: PHP/5.1.1
    Via: 1.1 varnish, 1.1 squid

    RFC7231 позволяет включать в ответ сервера заголовок Server , указывая конкретный софт на сервере, который используется для выдачи контента. Чаще всего это строка вроде "apache" или "nginx". Хотя заголовок разрешён, он не обязательный и не особо ценный для разработчиков или конечных пользователей. Тем не менее, сегодня это третий по популярности серверный HTTP-заголовок в интернете.

    X-Powered-By — самый популярный заголовок из тех, что не определены никаким стандартом, и у него похожая цель: обычно он указывает платформу приложений, на которой работает сервер. Самые популярные ответы включают в себя "ASP.net", "PHP" и "Express". Опять же, это не несёт никакой ощутимой пользы и просто занимает место.

    Читайте также:  Cam как настроить fps

    Возможно, более спорным можно считать Via . Его обязан (по RFC7230) добавлять в запрос каждый прокси, через который проходит запрос — для идентификации прокси. Это может быть имя хоста прокси, но чаще общий идентификатор вроде "vegur", "varnish" или "squid". Удаление (или не добавление) такого заголовка может привести к циклу переадресации прокси. Но интересно, что он также добавляется в ответ на обратном пути к браузеру — и здесь выполняет просто информационную функцию: никакие браузеры ничего не с ним делают, поэтому достаточно безопасно избавиться от него, если хотите.

    Устаревшие стандарты (P3P, Expires, X-Frame-Options, X-UA-Compatible)

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

    P3P: cp="this is not a p3p policy"
    Expires: Thu, 01 Dec 1994 16:00:00 GMT
    X-Frame-Options: SAMEORIGIN
    X-UA-Compatible: IE=edge

    P3P — забавная штучка. Я понятия не имел, что это такое. Ещё забавнее, что одно из самых распространённых содержаний заголовка P3P — «Это не правило P3P». Ну так это оно или нет?

    Тут история восходит к попытке стандартизировать машиночитаемые правила приватности. Были разногласия по поводу того, как отображать данные в браузерах, и только один браузер реализовал поддержку этого заголовка — Internet Explorer. Но даже в нём P3P не имел никакого визуального эффекта для пользователя; он просто должен был присутствовать, чтобы разрешить доступ к сторонним кукам во фреймах. Некоторые сайты даже установили правила несоблюдения P3P, как в примере выше, хотя делать так весьма сомнительно.

    Нечего и говорить, что чтение сторонних куков вообще обычно плохая идея, так что если вы этого не делаете, то вам и не нужно устанавливать заголовок P3P !

    Expires просто невероятно популярен с учётом того, что Cache-Control имеет преимущество перед Expires уже в течение 20 лет. Если заголовок Cache-Control содержит директиву max-age , то любой заголовок Expires в том же ответе игнорируется. Но огромное количество сайтов устанавливает оба этих заголовка, а Expires чаще всего ставят на дату Thu, 01 Dec 1994 16: 00: 00 GMT , чтобы контент не кэшировался. Конечно, ведь проще всего копипастнуть дату из спецификаций.

    Но просто незачем это делать. Если у вас заголовок Expires с датой из прошлого, просто замените его на:

    Cache-Control: no-store, private

    ( no-store — слишком строгая директива не записывать контент в постоянное хранилище, так что вы можете предпочесть no-cache ради лучшей производительности, например, для навигации назад/вперёд или возобновления «спящих» вкладок в браузере)

    Некоторые инструменты проверки сайтов посоветуют добавить заголовок X-Frame-Options со значением ‘SAMEORIGIN’. Он говорит браузерам, что вы отказываетесь отдавать контент во фрейм на другом сайте: как правило, это хорошая защита от кликджекинга. Но того же эффекта можно достигнуть другим заголовком с более последовательной поддержкой и более надёжным поведением:

    Content-Security-Policy: frame-ancestors ‘self’

    Здесь есть дополнительная выгода, потому что заголовок CSP вы всё равно должны отдавать по иным причинам (о них позже). Вероятно, в наше время можно обойтись без X-Frame-Options .

    Наконец, ещё в IE9 компания Microsoft представила «режим совместимости», который отображал страницу с помощью движка IE8 или IE7. Даже в нормальном режиме браузер думал, что для правильного рендеринга может понадобиться более ранняя версия движка. Эти эвристики не всегда работали корректно, и разработчики могли переопределить их, используя заголовок или метатег X-UA-Compatible . Фактически, это стало стандартной частью многих фреймворков вроде Bootstrap. Сейчас этот заголовок практически бесполезен: очень мала доля браузеров, которые понимают его. И если вы активно поддерживаете сайт, то очень маловероятно, что на нём используются технологии, которые запустят режим совместимости.

    Данные для отладки (X-ASPNet-Version, X-Cache)

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

    X-Cache: HIT
    X-Request-ID: 45a336c7-1bd5-4a06-9647-c5aab6d5facf
    X-ASPNet-Version: 3.2.32
    X-AMZN-RequestID: 0d6e39e2-4ecb-11e8-9c2d-fa7ae01bbebc

    На самом деле, эти «неизвестные» заголовки не выдумали разработчики. Обычно это артефакты использования определённых серверных фреймворков, софта или сервисов конкретных поставщиков (например, последний заголовок типичен для AWS).

    Заголовок X-Cache добавляет Fastly (и другие CDN), вместе с другими специфичными хедерами, такими как X-Cache-Hits и X-Served-By . Когда отладка включена, то добавляется ещё больше, например, Fastly-Debug-Path и Fastly-Debug-TTL .

    Эти заголовки не распознаются ни одним браузером, а их удаление совершенно не отразится на отображении страниц. Но поскольку они могут предоставить вам, разработчику, полезную информацию, то можете их сохранить.

    Недоразумения (Pragma)

    Не ожидал, что в 2018 году придётся упоминать заголовок Pragma , но согласно HTTP Archive он по-прежнему в топе (11-е место). Его не только объявили устаревшим ещё в 1997 году, но он вообще никогда не задумывался как заголовок ответа, а только как часть запроса.

    Тем не менее его настолько широко используют в качестве заголовка ответа, что некоторые браузеры даже распознают его и в этом контексте. Но сегодня практически нулевая вероятность, что кто-то понимает Pragma в контексте ответа, но не понимает Cache-Control . Если хотите запретить кэширование, всё что вам нужно — это Cache-Control: no-store, private .

    Не-браузеры (X-Robots-Tag)

    Один заголовок в нашем топ-30 не является заголовком для браузера. X-Robots-Tag предназначен для краулеров, таких как боты Google или Bing. Поскольку для браузера он бесполезен, то можете установить такой ответ только на запросы краулеров. Или вы решите, что это затрудняет тестирование или нарушает условия использования поисковой системы.

    Наконец, стоит закончить почётном упоминанием простых ошибок. Заголовок Host имеет смысл в запросе, но если он встречается в ответе, то вероятно ваш сервер неправильно настроен (и я хотел бы знать, как именно). Тем не менее 68 доменов в HTTP Archive возвращают заголовок Host в своих ответах.

    Удаление заголовков на edge-сервере CDN

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

    unset resp.http.Server;
    unset resp.http.X-Powered-By;
    unset resp.http.X-Generator;

    В следующей статье я расскажу о лучших практиках для действительно нужных заголовков и как активировать их на edge-сервере CDN.

    Рекомендуем к прочтению

    Добавить комментарий

    Ваш адрес email не будет опубликован.