HTTP Headers для "чайников"
Russian (Pусский) translation by Yuri Yuriev (you can also view the original English article)
Являетесь вы программистом или нет, вы видели его повсюду в Интернете. На данный момент в адресной строке браузера отображается нечто, что начинается с «https: //». Даже ваш первый скрипт Hello World отправил HTTP-header без вашего понимания. В этой статье мы собираемся узнать об основах HTTP-заголовков и о том, как их можно использовать в наших веб-приложениях.
Что такое HTTP Headers?
HTTP значит "Hypertext Transfer Protocol" (Протокол передачи гипертекста). Всемирная паутина использует этот протокол. Он был создан в начале 1990-х годов. Почти всё, что вы видите в вашем браузере, передаётся на ваш компьютер через HTTP. Например, когда вы открыли страницу этой статьи, ваш браузер отправил более 40 HTTP-запросов и получил HTTP-ответы для каждого из них.
Заголовки HTTP являются основной частью этих HTTP-запросов и ответов, и они несут информацию о браузере клиента, запрошенной странице, сервере и многом другом.


Пример
Когда вы вводите URL-адрес в адресной строке, ваш браузер отправляет HTTP-запрос, и он может выглядеть так:
1 |
GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1 |
2 |
Host: net.tutsplus.com |
3 |
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729) |
4 |
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 |
5 |
Accept-Language: en-us,en;q=0.5 |
6 |
Accept-Encoding: gzip,deflate |
7 |
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 |
8 |
Keep-Alive: 300 |
9 |
Connection: keep-alive |
10 |
Cookie: PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120 |
11 |
Pragma: no-cache |
12 |
Cache-Control: no-cache |
Первая строка - это "Request Line", которая содержит некоторую базовую информацию по запросу. Остальные - HTTP заголовки.
После этого запроса ваш браузер получает ответ HTTP, который может выглядеть так:
1 |
HTTP/1.x 200 OK |
2 |
Transfer-Encoding: chunked |
3 |
Date: Sat, 28 Nov 2009 04:36:25 GMT |
4 |
Server: LiteSpeed |
5 |
Connection: close |
6 |
X-Powered-By: W3 Total Cache/0.8 |
7 |
Pragma: public |
8 |
Expires: Sat, 28 Nov 2009 05:36:25 GMT |
9 |
Etag: "pub1259380237;gz" |
10 |
Cache-Control: max-age=3600, public |
11 |
Content-Type: text/html; charset=UTF-8 |
12 |
Last-Modified: Sat, 28 Nov 2009 03:50:37 GMT |
13 |
X-Pingback: https://net.tutsplus.com/xmlrpc.php |
14 |
Content-Encoding: gzip |
15 |
Vary: Accept-Encoding, Cookie, User-Agent |
16 |
|
17 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
18 |
<html xmlns="http://www.w3.org/1999/xhtml"> |
19 |
<head>
|
20 |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
21 |
<title>Top 20+ MySQL Best Practices - Nettuts+</title> |
22 |
<!-- ... rest of the html ... -->
|
Первая строка - это «Строка состояния», за которой следуют «HTTP-заголовки», до пустой строки. После этого начинается «содержимое» (в данном случае - HTML вывод).
Когда вы смотрите на исходный код веб-страницы в своём браузере, вы видите только часть HTML, а не заголовки HTTP, хотя они фактически были переданы вместе.
Эти HTTP-запросы также отправляются и принимаются для других вещей, таких как изображения, CSS-файлы, файлы JavaScript и т. д. Именно поэтому я сказал ранее, что ваш браузер отправил не менее 40 или более HTTP-запросов, поскольку вы загрузили только эту страницу статьи.
Теперь давайте рассмотрим структуру более подробно.
Как увидеть HTTP Headers
Для анализа HTTP-заголовков я использую следующие расширения Firefox:




В PHP:
- getallheaders() получает запросы headers. Вы можете использовать массив $_SERVER.
- headers_list() получает отзывы headers.
Далее в этой статье мы увидим примеры кода в PHP.
Структура запроса HTTP


Первая строка HTTP-запроса называется линией запроса и состоит из трёх частей:
- "method" указывает, какой это запрос. Наиболее распространённые методы GET, POST и HEAD.
- "path" , как правило, является частью URL-адреса, который идёт после host (домена). Например, если запрос "https://net.tutsplus.com/tutorials/other/top-20-mysql-best-practices/" , часть path будет "/tutorials/other/top-20-mysql-best-practices/".
- Часть "protocol" содержит "HTTP" и версию, которая обычно 1.1 в современных браузерах.
Остальная часть запроса содержит HTTP headers как пары "Name: Value" в каждой строке. Они содержат различную информацию о HTTP-запросе и вашем браузере. Например, строка "User-Agent" предоставляет информацию о версии браузера и операционной системе, которую вы используете. "Accept-Encoding" сообщает серверу, может ли ваш браузер принимать сжатый output, например gzip.
Возможно, вы заметили, что данные cookie также передаются внутри HTTP-заголовка. И если бы ссылочный url, это было бы в header тоже.
Большинство этих заголовков являются необязательными. Этот HTTP-запрос мог быть таким же маленьким:
1 |
GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1 |
2 |
Host: net.tutsplus.com |
И вы всё равно получите правильный ответ от веб-сервера.
Методы запроса
Три наиболее часто используемых метода запроса: GET, POST и HEAD. Вы, вероятно, уже знакомы с первыми двумя, начиная с написания html-форм.
GET: получение документа
Это основной метод, используемый для извлечения html, изображений, JavaScript, CSS и т. д. С использованием этого метода запрошено большинство данных, загружаемых в ваш браузер.
Например, при загрузке статьи Nettuts +, самая первая строка HTTP-запроса выглядит так:
1 |
GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1 |
2 |
... |
Как только html загрузится, браузер начнет отправлять GET-запрос изображений, который может выглядеть так:
1 |
GET /wp-content/themes/tuts_theme/images/header_bg_tall.png HTTP/1.1 |
2 |
... |
Веб-формы можно настроить под метод GET. Вот пример.
1 |
<form method="GET" action="foo.php"> |
2 |
|
3 |
First Name: <input type="text" name="first_name" /> <br /> |
4 |
Last Name: <input type="text" name="last_name" /> <br /> |
5 |
|
6 |
<input type="submit" name="action" value="Submit" /> |
7 |
|
8 |
</form>
|
Когда эта форма отправлена, HTTP-запрос начинается так:
1 |
GET /foo.php?first_name=John&last_name=Doe&action=Submit HTTP/1.1 |
2 |
... |
Вы можете видеть, что каждый ввод формы был добавлен в строку запроса.
POST: отправка данных на сервер
Даже если вы можете отправлять данные на сервер с помощью GET и строки запроса, во многих случаях POST будет предпочтительнее. Отправка больших объёмов данных с помощью GET нецелесообразна и имеет ограничения.
Запросы POST чаще всего отправляются веб-формами. Давайте изменим предыдущий пример формы на метод POST.
1 |
<form method="POST" action="foo.php"> |
2 |
|
3 |
First Name: <input type="text" name="first_name" /> <br /> |
4 |
Last Name: <input type="text" name="last_name" /> <br /> |
5 |
|
6 |
<input type="submit" name="action" value="Submit" /> |
7 |
|
8 |
</form>
|
Отправка этой формы создает HTTP-запрос следующим образом:
1 |
POST /foo.php HTTP/1.1 |
2 |
Host: localhost |
3 |
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729) |
4 |
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 |
5 |
Accept-Language: en-us,en;q=0.5 |
6 |
Accept-Encoding: gzip,deflate |
7 |
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 |
8 |
Keep-Alive: 300 |
9 |
Connection: keep-alive |
10 |
Referer: http://localhost/test.php |
11 |
Content-Type: application/x-www-form-urlencoded |
12 |
Content-Length: 43 |
13 |
|
14 |
first_name=John&last_name=Doe&action=Submit |
Здесь нужно отметить три важных момента:
- Путь в первой строке просто /foo.php, и больше нет строки запроса.
- Добавлены заголовки Content-Type и Content-Length, которые предоставляют информацию об отправляемых данных.
- Все данные теперь отправляются после заголовков, в том же формате, что и строка запроса.
Запросы POST метода также могут быть сделаны через AJAX, приложения, cURL и т. д. И все формы загрузки файлов необходимы для использования метода POST.
HEAD: получение информации заголовка
HEAD идентичен GET, за исключением того, что сервер не возвращает содержимое HTTP-ответа. Когда вы отправляете запрос HEAD, это означает, что вас интересуют только код ответа и HTTP headers, а не сам документ.
«Когда вы отправляете запрос HEAD, это означает, что вас интересуют только код ответа и HTTP headers, а не сам документ».
С помощью этого метода браузер может проверить, был ли документ изменён для целей caching. Он также может проверить, существует ли документ вообще.
Например, если у вас много ссылок на веб-сайте, вы можете периодически отправлять HEAD-запросы каждой из них, чтобы проверить наличие неработающих ссылок. Это будет намного быстрее, чем при использовании GET.
Структура ответа HTTP
После того, как браузер отправляет HTTP-запрос, сервер отвечает HTTP-ответом. Исключая контент, он выглядит так:


Первой порцией данных является протокол. Обычно это снова HTTP/1.x или HTTP/1.1 на современных серверах.
Следующая часть - это код состояния, за которым следует короткое сообщение. Код 200 означает, что наш запрос GET был успешным и сервер вернёт содержимое запрошенного документа сразу после headers.
Мы все видели "404" pages. Это число фактически приходит из части кода состояния HTTP-ответа. Если запрос GET будет создан для path, который сервер не может найти, он ответил бы 404, а не 200.
Остальная часть ответа содержит headers так же, как HTTP-запрос. Эти значения могут содержать информацию о софте сервера при последнем изменении страницы/файла, типе mime и прочее...
Опять же, большинство этих headers на самом деле являются необязательными.
Коды статуса HTTP
- 200 используются для успешных запросов.
- 300 для перенаправления.
- 400 используются, если возникла проблема с запросом.
- 500 используются, если возникла проблема с сервером.
200 OK
Как упоминалось ранее, этот код состояния отправляется в ответ на успешный запрос.
206 Partial Content
Если приложение запрашивает только диапазон запрошенного файла, возвращается код 206.
Это часто используется с менеджерами закачек, которые могут остановить и возобновить загрузку или разделить загрузку на части.
404 Not Found


Когда запрашиваемая страница или файл не найдена, сервер отправляет код ответа 404.
401 Unauthorized
Защищённые паролем веб-страницы отправляют этот код. Если вы не ввели логин правильно, вы можете увидеть следующее в вашем браузере.


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


403 Forbidden
Если вам не разрешен доступ к странице, этот код может быть отправлен в ваш браузер. Это часто происходит, когда вы пытаетесь открыть URL-адрес для папки, в которой нет индексной страницы. Если параметры сервера не позволяют отображать содержимое папки, вы получите ошибку 403.
Например, на моем локальном сервере я создал папку изображений. Внутри этой папки я помещаю файл .htaccess с этой строкой: "Options -Indexes". Теперь, когда я пытаюсь открыть http://localhost/images/ - я вижу это:


Существуют другие способы блокировки доступа и 403 могут быть отправлены. Например, вы можете блокировать по IP-адресу с помощью некоторых директив htaccess.
1 |
order allow,deny |
2 |
deny from 192.168.44.201 |
3 |
deny from 224.39.163.12 |
4 |
deny from 172.16.7.92 |
5 |
allow from all |
302 (or 307) Moved Temporarily & 301 Moved Permanently
Эти два кода используются для перенаправления браузера. Например, когда вы используете службу сокращения URL, такую как bit.ly, именно так они перенаправляют людей, которые идут по ссылке.
302 и 301 обрабатываются браузером очень похоже, но они могут иметь различные значения для spiders поисковых систем. Например, если ваш сайт не готов для обслуживания, вы можете перенаправить его в другое место с помощью 302. Поисковая система продолжит проверку вашей страницы в будущем. Но если вы перенаправите с использованием 301, это сообщит spider, что ваш сайт переехал в это место навсегда. За более точной информацией: http://www.nettuts.com перейдите на https://net.tutsplus.com/ используя 301 код вместо 302.
500 Internal Server Error


Этот код обычно отображается при сбое веб-скрипта. Большинство скриптов CGI не выводят ошибки непосредственно в браузер, в отличие от PHP. Если есть фатальные ошибки, они просто отправят код статуса 500. И тогда программист должен искать в журналах ошибок сервера, чтобы найти сообщения об ошибках.
Complete List
Вы можете найти полный список кодов состояния HTTP с их пояснениями here.
Заголовки HTTP в запросах HTTP
Теперь мы рассмотрим некоторые из наиболее распространенных HTTP headers , найденных в HTTP requests.
Почти все эти заголовки можно найти в массиве $ _SERVER в PHP. Вы также можете использовать функцию getallheaders() для извлечения всех заголовков одновременно.
Host
HTTP-запрос отправляется на определенные IP-адреса. Но так как большинство серверов способны размещать несколько сайтов под одним IP, они должны знать, какое доменное имя ищет браузер.
1 |
Host: net.tutsplus.com |
Это в основном имя host, включая домен и поддомен.
В PHP его можно найти, как $_SERVER['HTTP_HOST'] или $_SERVER['SERVER_NAME'].
User-Agent
1 |
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729) |
Этот заголовок может содержать несколько частей информации, таких как:
- Имя и версия браузера.
- Название и версия операционной системы.
- Язык по умолчанию.
Именно так веб-сайты могут собирать определённую общую информацию о своих системах surfers. Например, они могут определить, использует ли surfer мобильный браузер и перенаправляет их на мобильную версию своего веб-сайта, который лучше работает с низким разрешением.
В PHP может быть выражен так: $_SERVER['HTTP_USER_AGENT'].
1 |
if ( strstr($_SERVER['HTTP_USER_AGENT'],'MSIE 6') ) { |
2 |
echo "Please stop using IE6!"; |
3 |
}
|
Accept-Language
1 |
Accept-Language: en-us,en;q=0.5 |
Этот заголовок отображает настройки языка по умолчанию. Если сайт имеет разные языковые версии, он может перенаправить нового surfer на основе этих данных.
Он может содержать несколько языков, разделённых запятыми. Первый - это предпочтительный язык, и каждый из перечисленных языков может иметь значение «q», которое представляет собой оценку предпочтения пользователя для языка (min. 0 max. 1).
В PHP его можно найти так: $ _SERVER ["HTTP_ACCEPT_LANGUAGE"].
1 |
if (substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2) == 'fr') { |
2 |
header('Location: http://french.mydomain.com'); |
3 |
}
|
Accept-Encoding
1 |
Accept-Encoding: gzip,deflate |
Большинство современных браузеров поддерживают gzip и отправляют это в header. Затем веб-сервер может отправить выходной HTML-код в сжатом формате. Это позволяет уменьшить размер до 80% для экономии пропускной способности и времени.
В PHP его можно найти так: $ _SERVER ["HTTP_ACCEPT_ENCODING"]. Однако, когда вы используете функцию обратного вызова ob_gzhandler(), она будет проверять значение автоматически, поэтому вам это не нужно.
1 |
// enables output buffering
|
2 |
// and all output is compressed if the browser supports it
|
3 |
ob_start('ob_gzhandler'); |
If-Modified-Since
Если веб-документ уже сохранен в кеше в браузере и вы посещаете его снова, ваш браузер может проверить, был ли документ обновлён, отправив следующее:
1 |
If-Modified-Since: Sat, 28 Nov 2009 06:38:19 GMT |
Если он не изменялся с этой даты, сервер отправляет код ответа «304 Not Modified», а содержимое - нет, и браузер загружает содержимое из cache.
В PHP его можно найти так: $ _SERVER ['HTTP_IF_MODIFIED_SINCE'].
1 |
// assume $last_modify_time was the last the output was updated
|
2 |
|
3 |
// did the browser send If-Modified-Since header?
|
4 |
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { |
5 |
|
6 |
// if the browser cache matches the modify time
|
7 |
if ($last_modify_time == strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { |
8 |
|
9 |
// send a 304 header, and no content
|
10 |
header("HTTP/1.1 304 Not Modified"); |
11 |
exit; |
12 |
}
|
13 |
|
14 |
}
|
Существует также HTTP-заголовок Etag, который можно использовать для проверки текущего кэша. Мы поговорим об этом в ближайшее время.
Cookie
Как следует из названия, это отправляет файлы cookie, хранящиеся в вашем браузере для этого домена.
1 |
Cookie: PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120; foo=bar |
Это пары name=value, разделённые точками с запятой. Cookies могут также содержать id сеанса.
В PHP отдельные cookie-файлы могут быть доступны с помощью массива $ _COOKIE. Вы можете напрямую обращаться к переменным сеанса, используя массив $ _SESSION, и если вам нужен id сеанса, вы можете использовать функцию session_id () вместо cookie.
1 |
echo $_COOKIE['foo']; |
2 |
// output: bar
|
3 |
echo $_COOKIE['PHPSESSID']; |
4 |
// output: r2t5uvjq435r4q7ib3vtdjq120
|
5 |
session_start(); |
6 |
echo session_id(); |
7 |
// output: r2t5uvjq435r4q7ib3vtdjq120
|
Referer
Как следует из названия, этот HTTP header содержит ссылочный url.
Например, если я зашел на домашнюю страницу Nettuts + и нажал ссылку на статью, этот header будет отправлен в мой браузер:
1 |
Referer: https://net.tutsplus.com/ |
В PHP его можно найти как $ _SERVER ['HTTP_REFERER'].
1 |
if (isset($_SERVER['HTTP_REFERER'])) { |
2 |
|
3 |
$url_info = parse_url($_SERVER['HTTP_REFERER']); |
4 |
|
5 |
// is the surfer coming from Google?
|
6 |
if ($url_info['host'] == 'www.google.com') { |
7 |
|
8 |
parse_str($url_info['query'], $vars); |
9 |
|
10 |
echo "You searched on Google for this keyword: ". $vars['q']; |
11 |
|
12 |
}
|
13 |
|
14 |
}
|
15 |
// if the referring url was:
|
16 |
// http://www.google.com/search?source=ig&hl=en&rlz=&=&q=http+headers&aq=f&oq=&aqi=g-p1g9
|
17 |
// the output will be:
|
18 |
// You searched on Google for this keyword: http headers
|
Возможно, вы заметили, что слово «referrer» написано с ошибкой, как «referer». К сожалению, он превратился в официальную спецификацию HTTP подобным образом и застрял.
Authorization
Когда веб-страница запрашивает авторизацию, браузер открывает окно входа в систему. Когда вы вводите имя пользователя и пароль в этом окне, браузер отправляет другой HTTP-запрос, но на этот раз он содержит этот header
1 |
Authorization: Basic bXl1c2VyOm15cGFzcw== |
Данные внутри header имеют кодировку base64. Например, base64_decode ('bXl1c2VyOm15cGFzcw ==') возвратит 'myuser: mypass'
В PHP эти значения можно найти как $ _SERVER ['PHP_AUTH_USER'] и $ _SERVER ['PHP_AUTH_PW'].
Подробнее об этом будет, когда мы поговорим о заголовке WWW-Authenticate.
Заголовки HTTP в ответах HTTP
Теперь мы рассмотрим некоторые из наиболее распространенных HTTP headers, найденных в HTTP-ответах.
В PHP вы можете установить заголовки ответа, используя функцию header(). PHP уже отправляет определённые заголовки автоматически, для загрузки содержимого и настройки файлов cookie и прочее... Вы можете увидеть headers, которые отправляются или будут отправляться с помощью функции headers_list (). Вы можете проверить, были ли уже отправлены заголовки с помощью функции headers_sent().
Cache-Control
Определение из w3.org: «Поле заголовка Cache-Control используется для указания директив, которые ДОЛЖНЫ выполняться всеми механизмами кэширования по цепочке запросов/ответов». Эти «механизмы кэширования» включают шлюзы и прокси, которые может использовать ваш интернет-провайдер.
Пример:
1 |
Cache-Control: max-age=3600, public |
"public" означает, что ответ может быть кэширован кем угодно. "max-age" указывает, сколько секунд действителен кеш. Разрешение кэширования вашего сайта может снизить нагрузку на сервер и пропускную способность, а также увеличить время загрузки в браузере.
Кэширование также может быть предотвращено с помощью директивы "no-cache".
1 |
Cache-Control: no-cache |
Подробности смотрите в w3.org.
Content-Type
Этот header указывает "mime-type" документа. Затем браузер определяет, как интерпретировать содержимое на основании этого. Например, страница html (или PHP-скрипт с выходом html) может возвращать это:
1 |
Content-Type: text/html; charset=UTF-8 |
"text" - это тип, а "html" - подтип документа. Заголовок также может содержать больше информации, такой как charset.
Для gif-изображения это может быть отправлено.
1 |
Content-Type: image/gif |
Браузер может использовать внешнее приложение или расширение браузера на основе mime-type. Например, это приведет к загрузке Adobe Reader:
1 |
Content-Type: application/pdf |
При загрузке напрямую Apache обычно может обнаружить mime-тип документа и отправить соответствующий header. Кроме того, большинство браузеров имеют некоторую степень отказоустойчивости и автоопределение типов mime, если заголовки указаны неверно или отсутствуют.
Вы можете найти список общих типов mime here.
В PHP вы можете использовать функцию finfo_file() для определения mime-типа файла.
Content-Disposition
Этот header указывает браузеру открыть окно загрузки файла, вместо того, чтобы пытаться проанализировать содержимое. Пример:
1 |
Content-Disposition: attachment; filename="download.zip" |
Это заставит браузер сделать это:


Обратите внимание, что соответствующий заголовок Content-Type также должен быть отправлен вместе с этим:
1 |
Content-Type: application/zip |
2 |
Content-Disposition: attachment; filename="download.zip" |
Content-Length
Когда контент будет передаваться браузеру, сервер может указать его размер (в байтах), используя этот header.
1 |
Content-Length: 89123 |
Это особенно полезно при загрузке файлов. Именно так браузер может определить ход загрузки.
Например, вот сценарий-макет, который я написал, имитирует медленную загрузку.
1 |
// it's a zip file
|
2 |
header('Content-Type: application/zip'); |
3 |
// 1 million bytes (about 1megabyte)
|
4 |
header('Content-Length: 1000000'); |
5 |
// load a download dialogue, and save it as download.zip
|
6 |
header('Content-Disposition: attachment; filename="download.zip"'); |
7 |
|
8 |
// 1000 times 1000 bytes of data
|
9 |
for ($i = 0; $i < 1000; $i++) { |
10 |
echo str_repeat(".",1000); |
11 |
|
12 |
// sleep to slow down the download
|
13 |
usleep(50000); |
14 |
}
|
Вот результат:


Теперь я собираюсь закомментировать заголовок Content-Length
1 |
// it's a zip file
|
2 |
header('Content-Type: application/zip'); |
3 |
// the browser won't know the size
|
4 |
// header('Content-Length: 1000000');
|
5 |
// load a download dialogue, and save it as download.zip
|
6 |
header('Content-Disposition: attachment; filename="download.zip"'); |
7 |
|
8 |
// 1000 times 1000 bytes of data
|
9 |
for ($i = 0; $i < 1000; $i++) { |
10 |
echo str_repeat(".",1000); |
11 |
|
12 |
// sleep to slow down the download
|
13 |
usleep(50000); |
14 |
}
|
Теперь результат такой:


Браузер может только сказать, сколько байтов было загружено, но он не знает общую сумму. И индикатор выполнения не показывает прогресс.
Etag
Это еще один header, который используется для кеширования. Это выглядит так:
1 |
Etag: "pub1259380237;gz" |
Веб-сервер может отправлять этот header с каждым документом, который он обслуживает. Значение может быть основано на последней изменённой дате, размере файла или даже контрольной сумме файла. Браузер затем сохраняет это значение, так как он кэширует документ. В следующий раз, когда браузер запрашивает тот же файл, он отправляет это в HTTP-запросе:
1 |
If-None-Match: "pub1259380237;gz" |
Если значение Etag документа совпадает с этим, сервер будет отправлять код 304 вместо 200, и никакого содержимого. Браузер будет загружать содержимое из своего кеша.
Last-Modified
Как следует из названия, этот header указывает дату последнего изменения документа в формате GMT:
1 |
Last-Modified: Sat, 28 Nov 2009 03:50:37 GMT |
1 |
$modify_time = filemtime($file); |
2 |
|
3 |
header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modify_time) . " GMT"); |
Это предлагает браузеру другой способ для cache документа. Браузер может отправить это в HTTP-запросе:
1 |
If-Modified-Since: Sat, 28 Nov 2009 06:38:19 GMT |
Мы уже говорили об этом ранее в разделе "If-Modified-Since".
Location
Этот заголовок используется для перенаправления. Если код ответа 301 или 302, сервер также должен отправить этот header. Например, когда вы перейдете на страницу http://www.nettuts.com, ваш браузер получит следующее:
1 |
HTTP/1.x 301 Moved Permanently |
2 |
... |
3 |
Location: https://net.tutsplus.com/ |
4 |
... |
В PHP вы можете перенаправить surfer так:
1 |
header('Location: https://net.tutsplus.com/'); |
По умолчанию, это отправит 302 код ответа. Если вы хотите вместо 301 отправить:
1 |
header('Location: https://net.tutsplus.com/', true, 301); |
Set-Cookie
Когда веб-сайт хочет установить или обновить файл cookie в вашем браузере, он будет использовать этот header.
1 |
Set-Cookie: skin=noskin; path=/; domain=.amazon.com; expires=Sun, 29-Nov-2009 21:42:28 GMT |
2 |
Set-Cookie: session-id=120-7333518-8165026; path=/; domain=.amazon.com; expires=Sat Feb 27 08:00:00 2010 GMT |
Каждый файл cookie отправляется как отдельный header. Обратите внимание, что файлы cookie, установленные с помощью JavaScript, не проходят через HTTP headers.
В PHP вы можете установить cookie-файлы, используя функцию setcookie(), а PHP отправляет соответствующие HTTP headers.
1 |
setcookie("TestCookie", "foobar"); |
Что приводит к отправке этого заголовка:
1 |
Set-Cookie: TestCookie=foobar |
Если дата истечения срока действия не указана, cookie удаляется, когда окно браузера закрыто.
WWW-Authenticate
Сайт может отправить этот header для аутентификации пользователя через HTTP. Когда браузер увидит этот header, он откроет диалоговое окно входа в систему.
1 |
WWW-Authenticate: Basic realm="Restricted Area" |
Что будет выглядеть так:


В руководстве PHP есть section, в котором приведены образцы кода, как это сделать в PHP.
1 |
if (!isset($_SERVER['PHP_AUTH_USER'])) { |
2 |
header('WWW-Authenticate: Basic realm="My Realm"'); |
3 |
header('HTTP/1.0 401 Unauthorized'); |
4 |
echo 'Text to send if user hits Cancel button'; |
5 |
exit; |
6 |
} else { |
7 |
echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>"; |
8 |
echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as your password.</p>"; |
9 |
}
|
Content-Encoding
Этот header обычно устанавливается, когда возвращаемое содержимое сжимается.
1 |
Content-Encoding: gzip |
В PHP, если вы используете функцию обратного вызова ob_gzhandler(), она будет автоматически установлена.
Заключение
Спасибо за прочтение. Надеюсь, эта статья послужит хорошей отправной точкой для изучения HTTP Headers. Пожалуйста, оставьте свои комментарии и вопросы ниже, и я постараюсь дать как можно больше ответов.



