Rocket science не будет. Если вы используете php-fpm, то скорее всего в связке с nginx. Простой вопрос: как в PHP получить значения HTTP заголовков запроса клиента?
- Например, стандартные Accept, Host или Referer?
- Знаете? Здорово! А как получить значение Content-Type, Content-Length?
- Ничем вас не удивить, а как получить значение произвольного заголовка, например X-Forwarded-For?
Как в PHP получить значения HTTP заголовков входящего запроса?
Всё очень просто (табличка сарказм). Нужно перейти на страницу документации переменной $_SERVER.
Переменная $_SERVER — это массив, содержащий информацию, такую как заголовки, пути и местоположения скриптов. Записи в этом массиве создаются веб-сервером.
Нет гарантии, что каждый веб-сервер предоставит любую из них;
сервер может опустить некоторые из них или предоставить другие, не указанные здесь.
Тем не менее многие эти переменные присутствуют в спецификации CGI/1.1,
так что вы можете ожидать их наличие.
Согласитесь звучит не очень обнадеживающе? Складывается ощущение, что это переменные Шрёдингера. На странице документации приводится ответ на первый вопрос.
$_SERVER['HTTP_ACCEPT']
$_SERVER['HTTP_HOST']
$_SERVER['HTTP_REFERER']
Ок, вроде бы всё просто, хоть на странице документации и не сказано про CONTENT_TYPE (правда есть небольшая подсказка комментария 2013 года), попробуем получить значение по аналогии.
$_SERVER['HTTP_CONTENT_TYPE']
К сожалению, такого ключа в массиве нет.
Ну да ладно, давайте посмотрим спецификацию CGI/1.1.
4.1.3. CONTENT_TYPE
If the request includes a message-body, the CONTENT_TYPE variable is
set to the Internet Media Type [6] of the message-body.//…
There is no default value for this variable. If and only if it is
unset, then the script MAY attempt to determine the media type from
the data received. If the type remains unknown, then the script MAY
choose to assume a type of application/octet-stream or it may reject
the request with an error (as described in section 6.3.3).//…
The server MUST set this meta-variable if an HTTP Content-Type field
is present in the client request header. If the server receives a
request with an attached entity but no Content-Type header field, it
MAY attempt to determine the correct content type, otherwise it
should omit this meta-variable.
Мы узнали ответ на второй вопрос.
$_SERVER['CONTENT_TYPE']
$_SERVER['CONTENT_LENGTH']
Перейдём к 3-му вопросу, продолжив чтение спецификации.
4.1.18. Protocol-Specific Meta-Variables
The server SHOULD set meta-variables specific to the protocol and
scheme for the request. Interpretation of protocol-specific
variables depends on the protocol version in SERVER_PROTOCOL. The
server MAY set a meta-variable with the name of the scheme to a
non-NULL value if the scheme is not the same as the protocol. The
presence of such a variable indicates to a script which scheme is
used by the request.Meta-variables with names beginning with «HTTP_» contain values read
from the client request header fields, if the protocol used is HTTP.
The HTTP header field name is converted to upper case, has all
occurrences of “-” replaced with “_” and has «HTTP_» prepended to
give the meta-variable name. The header data can be presented as
sent by the client, or can be rewritten in ways which do not change
its semantics. If multiple header fields with the same field-name
are received then the server MUST rewrite them as a single value
having the same semantics. Similarly, a header field that spans
multiple lines MUST be merged onto a single line. The server MUST,
if necessary, change the representation of the data (for example, the
character set) to be appropriate for a CGI meta-variable.The server is not required to create meta-variables for all the
header fields that it receives. In particular, it SHOULD remove any
header fields carrying authentication information, such as
‘Authorization’; or that are available to the script in other
variables, such as ‘Content-Length’ and ‘Content-Type’. The server
MAY remove header fields that relate solely to client-side
communication issues, such as ‘Connection’.
А вот и ответ на 3-ий вопрос.
$_SERVER['HTTP_X_FORWARDED_FOR']
Тут же мы узнали, что спецификация просит не заполнять $_SERVER[‘HTTP_CONTENT_TYPE’], а использовать $_SERVER[‘CONTENT_TYPE’].
Как Content-Type попадет в переменную $_SERVER[‘CONTENT_TYPE’]?
Перейдём ко второй части. Копнём чуть глубже, и посмотрим как веб-сервер (nginx) заполняет данными php массив $_SERVER.
Допустим мы решили поднять nginx + php-fpm через docker-compose
docker-compose.yaml
version: '3'
services:
nginx_default_fastcgi_params:
image: nginx:1.18
volumes:
- ./app/public:/var/www/app/public:rw
- ./docker/nginx_default_fastcgi_params/app.conf:/etc/nginx/conf.d/app.conf:rw
php-fpm:
build:
context: docker
dockerfile: ./php-fpm/Dockerfile
volumes:
- ./app:/var/www/app:rw
Примерно так будет выглядеть nginx конфиг app.conf
server {
listen 81;
server_name server1.local;
root /var/www/app/public;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/index.php {
fastcgi_pass php-fpm:9000;
fastcgi_split_path_info ^(.+.php)(/.*)$;
# file location /etc/nginx/fastcgi_params
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
error_log /var/log/nginx/app_error.log;
access_log /var/log/nginx/app_access.log;
}
Здесь нужно обратить внимание на строчку include fastcgi_params;
. Она подключает файл /etc/nginx/fastcgi_params, который выглядит примерно так
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
В этом месте как раз заполняется $_SERVER['CONTENT_TYPE']
. А так же остальные значения указанные в спецификации
.
И последний вопрос: Как остальные HTTP заголовки, например User-Agent попадают от nginx к php-fpm?
Всё просто, документация nginx даёт ответ.
Parameters Passed to a FastCGI Server
HTTP request header fields are passed to a FastCGI server as parameters. In applications and scripts running as FastCGI servers, these parameters are usually made available as environment variables. For example, the “User-Agent” header field is passed as the HTTP_USER_AGENT parameter. In addition to HTTP request header fields, it is possible to pass arbitrary parameters using the fastcgi_param directive.
Заметьте, здесь сказано, что HTTP заголовки передаются в приложение как HTTP_*. Но на самом деле два заголовка Content-Type и Content-Length, передаются по другому. Я бы назвал это ошибкой документации, но в ней есть слово usually, поэтому не будем придираться.
Выводы
1) Чтобы в php получить значение заголовка Content-Type/Content-Length
нужно использовать $_SERVER['CONTENT_TYPE']/$_SERVER['CONTENT_LENGTH']
. Для всех остальных заголовков $_SERVER['HTTP_*']
2) Я не знаю причину почему CGI выделил логику заголовков Content-Type/Content-Length. Возможно, для этого была весомая причина. Но результатом является куча неправильного кода программистов.
Например, на stackoverflow советуют вот так получить все HTTP заголовки
function getRequestHeaders() {
$headers = array();
foreach($_SERVER as $key => $value) {
if (substr($key, 0, 5) <> 'HTTP_') {
continue;
}
$header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
$headers[$header] = $value;
}
return $headers;
}
Как не сложно заметить, заголовки Content-Type/Content-Length данный код не вернет. При этом ответ имеет 350+ лайков.
Похожий код можно найти и в документации php
<?php
if (!function_exists('getallheaders')) {
function getallheaders() {
$headers = [];
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Оцените полезность статьи
19.12%
Я узнал много нового
26
35.29%
Я узнал немного нового
48
45.59%
Я не узнал ничего нового из этой статьи
62
Проголосовали 136 пользователей.
Воздержались 18 пользователей.
Являетесь вы программистом или нет, вы видели его повсюду в Интернете. На данный момент в адресной строке браузера отображается нечто, что начинается с «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:
- Firebug
- Live HTTP Headers
В 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, они должны знать, какое доменное имя ищет браузер.
Это в основном имя 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 |
// 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 обычно устанавливается, когда возвращаемое содержимое сжимается.
В PHP, если вы используете функцию обратного вызова ob_gzhandler(), она будет автоматически установлена.
Заключение
Спасибо за прочтение. Надеюсь, эта статья послужит хорошей отправной точкой для изучения HTTP Headers. Пожалуйста, оставьте свои комментарии и вопросы ниже, и я постараюсь дать как можно больше ответов.
I’m implementing a REST service in PHP. This service should be able to support multiple input and output formats (JSON, XML). For that reason I want to check the request headers “Accept” and “Content-Type” for the type of content sent and requested by the client.
Accessing the “Accept” header is a simple as using $_SERVER['HTTP_ACCEPT']
. But accessing the “Content-Type” header seems to be a difficult task. I searched the PHP documentation and the web, but the only solution offered was the use of the PHP function apache_request_headers()
which is only supported when PHP is installed as an Apache module, which is not true in my case.
So my question now: How can I access the header “Content-Type” of a request?
asked Apr 1, 2011 at 23:09
1
Normal (GET) requests do not have a Content-Type
header. For POST requests it would appear as $_SERVER["CONTENT_TYPE"]
, with a value like multipart/form-data or application/x-www-form-urlencoded.
This is mandated by the CGI/1.1 specification: http://www.ietf.org/rfc/rfc3875.
Mark Amery
140k78 gold badges402 silver badges457 bronze badges
answered Apr 1, 2011 at 23:13
mariomario
144k20 gold badges237 silver badges289 bronze badges
2
You’ll need to manually instruct Apache to supply the Content-Type
header (plus any other headers you want).
Pop something like this in your .htaccess
file or virtual host:
RewriteEngine on
RewriteRule .* - [E=HTTP_CONTENT_TYPE:%{HTTP:Content-Type},L]
And voila, you just synthesised your very own $_SERVER['HTTP_CONTENT_TYPE']
!
Edit:
I assume you’re running PHP as CGI with Apache so you can use verbs other than GET and POST, as most rest services do. If you’re using another web server or largely unheard-of PHP SAPI, you’ll need to use a similar trick; PHP as CGI simply doesn’t have access to request headers outside the contents of $_SERVER
, no matter what other mechanisms you use – $_ENV
, apache_request_headers()
, even the classes in the php_http
extension will all be empty.
answered Apr 1, 2011 at 23:17
Neil E. PearsonNeil E. Pearson
3,0012 gold badges16 silver badges20 bronze badges
7
You can also get the content type (like “text/html”) with this :
echo split(',', getallheaders()['Accept'])[0];
or
echo get_headers('http://127.0.0.1', 1)["Content-Type"]
Update
Like Benjamin said, apache_request_headers
is available with FastCGI from 5.4.0 and from internal PHP server since 5.5.7.
answered Dec 6, 2012 at 9:46
Fabien SaFabien Sa
9,0053 gold badges37 silver badges43 bronze badges
2
The Content-Type header is used to indicate the media type of the resource. The media type is a string sent along with the file indicating the format of the file. For example, for image file its media type will be like image/png or image/jpg, etc.
In response, it tells about the type of returned content, to the client. The browser gets to know about the type of content it has to load on the machine. Every time its byte stream of the file that browsers receive, by the Content-type header, the browser will do something known as MIME sniffing i.e. it will inspect the stream it is receiving and then loads the data accordingly.
Syntax:
Content-Type: text/html; charset=UTF-8 Content-Type: multipart/form-data; boundary=something
Directives: There are three directives in the HTTP headers Content-type.
- media type: It holds the MIME (Multipurpose Internet Mail Extensions) type of the data.
- charset: It holds the character encoding standard. Charset is the encoding standard in which the data will be received by the browsers.
- boundary: The boundary directive is required when there is multipart entities. Boundary is for multipart entities consisting of 70 characters from a set of characters known to be very robust through email gateways, and with no white space.
Example: This example display how the images are read by browser with and without setting the Content-type header.
Output :
Now without using Content-type header we will get the content of the image in bytes, So it is not of any use to us.
Output:
?PNG IHDRX??'?iCCPsRGB IEC61966-2.1(?u??+DQ??3????????????63??P????H?U????l??RDJV???9oF? $sn????{N???pZ??^?d?Z(p?E?]??h??QEW?f??T??{, f???????????z?aE??????y???6%]>vkrA?;S?????d??M? ¡?6???`%?????&???Q-Z?j????BSZo?a???}N ?._u {??#??N?g?{-bKGD??????? pHYs.#.#x??vtIME?4_?X IDATx??w?U??????MB$??$@@? 2t?"EDa???"? C?*C????Hq?ja??w ????????L{??}?}??w?;??{???{.4, ???j??? q10??_??h2]`P??:^?5??@?W?=????????XY??? w.??9??`z?1?!V??B????XM~^?|?1?qm???(?h??C?OV?js{e?+ L?b?{%?@`?+:sQ?@?
Here, it is clearly visible that by applying the Content-type header information tells the browser, the type of response it is getting from the server.
All possible values of HTTP Content-type header:
Type | Values |
---|---|
Application | application/EDI-X12 application/EDIFACT application/javascript application/octet-stream application/ogg application/pdf application/xhtml+xml application/x-shockwave-flash application/json application/ld+json application/xml application/zip application/x-www-form-urlencoded |
Audio | audio/mpeg audio/x-ms-wma audio/vnd.rn-realaudio audio/x-wav |
Image | image/gif image/jpeg image/png image/tiff image/vnd.microsoft.icon image/x-icon image/vnd.djvu image/svg+xml |
Multipart | multipart/mixed multipart/alternative multipart/related (using by MHTML (HTML mail).) multipart/form-data |
Text | text/css text/csv text/html text/javascript (obsolete) text/plain text/xml |
Video | video/mpeg video/mp4 video/quicktime video/x-ms-wmv video/x-msvideo video/x-flv video/webm |
VND | application/vnd.oasis.opendocument.text application/vnd.oasis.opendocument.spreadsheet application/vnd.oasis.opendocument.presentation application/vnd.oasis.opendocument.graphics application/vnd.ms-excel application/vnd.openxmlformats-officedocument.spreadsheetml.sheet application/vnd.ms-powerpoint application/vnd.openxmlformats-officedocument.presentationml.presentation application/msword application/vnd.openxmlformats-officedocument.wordprocessingml.document application/vnd.mozilla.xul+xml |
Supported Browsers: The browsers compatible with HTTP headers Content-type are listed below:
- Google Chrome
- Internet Explorer
- Firefox
- Safari
- Opera
HTML is the foundation of webpages, is used for webpage development by structuring websites and web apps.You can learn HTML from the ground up by following this HTML Tutorial and HTML Examples.
Last Updated :
29 Jul, 2021
Like Article
Save Article
Content-Type
Content-Type
Content-Type
заголовок представления используется , чтобы указать исходный тип носителя ресурса (до любого кодирования контента применяется для отправки).
В ответах заголовок Content-Type
предоставляет клиенту фактический тип возвращаемого содержимого. Значение этого заголовка можно игнорировать, например, когда браузеры выполняют сниффинг MIME; установите для заголовка X-Content-Type-Options
значение nosniff
, чтобы предотвратить такое поведение.
В запросах (таких как POST
или PUT
) клиент сообщает серверу, какой тип данных фактически отправляется.
Syntax
Content-Type: text/html; charset=UTF-8 Content-Type: multipart/form-data; boundary=something
Directives
media-type
-
Тип MIME ресурса или данных.
- charset
-
Стандарт кодирования символов.
- boundary
-
Для составных объектов требуется
boundary
директива. Директива состоит из от 1 до 70 символов из набора символов (и не заканчивается пробелом), которые, как известно, очень надежны для шлюзов электронной почты. Он используется для инкапсуляции границ нескольких частей сообщения. Часто к границе заголовка добавляются два тире, а к последней границе добавляются два тире в конце.
Examples
Content-Type
в HTML-формах
В запросе POST
, полученном в результате enctype
HTML-формы, Content-Type
запроса указывается атрибутом enctype в элементе <form>
.
<form action="/" method="post" enctype="multipart/form-data"> <input type="text" name="description" value="some text" /> <input type="file" name="myFile" /> <button type="submit">Submit</button> </form>
Запрос выглядит примерно так (здесь опущены менее интересные заголовки):
POST /foo HTTP/1.1 Content-Length: 68137 Content-Type: multipart/form-data; boundary= Content-Disposition: form-data; name="description" some text Content-Disposition: form-data; name="myFile"; filename="foo.txt" Content-Type: text/plain (content of the uploaded file foo.txt)
Specifications
Browser compatibility
Desktop | Mobile | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Chrome | Edge | Firefox | Internet Explorer | Opera | Safari | WebView Android | Chrome Android | Firefox для Android | Opera Android | Safari на IOS | Samsung Internet | |
Content-Type |
Yes |
12 |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
See also
Accept
Content-Disposition
-
206
Частичное содержимое X-Content-Type-Options
HTTP
-
CSP: upgrade-insecure-requests
Директива HTTP Content-Security-Policy (CSP) upgrade-insecure-requests предписывает агентам пользователя обрабатывать все URL-адреса сайта (те, которые обслуживаются, как если бы
-
CSP: worker-src
Директива worker-src HTTP Content-Security-Policy (CSP)определяет допустимые источники для сценариев SharedWorker,ServiceWorker.
-
Cookie
Заголовок HTTP-запроса Cookie содержит сохраненные файлы cookie, связанные с сервером. Заголовок Cookie является необязательным и может быть опущен, если, например, браузер
-
Cross-Origin-Embedder-Policy
Заголовок ответа HTTP Cross-Origin-Embedder-Policy (COEP) предотвращает загрузку документом любых ресурсов, которые явно не предоставляют разрешения (с использованием