Сервер не выдает дату документа. Статьи. wb0.ru - Все для веб-мастера, on-line сервисы

Сервер не выдает дату документа

1. Дата изменения документа (Last-Modified)

При добавлении сайта или отдельных страниц для индексирования, некоторые поисковые системы (в частности "Яндекс"), дают сообщение: "Внимание! Сервер не выдает дату документа, поэтому в результатах поиска дата для него показываться не будет".
    Но через пару недель, Вы видите, что сайт проиндексировался и присутствует в "выдаче" поисковиков, и ощущение что "что-то не так в Датском королевстве", постепенно проходит. А напрасно!
    Проблема осталась, и, несмотря на отсутствие ярко выраженных внешних проявлений, последствия её могут быть достаточно серьёзными, и потом будет очень трудно сообразить откуда всё началось.

Вот что сам Яндекс пишет по этому поводу:
  Насколько критично, что мой сервер не выдает last-modified? Я пытался настроить, но ничего не вышло.

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

Зайдите, например, на эту страничку и введите URL Вашего сайта. Если в результатах выдачи, среди показанных заголовков Вы не обнаружите полей типа: " Last-Modified: Wed, 08 Feb 2006 09:34:29 GMT" и/или: "ETag: "1-0-1139391269" с возможным дополнением в виде: "Cache-Control: max-age=600"
"Expires: Fri, 10 Feb 2006 12:35:29 GMT"
 - тогда эта статья - для Вас!!

2. Откуда возникло это Last-Modified

Раньше, когда сайты создавались в соновном на HTML и их страницы были СТАТИЧЕСКИМИ, такой проблемы не возникало. Дело в том, что веб-сервер, например Apache, сам обрабатывал присылаемые заголовки и выдавал в ответ правильный заголовок с Last-Modified (время последнего изменения документа). Теперь же, чаще создают ДИНАМИЧЕСКИЕ страницы: SSI, CGI-скрипты, PHP, Perl. И веб-сервер не берёт на себя функции по выдаче Last-Modified для страниц, которые он считает динамическими. Это достаточно логично с точки зрения веб-сервера: контент, отдаваемый пользователю, фактически создаётся в момент обращения к странице, поэтому дата модификации непосредственно самого файла скрипта или SSI-страницы теряет практический смысл.

Можно, конечно, настроить веб-сервер, чтобы он обрабатывал отдавал дату модификации собственно самого скрипта динамической страницы, так же как и для статических страниц html, и в Интернете есть рекомендации как это реализовать на практике. Например,
      для SSI-документов("server-parsed"):
    В зависимости от настроек хостинга, Веб-cервер Apache будет выдавать Last-Modified в том случае, если указана директива "XBitHack full" (например, в файле .htaccess), и для файла, к которому происходит обращение, выставлен атрибут "исполняемый" для группы (например, командой "chmod g+x имя_файла", выполненной в Unix-shell).
    Или в файл .htaccess добавляется директива "RemoveHandler .htm .html" - она убирает все ассоциации с этим расширением у сервера (например обработку SSI команд). В результате сервер начинает отдавать дату документов, но перестает выполнять SSI диррективы в этом файле.
      для скриптов на PHP: <?php // Отдавать текущее время
   header ("Last-Modified: " . gmdate("D, d M Y H:i:s") ." GMT");
?>
      для скриптов на PERL: #!/usr/local/bin/perl
use POSIX qw(strftime);
my $LM = strftime "%a, %e %b %Y %H:%M:%S GMT", gmtime(time());
print "Last-Modified: $LM ";

    Но прежде, чем делать подобные вещи, лучше сначала понять, что такое Last-Modified и зачем оно надо?

3. Что такое КЭШ на стороне клиента?

Кэш на стороне клиента позволяет броузеру не запрашивать заново страницу при рефреше или повторном её вызове, а запросить только дату её изменения, и если веб-вервер подтвердит, что страница не менялась - показать её из своего КЭШа. Это позволяет сильно экономить трафик при навигации по сайту и резко увеличить скорость загрузки страниц - вместо повторной пересылки страницы, идет обмен маленькими заголовками, в СОТНИ раз меньшими по размеру!
   Спецификацией протоколов HTTP/1.0 и особенно HTTP/1.1, предусмотрена гибкая возможность кэширования как броузером страниц на стороне клиента так и на промежуточных прокси-серверах.

Кэш на стороне клиента реализуется 2-я способами:

 1. Meta-тегами HTML. Например, следующие строки запрещают кэширование броузером: <meta http-equiv="Expires" content="Mon, 19 Jul 1997 15:11:03 GMT" />
<meta http-equiv="Pragma" content="no-cache" />
Однако есть некоторые недостатки и "опасности" этого метода: - Если тэг не существовал когда страница была запрошена браузером впервые, но появляется позже, например, при модифицкации файла, браузер может оставаться в блаженном неведении и пользоваться свей кэшированной копей оригинала страницы.
- Прокси-серверы, кэширующие web-страницы, вообще не исследуют содержимое HTML-документа. Вместо этого они полагаются только на web-сервер, с которого пришли документы, и протокол HTTP. При неправильной выдаче заголовков сервером, web-браузер может считать, что не должен кэшировать страницу, но прокси-сервер между браузером и вашим web-сервером может не знать этого - и продолжит отправлять клиенту ту же самую, уже устаревшую, страницу.
** Netscape версии менее 6 не очень хорошо обрабатывает заголовок "Pragma: no-cache".
Поэтому, лучший подход состоит в том, чтобы использовать заголовки протокола HTTP. Например, с помощью функции PHP header, можно реализовать пример, эквивалентный приведённым выше двум мета-тэгам так:

<?php
header('Expires: Mon, 19 Jul 1997 15:11:03 GMT');
header('Pragma: no-cache');
?>

 2. HTTP-заголовками, присыламыми как клиентом, так и сервером.
Протоколом HTTP/1.0 предусмотрена следующая возможность управления КЭШем на стороне клиента:
   - Выдача <веб-сервером> заголовка Last-Modified в ответ на заголовок клиента If-Modified-Since
Дополнительно, <веб-сервер> может выдавать заголовок типа 'Expires: Fri, 10 Feb 2006 12:35:29 GMT', который если дата, указанная в нём меньше текущей - запрещает кэширование, если больше - разрешает.

Протокол HTTP/1.1 дополнительно ввёл ещё одну возможность управления кэшем:
   - Выдача <веб-сервером> заголовка 'ETag' в ответ на заголовок клиента 'If-None-Match'
Дополнительно в HTTP/1.1 может использоваться отправка "веб-сервером" заголовка 'Cache-Control: max-age=600', которая говорит о том, что до 600 секудн броузер клиента может считать страницу не изменившейся и даже на запрашивать о дате её модификации.
    Так же может использоваться директива HTTP/1.0 с полем заголовка 'Expires: Fri, 10 Feb 2006 12:35:29 GMT', которая при значении даты, относящейся к будущему, означает, что страница может быть занесена в кэш (если только не указано обратного в поле заголовка Cache-Control, типа 'Cache-Control: no-store').

Замечание. Если отклик содержит поле Cache-Control с директивой max-age, то эта директива переписывает значение поля Expires. Под словом "веб-сервер" надо понимать "Ваша веб-страница", поскольку в случае в случае динамических страниц сервер Apache за Вас это делать уже не станет. Это дополнительные "накладные расходы" за удобство PHP, SSI, CGI и прочих средств создания динамических страниц, и ложатся они на Ваши плечи (кроме отдельно подгружаемых JavaScript, стилей CSS и прочих статических элементов из ОТДЕЛЬНЫХ файлов - их Apache по-прежнему "отслеживает" сам).

Если Вы проигнорируете эту возможность управления КЭШем на стороне клиента: - Ваш сайт будет грузиться намного дольше, что, естественно, оттолкнёт от сайта часть Клиентов;
- Клиенты получат дополнительные расходы на входящий трафик;
- Вы получите дополнительную нагрузку на веб-сервер, который будет все страницы формировать и посылать заново;
- Вы получите некоторый дополнительный входящий трафик;
- Вы получите <нелюбовь> поисковых роботов, что проявится при большом количестве страниц на сайте, и будет выражаться в их медленной индексации и переиндексации.
Робот Яндекса, например, сразу не индексирует весь сайт, и потом приходит повторно, за "очередной порцией" и проверяет, что изменилось. А сайт ему отвечает - "а у меня все 10 000 страниц, и все - новые". Даже если на загрузку страницы уходит 2 сек, загрузить 10 000 страниц - это 6 часов! А когда Робот Яндекса увидит, что "новые" страницы - те же, что и хранятся в его индексе - быстро индексировать такой сайт он не будет.
    А если у Вас стоит Web-камера, картинки которой обновляются раз в несколько минут - какой смысл передавать клиенту одну и ту же картинку, если она не поменялась?

Если Вы будете неправильно управлять КЭШем на стороне клиента (выдавать некорректные данные) - возможна ситуация, когда содержимое сайта действительно изменится, а Клиент будет ещё долго брать её из своего КЭШа.
Поэтому: или ничего не делать, или, если делать - то очень тщательно!

4. Управление КЭШем на стороне клиента

If-Modified-Since - Last-Modified:
    Для управления КЭШем на стороне клиента, спецификацией протокола HTTP/1.0 предусмотрен следующий механизм: при обращении к странице, Броузер клиента, если такая страница есть в его КЭШе, отправляет на сервер заголовок: If-Modified-Since: Tue, 30 Mar 2004 13:57:13 GMT где "Tue, 30 Mar 2004 13:57:13 GMT" дата документа из КЭШа броузера. Сервер должен сравнить эту дату с датой "последней модификации" документа, и если документ не изменился - выдать в ответ заголовок: HTTP/1.0 304 Not Modified не передавая саму страницу. Броузер "поднимает" страницу их своего КЭШа и показывает Клиенту. Если документ изменился, то сервер должен выдать заголовок:

HTTP/1.0 200 OK
Last-Modified: 30 Apr 2005 16:07:23 GMT"
и следом передать саму изменённую страницу. Броузер обновляет ской КЭШ для данной страницы и запоминает новую дату её "последней модификации". Таким образом, экономится время и трафик на "передачу" страницы клиенту, если она не изменилась и снижается нагрузка на сервер.

В дополнение к этому, согласно спецификации HTTP/1.0, вместе с полем заголовка Last-Modified: 30 Apr 2005 16:07:23 GMT, сервер может передавать заголовок: "Expires: 30 Apr 2005 20:07:23 GMT", который информирует броузер клиента, что до 30 Apr 2005 20:07:23 GMT эта страница не изменится и можно показывать её их КЭША, не выполняя запрос "If-Modified-Since: ..." к серверу. Полный ответ сервера будет выглядеть так: HTTP/1.0 200 OK
Last-Modified: 30 Apr 2005 16:07:23 GMT"
Expires: 30 Apr 2005 20:07:23 GMT"
и/или заголовок 'Cache-Control: max-age' (появился в HTTP/1.1), где max-age - через сколько секунд устаревает сохранённая в кэше броузера копия документа: Cache-Control: max-age=600" - копия устаревает через 10 минут от времени запроса (см * - остальные директивы Cache-Control). 1. 'Cache-Control: max-age' переопределяет 'Expires:', если используются совместно.
2. Сервер Apache автоматически выдаёт и перезаписывает директиву Expires: на 'Expires: 1 Jan 1970', если в httpd.conf или .htaccess не включена директива 'CharsetOverrideExpires Off', разрешающая такое поведение сервера ТОЛЬКО если страница сама не выдаёт в заголовке поле Expires:.

If-None-Match - ETag:
    Спецификацией протокола HTTP/1.1 предусмотрена дополнительная, более гибкая возможность управления КЭШем на стороне клиента, которая может осуществляться как ОДНОВРЕМЕННО, так и ОТДЕЛЬНО от "If-Modified-Since - Last-Modified", это обмен заголовками ETag. Если броузер клиента поддерживает обмен заголовками ETag, то в запросе к серверу, он может прислать заголовок: If-None-Match: "1c9113-1be2-40697cb9 где 1c9113-1be2-40697cb9 и есть ETag - некий уникальный хеш документа, хранимой в КЕШе борузера. Этот идентификатор генерируется сервером, и присылается вместе со страницей. Как наиболее распространеный вариант - md5().
    В ответ на заголовок "If-None-Match: "1c9113-1be2-40697cb9", сервер проверяет изменение страницы с данным ETag, и если она не изменилась, возвращает ответ:

HTTP/1.1 304 Not Modified
ETag: "1c9113-1be2-40697cb9"
то есть, возвращает то же значение ETag-а с кодом 304 и не передаёт саму старницу. Если страница изменилась, то ответ сервера: HTTP/1.1 200 OK
ETag: "2b3412-3af4-67812ac6"
то есть ETag присылается новый и следом передаётся новая страница, которую вместе с ETag-ом броузер запоминает в своём КЭШе.

    Например, в Apache значение ETag формируется из inode файла, размера файла, и времени его последней модификации (mtime). ETag в Apache равен 16-тиричному представлению Inode-FileSize-mtime, где mtime это Unix timestamp (количество секунд прошедших с 1 января 1900 года).
ETag, аналогичный выдаваемому сервером Apache, можно сгенерировать php-функцией:
<?php
function get_file_etag($filename) {
 return dechex(fileinode($filename)).'-'.dechex(filesize($filename)).'-'.dechex(filemtime($filename));
 }
?>

ETag может быть "строгим" (два документа имеют одинаковые ETags только если они совпадают побитово) или "нестрогим" (два документа имеют одинаковые ETags если они совпадают по содержанию, но могут отличаться в незначительных деталях). Для файла, "строгим" ETag-ом может быть, например, его md5-хэш, а для динамической страницы "нестрогим" ETag-ом может быть md5-хэш её основного содержимого (без учёта дизайна и баннеров).
    Строгий ETag-заголовок имеет формат "Entity Tag", а нестрогий W/"Entity Tag": ETag: "2b3412-3af4-67812ac6" Пример нестрогого ETag:
ETag: W/"2b3412-3af4-67812ac6"

Так же надо отметить, что спецификация протокла HTTP/1.1 позволяет Клиенту присылать запросы серверу на проверку ETag в форматах: If-None-Match: * что означает "любой ETag", и в формате: If-None-Match: "строка1", "строка2", "строкаN" что означает проверку всего списка ETag-ов.

Таким образом, с помощью ETag можно обеспечить более гибкий механизм управления КЭШем на стороне клиента, поскольку можно разрешить броузеру показывать страницу из КЭШа, если на странице произошли незначительные изменения, например, изменились только баннеры.

5. Принятие сервером решения об обновлении страницы

При принятии сервером решения о выдаче ответа об изменении страницы, надо учесть проверку If-Modified-Since на корректность - если If-Modified-Since больше текущего времени, нужно отдать документ с "200 OK". В общем виде, таблица принятия сервером решения какой заголовок надо выдать в различных случаях и комбинациях If-Modified-Since и If-None-Match, выглядит следующим образом:

Таблица принятия сервером решения выдачи КОДА заголовка
If-Modified-Since: DATE If-None-Match: ETAG
не прислан ETAG == ETag или ETAG =='*' ETAG != ETag
не прислан 200 304 200
DATE >= Last-Modified 304 304 200
DATE < Last-Modified
или DATE > time()
200 200 200

где:
DATE - дата, присланная в запросе "If-Modified-Since:";
ETAG - ETag, присланный в запросе "If-None-Match:";
Last-Modified - дата последней модификации страницы на сервере;
ETag - текущий ETag страницы на сервере;
time() - текущая дата на сервере;

    304 - означает, что сервер НЕ ПЕРЕДАЁТ страницу, а только возвращает заголовок: HTTP/1.1 304 Not Modified
ETag: "1c9113-1be2-40697cb9"
Last-Modified: 30 Apr 2005 16:07:23 GMT"
где:
"1c9113-1be2-40697cb9" - текущий ETag страницы (может отсутствовать, если сервер не поддерживает ETag);
30 Apr 2005 16:07:23 GMT - дата последней модификации страницы;

    200 - означает, что сервер возвращает заголовок: HTTP/1.1 200 OK
ETag: "1c9113-1be2-40697cb9"
Last-Modified: 30 Apr 2005 16:07:23 GMT"
где:
"1c9113-1be2-40697cb9" - новый ETag страницы (может отсутствовать, если сервер не поддерживает ETag);
30 Apr 2005 16:07:23 GMT - новая дата последней модификации страницы;
и следом ПЕРЕДАЁТ обновлённую страницу!

6. Практическая реализация

Ниже приведён пример PHP-функции для принятия решения о возвращении кода 304 или 200 в зависимости от присланных в запросе GET или HEAD заголовков Last-Modified и/или If-None-Match.

<?php
  .// $last_modified - дата последней модификации документа на сервере, Unix Timestamp. Default -текущая
  .// $etag = текущий ETag документа на серверре, default = ""
  .// return TRUE - обновить страницу (200 ОК), FALSE - не обновлять (304 Not Modified)

function MakeHeaders($last_modified = "", $etag="") {
  global $_SERVER;
  $refresh = TRUE;

  if ($last_modified == "") $last_modified = time();

  $none_match = (isset($_SERVER['HTTP_IF_NONE_MATCH'])) ?$_SERVER['HTTP_IF_NONE_MATCH'] :"";
  $modified_since = (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) ?$_SERVER['HTTP_IF_MODIFIED_SINCE'] :"";

  if($modified_since) {   // BUG: NetScape sends ";lenght = xxx" after the date
    $arraySince = explode(";", $modified_since);
    $since = strtotime($arraySince[0]);
    }

  switch (TRUE) {
    case (!$none_match && $modified_since):
      if ( ($since <= time())
        && is_int($since)
        && ($since >= $last_modified) ) {
          return FALSE;
          }
      break;
    case ($none_match):
      if ($modified_since) {         // Проверка и по If-None-Match, и по If-Modified-Since
        if (($since > time()) || !is_int($since)
         || ($since < $last_modified)) break; // Файл в кеше клиента устарел по If-Modified-Since
       }
        .// Проверку If-Modified-Since, если она была - прошли. Проверка по If-None-Match:
      if ($etag == "") break;
      $INM = split('[,][ ]?', $none_match);
      foreach($INM as $enity) {
        if ($enity=="\"$etag\"" || $enity=="\"*\"")
          return FALSE;   // 304 Not Modified
        }
      break;
    default: ;   // Conditional Get не задан - просто отдаем страницу.
    }
  return TRUE;   // Страница изменилась (200 ОК)
  }
?>

7.* Список директив HTTP/1.1 Cache-Control
Таблица директив Cache-Control, посылаемых сервером в заголовках
Директива Описание
public Ответ разрешается сохранять в любом кэше.
private Ответ разрешается сохранять только в закрытом кэше (т.е. только для этого пользователя).
no-cache Кэшированный ответ не должен использоваться без предварительной его валидации.
no-store Ответ не разрешается сохранять в кэше.
no-transform К передаваемому документу не должны применяться преобразования.
must-revalidate Если кэшированный ответ устарел, то к нему должна применяться процедура валидации.
max-age=delta-seconds Клиент использует кэшированный ответ, если его возраст не превышает delta-seconds секунд; клиент не требует его валидации.
s-maxage=delta-seconds То же, что max-age, но действует только на открытые кэши.
proxy-revalidate То же, что must-revalidate, но действует только на прокси-сервера.
Если необходимо дать несколько директив Cache-Control, они перечисляются в ОДНОМ заголовке через запятую: "Cache-Control: max-age=$maxCache, public, must-revalidate".

Дополнительно возникает вопрос по поводу передачи сервером в заголовке поля "Content-Length: ..."
HTTP/1.0 - ответ при удаче всегда 200 ОК - размер файла не передается;
HTTP/1.1 - если в запросе Клиента не определен "Accept-Ranges: ...", ответ сервера 200 OK + размер файла в Content-Length, иначе 206 Partial Content - и в Content-Length - размер контента;
Кроме того, иногда используется блочная передача (chunked) , при этом размер также не передается. Сообщения НЕ ДОЛЖНЫ одновременно включать и поле заголовка Content-Length и применять кодирование передачи типа "chunked".

8. Ссылки по теме

* В связи с тем, что спецификация протокола HTTP/1.1 RFC-2068 несколько устарела, с новой спецификацией RFC-2616 и обновленим к ней RFC-2817 можно ознакомится на: RFC 2616 - описание и спецификация протокола HTTP/1.1

  1. Простой скрипт просмотра заголовков, присылаемых веб-сервером. (Не показывает, но понимает Redirect и делает автоматический переход по нему)
  2. Другой скрипт просмотра заголовков, присылаемых веб-сервером. (Показывает Redirect но не делает автоматический переход по нему)
  3. SEO скрипт анализа страниц. (Показывает Redirect, если страница переадресует куда-либо, и массу дополнительных SEO-параметров для анализа сайта. Автоматический переход по Redirect-у не делает)
  4. Другой SEO скрипт анализа страниц. (Аналогичен предыдущему, но с несколько большей функциональностью)
  5. Скрипт просмотра того, что cервер посылает броузеру. (позволяет выбрать метод(GET/HEAD/TRACE), User-Agent, HTTP 1.0/1.1, Accept-Encoding, то есть может прикидываться любым броузером или Роботом)
  6. RFC-2068 - описание и спецификация протокола HTTP/1.1
  7. Библиотека i2r: RFC 2068 - HTTP/1.1
  8. RFC 1945 - описание и спецификация протокола HTTP/1.0
  9. Сервис по проверке возможности кэширования сайта

Дата публикации: 30.08.2008
info.data-com.ru

Статьи по теме:

   Ваш псевдоним:
Ваш комментарий:

Календарь событий


Новости Интернет


Поиск





Последний пересчет

тИЦ:07 Окт 15
PR:09 Дек 13

Наши партнеры

wservices.ru - регистрация доменов, Whois-сервисы Смайлы на все случаи жизни


 
Copyright © 2006-2024, wb0.ru