У меня есть PHP-файл, который будет возвращать то же самое с теми же параметрами $ _GET каждый раз – он детерминирован.
К сожалению, для эффективности (этот файл запрашивается очень часто), Apache по умолчанию получает ответ «200 OK» всякий раз, когда запрашивается страница PHP, заставляя пользователя снова загружать файл.
Есть ли способ отправить заголовок 304 Not Modified
тогда и только тогда, когда параметры одинаковы?
Бонус : могу ли я установить время истечения на нем, так что если кешированная страница больше, чем, скажем, три дня, она отправляет ответ «200 OK»?
Без кэширования самой страницы (или, по крайней мере, ее Etag) вы не можете использовать 304. Полноценный алгоритм кэширования несколько выходит за рамки, но общая идея:
<?php function getUrlEtag($url){ //some logic to get an etag, possibly stored in memcached / database / file etc. } function setUrlEtag($url,$etag){ //some logic to get an etag, possibly stored in memcached / database / file etc. } function getPageCache($url,$etag=''){ //[optional]some logic to get the page from cache instead, possibly not even using etag } function setPageCache($url,$content,$etag=''){ //[optional]some logic to save the page to cache, possibly not even using etag } ob_start(); $etag = getUrlEtag($_SERVER['REQUEST_URI']); if(isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) { header("HTTP/1.1 304 Not Modified"); exit; } if(($content=getPageCache($_SERVER['REQUEST_URI'],$etag))!==false){ echo $content; exit; } ?> //the actual page <?php $content = ob_get_clean(); setUrlEtag($_SERVER['REQUEST_URI'],$etag=md5($url.$content)); function setPageCache($_SERVER['REQUEST_URI'],$content,$etag); header("Etag: $etag"); echo $content; ?>
Все распространенные ошибки: вы можете не отображать страницы кэша для зарегистрированных пользователей, кеширование частичного контента может быть более желательным, вы сами несете ответственность за предотвращение устаревающего содержимого в кеше (возможно, используя триггеры в бэкэнд или базу данных при модификациях или просто играя с логикой getUrlEtag
) и т. д. и т. д.
Вы можете также играть с HTTP_IF_MODIFIED_SINCE
если это легче контролировать.
Как правило, вы возвращаете коды состояния HTTP, используя функцию заголовка:
Header("HTTP/1.1 304 Not Modified"); exit();
Однако этого недостаточно.
Проблема в том, что вы не знаете, как запросил файл, поэтому вам потребуется немного сотрудничества с браузером.
Вы можете искать If-modified-since
заголовками входящего запроса и возвращать соответствующий код состояния, если он присутствует, и в пределах диапазона дат.
Если вы отправляете надлежащий заголовок Expires
при первоначальном генерировании PHP, тогда браузер или прокси-кеш могут решить не получать запрос вообще (хотя, скорее всего, они установят заголовок If-modified-since
). Без заголовка Expires
браузер, скорее всего, всегда повторит полный запрос.
Для получения дополнительной информации см. http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html и выполните поиск "14.25"
Браузер выполнит сопоставление параметров GET
с кешированной копией, кстати. Вам не нужно ничего делать.
Вы пробовали header("HTTP/1.0 304 Not Modified");
в вашем PHP-коде, который вызывается? Если вы незнакомы, вы захотите поместить это в свой код, прежде чем вы начнете выводить что-либо в буфер.
Этот скрипт снова отобразит весь PHP-скрипт, но после этого он проверяет, соответствует ли ETag эквиваленту MD5 выходной строки, и если да, то он отправляет 304, и никакой полосы пропускания не используется. Вы также могли бы создать такую вещь с MD5 всего QueryString и т. Д. И хранить ее где-то, вам не нужно будет воссоздавать выходной контент (даже быстрее)
function sanitize_output($buffer) { $headers = apache_request_headers(); $tt5=md5($buffer); header('ETag: '.$tt5); if (isset($headers['If-None-Match']) && $headers['If-None-Match']===$tt5) { header('HTTP/1.1 304 Not Modified'); header('Connection: close'); exit(); } return $buffer; } ob_start("sanitize_output");