Архив января 2009

Статус 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 в противном случае.
Далее…


Rambler's Top100