26 января 2016      607      0

Http заголовки в ответах сервера

Все статьи из цикла:

В этой статье мы рассмотрим заголовки в ответах сервера.

В php можно отослать заголовки с помощью функции header(«заголовок»).
Но не все заголовки нужно прописывать вручную: php сам посылает нужные заголовки, такие как cookies, заголовки для загрузки контента и т.д., которые могут быть сгенерированны автоматически.
С помощью функции headers_list() можно просмотреть заголовки, которые уже отправлены или только собираются быть отправленными. Если необходимо получить уже отправленные заголовки, воспользуйтесь функцией headers_sent().
Итак, начнём разбор заголовков ответа.

Cache-Control

Cache-Control — главный заголовок, используемый для указания вида кеширвоания страницы всеми механизмами кеширования, которые может использовать ваш провайдер. Данный заголовок может быть использован со следующими значениями:

  • no-store — вообще не допускает сохранения у прокси-сервера (используется, например, при передаче приватных данных).
  • no-transform — не допускает изменения данных при кешировании (изменения могут включать в себя сжатие, перевод изображений в другой формат и т.п.).
  • no-caсhe — Запрет использования информации из кеша без предворительной проверки на соответствие информации на сервере. Используется в часто обновляемых страницах и страницах с динамическим содержанием. Его действие подобно meta тегу «pragma: no-cache».
  • public — Разрешение кеширования страницы как локальным клиентом, так и прокси-сервером.
  • private — Разрешение кеширования только локальным клиентом.
  • max-age — Разрешение использования кешированного документа в течение заданного времени в секундах.
Cache-Control: max-age=3600, public
Content-Type

Этот заголовок указывает mime-type документа. В зависимости от значения content-type, браузер по-разному отображает переданное ему содержимое. Например, HTML-страницы (или скрипты PHP, возвращающие HTML) могут вернуть браузеру такой заголовок:

Content-Type: text/html; charset=UTF-8

«text» — это тип документа, а html — его подтип. Кроме типов, указывается кодировка, в которой передаётся текст.

Для передачи gif изображения content-type будет таким:

Content-Type: image/gif

На основе содержания content-type, браузер использует свои программы или подключает программы извне.
Например, следующий заголовок заставит браузер загрузить Adobe Reader для успешного отображения документа:

Content-Type: application/pdf

При загрузке документов, сервер apache, как правило, сам определяет mime-type документов и отправляет нужный заголовок. Однако, если этого не случилось по каким-то причинам, или mime-type указан неверно, браузеры могут отобразить содержимое документа корректно благодаря собственным механизмам распознавания вида документа.

Список самых распространённых mime-типов документов вы можете найти здесь

Если вы не можете определить mime-type документа, воспользуйтесь функцией php finfo_file()

Content-Disposition

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

1
Content-Disposition: attachment; filename="download.zip"

После этого в браузере появится такое окно:

download

Примечание: вместе с этим заголовком обязательно должен быть отправлен content-type.

1
2
Content-Type: application/zip
Content-Disposition: attachment; filename="download.zip"
Content-Length

В этом заголовке сервер сообщает о размере передаваемого файла. Размер указан в байтах.

1
Content-Length: 89123

Это очень полезно для отображения прогресса загрузки файлов.

Проведём эксперимент.
Этот скрипт имитирует медленную загрузку файла.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// притворяемся zip-файлом
header('Content-Type: application/zip');
// назначаем размер 1 миллион байтов (около 1 мегабайта)
header('Content-Length: 1000000');
// вызываем диалоговое окно загрузки и сохраняем файл как download.zip
header('Content-Disposition: attachment; filename="download.zip"');
// 1000 раз отправляем  1000 байтов
for ($i = 0; $i < 1000; $i++) {
    echo str_repeat(".",1000);
    // останавливаем выполнение скрипта для имитации медленной загрузки
    usleep(50000);
}

Результат работы:

download_with_progress

Как мы можем наблюдать, браузер отслеживает прогресс загрузки в процентах.

А теперь давайте посмотрим на то, как пойдет загрузка, если мы не сообщим браузеру размер файла

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Это zip файл
header('Content-Type: application/zip');
// Не говорим браузеру размер файла
// header('Content-Length: 1000000');
// вызываем диалоговое окно загрузки и сохраняем файл как download.zip
header('Content-Disposition: attachment; filename="download.zip"');
// 1000 раз отправляем  1000 байтов
for ($i = 0; $i < 1000; $i++) {
    echo str_repeat(".",1000);
    // останавливаем выполнение скрипта для имитации медленной загрузки
    usleep(50000);
}

Результат:

download_without_progress

Теперь браузер показывает, сколько он уже скачал, но не показывает, сколько ещё осталось скачать.

Etag

Ещё один тег, используемый для кеширования. Выглядит он так:

1
Etag: "pub1259380237;gz"

При выдаче документа, основанного на файле (картинка, zip-файл и т.д.), Apache формирует в ответе заголовок ETag (объектная метка) . По сути ETag — это уникальный идентификатор документа, значение которого меняется при изменении документа. ETag используется при кэшировании документа. Этот заголовок сохраняется на клиенте, и в случае повторного обращения к документу позволяет браузеру обратиться к серверу с запросом ‘If-None-Match’, а сервер должен по значению ETag-метки определить, не изменился ли документ, и, если тот не изменился, ответить кодом ‘304 Not Modified’.

В Apache значение ETag формируется из размера файла, inode файла, и времени его последней модификации (mtime). ETag в Apache равен 16-тиричному представлению Inode-FileSize-mtime, где mtime это Unix timestamp (количество секунд прошедших с 1 января 1900 года).

Заголовок ETag появился в HTTP 1.1.

Last-Modified

Этот заголовок указывает дату последнего изменения файла в формате GMT

1
Last-Modified: Sat, 28 Nov 2009 03:50:37 GMT

В php:

1
2
$modify_time = filemtime($file);
header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modify_time) . " GMT");

Этот заголовок используется в связке с заголовком запроса If-Modified-Since. Оба заголовка позволяют кешировать файл, а затем сравнивать время создания кеша и время последнего изменения файла. Если же в кеше устаревшая версия файла, файл будет скачан с сервера. В противном случае файл будет взят из кеша.

Location

Один из моих любимых заголовков, который я часто использую. Заголовок используется для перенаправления (редиректа) браузера. Если статус запроса будет равен 301 или 302, сервер обязательно отправит этот заголовок.

В php можно перенаправить пользователя на другую страницу следующим образом:

1
header('Location: http://www.scriptsite.ru');

По умолчанию этот заголовок сопровождается статусом 302. Если вы хотите отправить статус 301, то код должен выглядеть следующим образом:

1
header('Location: http://www.scriptsite.ru', true, 301);

Небольшое пояснение: второй аргумент функции равен true. Этот аргумент отвечает за перезапись заголовков с таким же названием. Если его значение установлено в false, то в добавок к существющему заголовку location будет сформирован ещё один, который мы сейчас создали. Если его значение установлено в true, то наш новый заголовок будет записан поверх старого.
Ну а третий аргумент — это и есть статус сервера.

Set-Cookie

Заголовок используется для установки и обновления cookie на в вашем браузере.

1
2
Set-Cookie: skin=noskin; path=/; domain=.amazon.com; expires=Sun, 29-Nov-2009 21:42:28 GMT
Set-Cookie: session-id=120-7333518-8165026; path=/; domain=.amazon.com; expires=Sat Feb 27 08:00:00 2010 GMT

Каждая «кука» отправляется в отдельном заголовке. Куки, создаваемые посредством javascript, не отправляются через http заголовки

В php куки отправляются функцией setcookie(), с помощью которой php сам создаст необходимые заголовки.

1
setcookie("TestCookie", "foobar", time()+3600);

TestCookie — имя переменной, foobar — значение, time()+3600 — время жизни куки (нынешнее время + 3600 секунд. т.е. ещё один час с момента отправки)

Если не указывать срок хранения cookie, они удалятся автоматически при закрытии окна браузера

WWW-Authenticate

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

1
WWW-Authenticate: Basic realm="Restricted Area"

401_prompt (1)

В php авторизацию посредством http заголовков можно осуществить так:

1
2
3
4
5
6
7
8
9
if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm="My Realm"');
    header('HTTP/1.0 401 Unauthorized');
    echo 'Текст, который увидит пользователь, если нажмёт кнопку "Отмена" ';
    exit;
} else {
    echo "<p>Здравствуй, {$_SERVER['PHP_AUTH_USER']}.</p>";
    echo "<p>Ты ввёл {$_SERVER['PHP_AUTH_PW']} в качестве своего пароля!</p>";
}

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

Content-Encoding

Этот заголовок, как правило, отправляется, если содержимое документа было сжато.

1
Content-Encoding: gzip

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

1
2
3
4
5
6
7
8
9
10
11
12
13
//Разрешаем буфферизацию вывода.
//Если браузер поддерживает сжатие, то все данные на выходе будут сжаты
 
<?php
 
ob_start("ob_gzhandler");
 
?>
<html>
<body>
<p>Эта страница будет сжата.</p>
</html>
<body>

На этом всё. Ознакомление с http заголовками окончено.

Все темы на сайте

© 2017 BorPost · Копирование материалов сайта без разрешения запрещено