Архив ‘HTTP’

Настройка кеширования в Apache

Пятница, 10 апреля, 2009

По умолчанию Apache настроен так, что кеширование запрещено даже для статического контента. Это означает, что при каждом обращении к сайту заново подгружаются все файлы CSS, JavaScript и картинки, что явно не способствует быстродействию.
Чтобы этого избежать, следует настроить выдачу заголовков Expires (время устаревания контента), ETag (строка, которая изменяется в случае изменения файла) и Cache Control (управление кешированием).
В Apache 2.x это делается с помощью следующих директив:
Далее…

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

Вторник, 17 марта, 2009

Webo.In — еще один сайт для измерения времени загрузки. Примечателен тем, что кроме измерения и детального расписывания всех составляющих времени загрузки дает целый набор рекомендаций по повышению скорости загрузки сайта (включая как изменение самой Web-страницы, например, объединение/сжатие CSS, так и настройки Apache). Также содержит множество полезных статей по теме ускорения загрузки сайта.

DURIS — сайт, позволяющий сжимать CSS и применять в них технологию Data In URIs, с помощью которой можно помещать картинки в непосредственно в текст CSS, не получая их с сервера в виде отдельных файлов.

Статус 304 и буферизация в PHP

Пятница, 30 января, 2009

Если используется буферизация средствами PHP (т.е. в скрипте имеется конструкция вида ob_start), то при выдаче статуса 304 возникает следующая ситуация: после выдачи статуса происходит выдача тела документа, содержащегося в буфере (даже в том случае, если функция ob_start была вызвана, а какого-либо вывода не производилось, т.е. буфер имеет нулевую длину). Это является нарушением протокола HTTP, согласно которому после выдачи статуса 304 контента выдаваться не должно и вызывает негативную реакцию роботов поисковых систем (в частности, Яндекс такую ошибку интерпретирует как “Передано неверное количество данных” и может удалить соответствующие страницы из своего индекса).
Избежать этой ситуации можно следующим образом: после вызова функции header с выдачей заголовка очищать буфер вывода с помощью функции ob_end_clean.

Правильная проверка статуса 304

Пятница, 30 января, 2009

В HTTP-протоколах версии 1.0 и 1.1 имеются специальные средства, позволяющие проверять, нужно ли обновить документ, который хранится в кеше у клиента. В случае если страница не изменялась, существует возможность выдать только заголовок со статусом 304 и тем самым сэкономить траффик, а в ряде случаев и разгрузить сервер за счет того, что выполнение скрипта в такой ситуации можно завершить раньше.
В HTTP 1.0 такими средствами является пара заголовков Last-modified/If-Modified-Since, в HTTP 1.1 к ним добавляются заголовки ETag/If-None-Match.
Работают эти средства следующим образом: при первом обращении клиента сервер выдает запрошенный документ и поле заголовка Last-Modified, в котором указывает дату последней модификации документа. (Для статики это время последнего изменения файла, которое хранится в файловой системе, для динамически генерируемого контента о хранении этой даты придется позаботиться разработчику скрипта.) При повторных обращениях к этому же документу клиент передает в заголовке поле If-Modified-Since, в котором указывает дату модификации копии документа в своем кеше. Скрипт, выполняющийся на сервере, сравнивает полученную дату с датой модификации данного документа на сервере, и если документ не изменился (т.е. дата на сервере меньше или равна дате на клиенте), может выдать статус 304 и завершить работу, не производя генерации документа заново.
Пара заголовков ETag/If-None-Match работает аналогично, только вместо даты используется заключенная в кавычки произвольная строка (метка), характеризующая документ. На практике в качестве такой строки для скриптов чаще всего берется MD5-хеш документа. Кроме того, в поле If-None-Match могут передаваться несколько меток, разделенных запятой.
Код для выполнения подобной проверки выглядит следующим образом (пояснение: глобальная переменная $GLOBALS['config']['nocache'] используется для отключения проверки кеша в отладочных целях, прочие параметры аналогичны описанным в предыдущем сообщении):
Далее…

Выдача HTTP-заголовков

Пятница, 30 января, 2009

Хотя выдача HTTP-заголовков на первый взгляд кажется очень простой задачей, в ней имеется ряд тонкостей:

  1. Статусы кроме числового кода имеют еще и стандартизированное словесное описание, которое также следует отдавать в строке статуса.
  2. Отдавать заголовок желательно в той же версии протокола, в которой был сделан запрос (в противном случае возможны проблемы с некоторыми старыми proxy-серверами).
  3. Некоторые дополнительные поля (например, ETag или Expires) есть только в версии HTTP 1.1.

Ниже приведен пример кода, который реализует корректную выдачу основных HTTP-заголовков (статус передается в $GLOBALS['status'], адрес перехода, если требуется, в $GLOBALS['location'], MD5-хеш отдаваемого документа — в $hash, дата последней модификации — в $GLOBALS['lastmod']). Код возвращает true, если требуется выдача тела документа и false в противном случае.
Далее…

Использование редиректов

Четверг, 28 февраля, 2008

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

Но есть более эффективный способ решения такой ситуации: использовать вместо META-тега стандартный переход HTTP по статусу 302, а сообщение об успешном редактировании выводить на той странице, куда будет выполнен переход. Такой вариант позволит с одной стороны, не допустить многократной отправки формы (проверенно в Opera 9.x, Firefox 1.5, 2.x, MSIE 6.x и 7.x) а с другой — не заставлять пользователя делать лишний щелчок мышью или ждать лишние секунды.

Но при этом возникает вопрос: как же узнать, нужно ли отображать сообщение со статусом Ok или нет? Решается это достаточно просто: при выполнении редиректа в новый URL приписывается определенный параметр, например, Ok=1 по наличию которого затем и принимается решение о выводе сообщения.

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

В итоге получаем примерно такой код:

header('HTTP/1.1 302 Object Moved');
header('Location: '.$_SERVER['HTTP_HOST'].'/newpage.php?ok=1');
// вывод заголовочной части HTML-страницы
echo 'Форма успешно обработана.  <a href="'.$_SERVER['HTTP_HOST'].'/newpage.php">Перейти на следующую страницу</a>';

Загрузка файла с возможностью докачки

Среда, 6 февраля, 2008

В HTTP-протоколе предусмотрена возможность получения части загружаемого файла или страницы. Для в запросе передается специальный параметр Range, который имеет вид Range: bytes=начало-конец. В ответе вместо обычного статуса 200 Ok выдается статус 206 Partial Content и добавляются поля Accept-Ranges: bytes и Content-Range: bytes начало-конец/общая_длина, при этом в поле Content-Length указывается длина только той части, которая передается клиенту.

На PHP это можно реализовать примерно так:

function send_file($filename) {
// примечание: в $filename -- полное имя файла, который нужно отдать клиенту
$length=filesize($filename);
if ($_SERVER["HTTP_RANGE"]) { // проверяем, пришел ли заголовок Range
  $range = $_SERVER["HTTP_RANGE"];
  $range = str_replace('bytes=','', $range);
  list($range_start,$range_end) =explode("-", $range);
  header('HTTP/1.1 206 Partial Content');
  header('Accept-Ranges: bytes');
  header('Content-Range: bytes '.intval($range_start).'-'.intval($range_end).'/'.$length);
}
else {
  $range_start=0;
  $range_end=$length-1;
  header('HTTP/1.1 200 Ok');
}
header('Content-Length: '.($range_end-$range_start+1));
header('Content-Type: application/octet-stream');
header('content-disposition: attachment; filename="'.basename($filename).'"');
header("Last-Modified: ".date('r',filemtime($filename)));
$fh=fopen($filename,'rb');
fseek($fh,$range_start);
$position=$range_start;
$bufsize=1*1024*1024;
while ($buffer=fread($fh,$bufsize) && $position<$range_end) {
  if ($position+$bufsize>$range_end) $buffer=substr($buffer,0,$range_end-$position);
  $position+=$bufsize;
  print $buffer;
}
fclose($fh);

Следует отметить, что поле content-disposition должно писаться со строчной (малой) буквы, т.к. в противном случае его некорректно воспринимает броузер Opera.

Отправка данных без перезагрузки страницы или HTTP-статус 204

Суббота, 15 декабря, 2007

Иногда бывает нужно отправить данные на сервер без перезагрузки Web-страницы (такое, например, часто требуется при сохранении в черновик редактируемго текста в CMS или форуме). Обычно это организовывается через запрос с помощью AJAX, но есть более простой и надежный способ, который будет работать даже при выключенном JavaScript — использование статуса 204 (No Content), предусмотренного в HTTP-протоколе. В PHP он выдается с помощью вызова функции header:

header('HTTP/1.0 204 No Content');

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

С точки зрения юзабилити желательно все же уведомить пользователя о том, что страница отправлена на сервер. Это уже можно сделать с помощью JavaScript следующим образом:

<div id="notyify" style="display:none">Страница отправлена на сервер</div>
<form>......
<button type="submit"
onClick="document.getElementById('notify').style.display='';
setTimeout('document.getElementById(\'notify\').style.display=\'none\'',10000);
return true">
Сохранить в черновик</button>
</form>

Rambler's Top100