Кэширование файлов nginx. Кэшируем Wordpress средствами Nginx

|

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

Ускорить загрузку сайта можно разными способами, и кэширование браузера – один из самых важных. Это позволяет браузеру повторно использовать локальные копии загруженных ранее файлов. Для этого необходимо ввести новые заголовки ответа HTTP.

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

Требования
  • Сервер Ubuntu 16.04 (о настройке сервера можно узнать ).
  • Пользователь с доступом к команде sudo.
  • Предварительно установленный веб-сервер Nginx (руководство по установке – ).
  • Модуль map (инструкции по настройке этого модуля – в ).
1: Создание тестовых файлов

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

Чтобы определить тип файла, передаваемого по сети, Nginx не анализирует его контент (это было бы слишком медленно), вместо этого он смотрит на расширение файла, чтобы определить его MIME тип, который определяет цель файла.

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

В каталоге Nginx по умолчанию создайте файл test.html с помощью truncate. Как видно по расширению, это будет HTML-файл.

sudo truncate -s 1k /var/www/html/test.html

Таким же образом создайте ещё несколько тестовых файлов с расширениями jpg (изображение), css (таблица стилей) и js (JavaScript):

sudo truncate -s 1k /var/www/html/test.jpg
sudo truncate -s 1k /var/www/html/test.css
sudo truncate -s 1k /var/www/html/test.js

2: Проверка стандартного поведения Nginx

По умолчанию все файлы кэшируются одинаково. Чтобы убедиться в этом, используйте тестовый HTML-файл.

Отправьте запрос к test.html с локального сервера Nginx и просмотрите заголовки ответа:

Эта команда вернёт такой заголовок ответа:

HTTP/1.1 200 OK

Date: Sat, 10 Sep 2016 13:12:26 GMT
Content-Type: text/html
Content-Length: 1024

Connection: keep-alive
ETag: "57d40685-400"
Accept-Ranges: bytes

В выделенной красным строке вы видите заголовок ETag, который содержит уникальный идентификатор этого просмотра запрашиваемого файла. Если вы повторно запустите команду curl, вы увидите точно такое же значение ETag.

Браузер хранит значение ETag и отправляет его обратно на сервер в заголовке ответа If-None-Match при необходимости повторно запросить файл (например, при обновлении страницы).

Вы можете симулировать это поведение с помощью следующей команды:

curl -I -H "If-None-Match: "57d40685-400"" http://localhost/test.html

Примечание : Укажите своё значение ETag вместо 57f6257c-400.

Теперь команда вернёт:

HTTP/1.1 304 Not Modified
Server: nginx/1.10.0 (Ubuntu)
Date: Sat, 10 Sep 2016 13:20:31 GMT
Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT
Connection: keep-alive
ETag: "57d40685-400"

В этот раз Nginx вернет 304 Not Modified. Веб-сервер не будет пересылать файл снова, он просто сообщит браузеру о том, что он может повторно использовать загруженный ранее файл.

Это уменьшает сетевой трафик, но этого не вполне достаточно для достижения высокой производительности кэширования. Проблема ETag заключается в том, что браузер всегда должен отправлять серверу запрос, чтобы повторно использовать свой кэшированный файл. В ответ сервер вместо файла отправляет 304, но эта процедура всё ещё занимает много времени.

3: Настройка заголовков Cache-Control и Expires

Кроме ETag существует ещё два заголовка ответа для управления кэшированием: Cache-Control и Expires. Cache-Control – более новый заголовок, он имеет больше функций, чем Expires, и в целом более полезен в настройке кэширования.

Эти заголовки сообщают браузеру, что запрашиваемый файл можно хранить локально в течение определённого периода (в том числе и всегда) и при этом не запрашивать его снова. Если эти заголовки не настроены, браузер будет вынужден постоянно запрашивать файлы у сервера и ожидать ответа 200 OK или 304 Not Modified.

Эти HTTP-заголовки можно настроить с помощью модуля header. Модуль Header встроен в Nginx, а значит, его не нужно устанавливать.

Чтобы добавить этот модуль, откройте файл виртуального хоста Nginx по умолчанию в текстовом редакторе:

sudo nano /etc/nginx/sites-available/default

Найдите блок server:

. . .

#
server {
listen 80 default_server;

. . .

Поместите в файл два новых раздела: один перед блоком server (настройки продолжительности кэширования файлов различных типов), а второй внутри этого блока (настройка заголовков кэширования).

. . .
# Default server configuration
#
# Expires map
map $sent_http_content_type $expires {
default off;
text/html epoch;
text/css max;
application/javascript max;
~image/ max;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
expires $expires;
. . .

Раздел перед блоком server – это новый блок map, который определяет соответствия между типом файла и периодом хранения его в кэше.

  • off — значение по умолчанию, которое не добавляет заголовков для управления кэшированием. Это мера предосторожности для контента, к кэшированию которого нет определённых требований.
  • text/html имеет значение epoch. Это специальное значение, которое отключает кэширование, вследствие чего браузер всегда будет запрашивать актуальное состояние сайта.
  • text/css (таблицы стилей) и application/javascript (файлы Javascript) имеют значение max. Это значит, что браузер будет кэшировать эти файлы в течение максимально возможного периода времени, значительно уменьшая количество запросов (учитывая, что таких файлов обычно много).
  • ~image/ — регулярное выражение, которое ищет все файлы с MIME-типом image/ (например, image/jpg и image/png). Оно также имеет значение max, поскольку изображений, как и таблиц стилей, на сайтах много. Кэшируя их, браузер уменьшит количество запросов.

Директива expires (включена в модуль headers) настраивает заголовки для управления кэшированием. Она использует значение переменной $expires, указанной в блоке map, благодаря чему заголовки ответа отличаются в зависимости от типа файла.

Сохраните и закройте файл.

Чтобы обновить настройки, перезапустите Nginx:

sudo systemctl restart nginx

4: Тестирование кэширования браузера

Выполните тот же запрос, что и в начале руководства:

curl -I http://localhost/test.html

Время ответа будет отличаться. В выводе вы увидите два новых заголовка ответа

HTTP/1.1 200 OK
Server: nginx/1.10.0 (Ubuntu)
Date: Sat, 10 Sep 2016 13:48:53 GMT
Content-Type: text/html
Content-Length: 1024
Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT
Connection: keep-alive
ETag: "57d40685-400"
Expires: Thu, 01 Jan 1970 00:00:01 GMT
Cache-Control: no-cache
Accept-Ranges: bytes

Заголовок Expires показывает дату в прошлом, а Cache-Control имеет значение no-cache,что значит, что браузер должен остоянно запрашивать актуальную версию файла (с помощью заголовка ETag).

Запросите другой файл:

curl -I http://localhost/test.jpg
HTTP/1.1 200 OK
Server: nginx/1.10.0 (Ubuntu)
Date: Sat, 10 Sep 2016 13:50:41 GMT
Content-Type: image/jpeg
Content-Length: 1024
Last-Modified: Sat, 10 Sep 2016 13:11:36 GMT
Connection: keep-alive
ETag: "57d40688-400"
Expires: Thu, 31 Dec 2037 23:55:55 GMT
Cache-Control: max-age=315360000
Accept-Ranges: bytes

Как видите, результат отличается. Expires содержит дату в далёком будущем, а Cache-Control имеет значение max-age, которое сообщает браузеру, как долго он может кэшировать файл (в секундах). В данном случае браузер будет кэшировать загруженный файл максимально долго, так что в дальнейшем для загрузки этого изображения браузер будет использовать локальный кэш.

Попробуйте отправить запросы к файлам test.js и test.css, вы должны получить похожий результат.

Из вывода команды curl видно, что кэширование браузера успешно настроено. Кэширование страниц увеличит производительность сайта и уменьшит количество запросов.

Примечание : Данное руководство предлагает удобные настройки кэширования, которые подойдут среднестатистическому сайту. Чтобы увеличить производительность своего сайта, проанализируйте его контент и настройте параметры кэширования, исходя из того, каких файлов больше на вашем сайте.

Кеширование (caching) — это технология или процесс создания копии данных на быстродоступных носителях информации (кеш, cash). Проще говоря и применяя к реалиям сайтостроения, это может быть создание статической html-копии страницы или её части, которая генерируется с помощью PHP-скриптов (или иных других, как-то Perl, ASP.net), смотря на каком языке написан CMS сайта) и сохраняется на диске, в оперативной памяти или даже частично в браузере (рассмотрим подробнее ниже). Когда произойдёт запрос страницы от клиента (браузера), вместо того, чтобы заново собирать её скриптами, браузер получит её готовую копию, что намного экономнее по затратам ресурсов хостинга, и быстрее, так как передать готовую страницу занимает меньше времени (порой значительно меньше), чем её создание заново.

Зачем использовать кеширование на сайте
  • Для снижения нагрузки на хостинг
  • Для быстрой отдачи содержимого сайта браузеру

Оба аргумента, думаю, в комментариях не нуждаются.

Недостатки и отрицательный эффект от кеширования сайта

Как ни странно, у кеширования сайта есть и свои минусы. В первую очередь это касается сайтов, содержание которых динамично изменяется при взаимодействии с ним. Зачастую, это сайты, которые выдают содержимое или его часть с помощью AJAX. В общем-то, кеширование AJAX тоже возможно и даже нужно, но это тема для отдельного разговора и не касается традиционно используемых технологий, о которых пойдёт речь далее.
Также, проблемы могут возникнуть у зарегистрированных пользователей, для которых постоянный кеш может стать проблемой при взаимодействии с элементами сайта. Тут, как правило, кеш отключается, либо используется объектное кеширование отдельных элементов сайта: виджетов, меню и тому подобных.

Как настроить кеширование у себя на сайте

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

Кеширование на стороне сервера Кеширование с помощью NGINX Кеширование с помощью htaccess (Apache)

Если у вас есть доступ только к.htaccess , и рабочий сервер только Apache, то вы можете использовать такие приёмы, как сжатие gzip и выставление заголовков Expires , чтобы использовать браузерный кеш.

Включаем сжатие gzip для соответствующих MIME-типов файлов

AddOutputFilterByType DEFLATE text/plain text/html AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript AddOutputFilterByType DEFLATE text/xml application/xml application/xhtml+xml application/rss+xml AddOutputFilterByType DEFLATE application/json AddOutputFilterByType DEFLATE application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon

Включаем заголовки Expires для статичных файлов сроком на 1 год (365 дней)

ExpiresActive on ExpiresDefault "access plus 365 days"

Кеширование с помощью Memcached Кеширование с помощью акселератора php

Если движок сайта написан на PHP, то при каждой загрузке любой страницы сайта происходит исполнение скриптов php: интерпретатор кода читает скрипты, написанные программистом, генерирует из них байткод, понятный машине, исполняет его и выдаёт результат. Акселератор PHP позволяет исключить постоянную генерацию байткода, кешируя скомпилированный код в памяти или на диске, тем самым увеличивая производительность и уменьшая время, затрачиваемое на исполнение PHP. Из поддерживаемых на сегодня акселераторов существует:

  • Windows Cache Extension for PHP
  • XCache
  • Zend OPcache

В PHP версии 5.5 и выше уже встроен акселератор Zend OPcache , поэтому чтобы включить акселератор, вам достаточно просто обновить версию PHP

Кеширование на стороне сайта

Как правило, тут подразумевается возможность CMS сайта создавать статические html-копии страниц. Такой возможностью обладают большинство популярных движков и фреймворков. Лично я работал со Smarty, WordPress, поэтому могу заверить, что они отлично справляются со своей работой. В оригинальном WordPress из коробки нет кеширующих возможностей, которые необходимы любому малость нагруженному проекту, зато есть множество популярных плагинов для кеширования:

  • , который как раз и занимается генерацией статических страниц сайта;
  • Hyper Cache, который по сути работает так же, как и предыдущий плагин;
  • DB Cache. Суть работы — кеширование запросов к базе данных. Тоже очень полезная функция. Можно использовать в связке с двумя предыдущими плагинами;
  • W3 Total Cache. Оставил его на десерт, это мой любимый плагин в WordPress. С ним сайт преображается, превращаясь из неповоротливого автобуса в гоночный болид. Его огромным преимуществом является огромный набор возможностей, как то несколько вариантов кеширования (статика, акселераторы, Memcached, запросы к базе данных, объектное и страничное кеширование), конкатенация и минификация кода (объединение и сжатие файлов CSS, Javascript, сжатие HTML за счёт удаления пробелов), использование CDN и многое другое.
  • Что тут скажешь — используйте правильные CMS, и качественное кеширование будет доступно практически из коробки.

    Кеширование на стороне браузера (клиента), заголовки кеширования

    Кеширование в браузере возможно потому, что любой уважающий себя браузер это позволяет и поощряет. Возможно это благодаря HTTP заголовкам , которые сервер отдаёт клиенту, а именно:

    • Expires;
    • Cache-Control: max-age;
    • Last-Modified;
    • ETag.

    Благодаря им пользователи, которые неоднократно заходят на сайт, тратят крайне мало времени на загрузку страниц. Заголовки кеширования должны применяться ко всем кешируемым статическим ресурсам: файлы шаблона, картинок, файлы javascript и css, если есть, PDF, аудио и видео, и так далее.
    Рекомендуется выставлять заголовки так, чтобы статика хранилась не менее недели и не более года, лучше всего год.

    Expires

    Заголовок Expires отвечает за то, как долго кеш является актуальным, и браузер может использовать кешированные ресурсы, не запрашивая у сервера их новую версию. Является сильным и крайне желательным к использованию, так как действует в обязательном порядке. В заголовке рекомендуется указывать срок от недели до года. Больше года лучше не указывать, это является нарушением правил RFC.

    Например, чтобы настроить Expires в NGINX для всех статических файлов на год (365 дней), в конфигурационном файле NGINX должен присутствовать код

    Location ~* ^.+\.(jpg|jpeg|gif|png|svg|js|css|mp3|ogg|mpe?g|avi|zip|gz|bz2?|rar|swf)$ { expires 365d; }

    Cache-Control: max-age;

    Cache-Control: max-age отвечает за то же самое.
    Более предпочтительно использование Expires, нежели Cache-Control ввиду большей распространённости. Однако, если Expires и Cache-Control будут присутствовать в заголовках одновременно, то приоритет будет отдан Cache-Control.

    В NGINX Cache-Control включается так же, как и Expires , директивой expires: 365d;

    Last-Modified и ETag

    Эти заголовки работают по принципу цифровых отпечатков. Это означает, что для каждого адреса URL в кеше будет устанавливаться свой уникальный id. Last-Modified создаёт его на основе даты последнего изменения. Заголовок ETag использует любой уникальный идентификатор ресурса (чаще всего это версия файла или хеш контента). Last-Modified – «слабый» заголовок, так как браузер применяет эвристические алгоритмы, чтобы определить, запрашивать ли элемент из кеша.

    В NGINX для статичных файлов ETag и Last-Modified включены по умолчанию. Для динамических страниц их либо лучше не указывать, либо это должен делать скрипт, генерирующий страницу, либо, что лучше всего, использовать правильно настроенный кеш, тогда NGINX сам позаботится о заголовках. Например, для WordPress, вы можете воспользоваться .

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

    Одновременное использование Expires и Cache-Control: max-age избыточно, так же, как избыточно одновременное использование Last-Modified и ETag. Используйте в связке Expires + ETag либо Expires + Last-Modified.

    Включить GZIP сжатие для статичных файлов

    Конечно, сжатие GZIP не относится к кешированию как таковому напрямую, однако, весьма экономит трафик и увеличивает скорость загрузки страниц.

    Как включить GZIP для статики в server { .... gzip on; gzip_disable "msie6"; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript; } Как включить GZIP для статики в Чтобы включить сжатие gzip в.htaccess, нужно в начало файла вставить следующий код: AddOutputFilterByType DEFLATE text/plain AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE application/xml AddOutputFilterByType DEFLATE application/xhtml+xml AddOutputFilterByType DEFLATE application/rss+xml AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE application/x-javascript

    |

    Nginx включает в себя модуль FastCGI, который позволяет использовать директивы для кэширования динамического контента в интерфейсе PHP. FastCGI устраняет необходимость искать дополнительные решения для кэширования страниц (например, обратные прокси или специальные плагины приложений). Контент также может быть исключен из кэширования на основе метода запроса, URL, cookies или любой другой переменной сервера.

    Активация кэширования FastCGI

    Чтобы следовать данному руководству, нужно заранее . Также нужно отредактировать конфигурационный файл виртуального хоста:

    nano /etc/nginx/sites-enabled/vhost

    Внесите следующие строки в начало файла вне директивы server { } :

    Директива fastcgi_cache_path задает путь к кэшу (/etc/nginx/cache), указывает его размер (100m), имя зоны памяти (MYAPP), уровни подкаталогов и таймер inactive.

    Кэш можно размещать в любой удобной точке жесткого диска. Максимальный размер кеша не должен превышать RAM сервера+размер swap-файла; в противном случае будет выведена ошибка Cannot allocate memory. Если кэш не был использован в течение конкретного периода времени, указанного с помощью опции «inactive» ​​(в данном случае это 60 минут), то Nginx удаляет его.

    Директива fastcgi_cache_key указывает способ хеширования имен файлов. Согласно данным настройкам, Nginx будет шифровать файлы с помощью MD5.

    Теперь можно перейти к директиве location, которая передает PHP-запросы модулю php5-fpm. В location ~ .php$ { } внесите следующие строки:

    fastcgi_cache MYAPP;
    fastcgi_cache_valid 200 60m;

    Директива fastcgi_cache ссылается на зону памяти, которая уже была указана в директиве fastcgi_cache_path.

    По умолчанию Nginx хранит кешированные объекты в течение времени, указанного с помощью одного из этих заголовков:

    X-Accel-Expires
    Expires
    Cache-Control.

    Директива fastcgi_cache_valid указывает срок хранения кэша по умолчанию, если ни одного из этих заголовков нет. Согласно установленному значению кэшируются только ответы с кодом состояния 200 (конечно, можно указать и другие коды состояния).

    Проверьте настройки FastCGI

    service nginx configtest

    Затем перезапустите Nginx, если с настройками все в порядке.

    service nginx reload

    На данном этапе файл vhost должен иметь следующий вид:

    fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=MYAPP:100m inactive=60m;
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
    server {
    listen 80;
    root /usr/share/nginx/html;
    index index.php index.html index.htm;
    server_name example.com;
    location / {
    try_files $uri $uri/ /index.html;
    }
    location ~ \.php$ {
    try_files $uri =404;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_cache MYAPP;
    fastcgi_cache_valid 200 60m;
    }
    }

    Теперь нужно проверить, работает ли кеширование.

    Проверка кэширования FastCGI

    Создайте PHP-файл, который выводит метку времени UNIX.

    /usr/share/nginx/html/time.php

    Внесите в файл:

    Затем несколько раз запросите данный файл через curl или веб-браузер.

    root@server:~# curl http://localhost/time.php;echo
    1382986152

    1382986152
    root@server:~# curl http://localhost/time.php;echo
    1382986152

    Если кеширование выполняется должным образом, временная отметка всех запросов будет совпадать (поскольку ответ был кеширован).

    Чтобы найти кэш этого запроса, нужно выполнить обратную запись кэша

    root@server:~# ls -lR /etc/nginx/cache/
    /etc/nginx/cache/:
    total 0
    drwx------ 3 www-data www-data 60 Oct 28 18:53 e
    /etc/nginx/cache/e:
    total 0
    drwx------ 2 www-data www-data 60 Oct 28 18:53 18
    /etc/nginx/cache/e/18:
    total 4
    -rw------- 1 www-data www-data 117 Oct 28 18:53

    Можно также добавить заголовок X-Cache, который укажет, что данный запрос был обработан из кеша (X-Cache HIT) или напрямую (X-Cache MISS).

    Над директивой server { } внесите:

    add_header X-Cache $upstream_cache_status;

    Перезапустите сервис Nginx и выполните подробный запрос с помощью curl, чтобы увидеть новый заголовок.

    root@server:~# curl -v http://localhost/time.php
    * About to connect() to localhost port 80 (#0)
    * Trying 127.0.0.1...
    * connected
    * Connected to localhost (127.0.0.1) port 80 (#0)
    > GET /time.php HTTP/1.1
    > User-Agent: curl/7.26.0
    > Host: localhost
    > Accept: */*
    >
    * HTTP 1.1 or later with persistent connection, pipelining supported
    < HTTP/1.1 200 OK
    < Server: nginx
    < Date: Tue, 29 Oct 2013 11:24:04 GMT
    < Content-Type: text/html
    < Transfer-Encoding: chunked
    < Connection: keep-alive
    < X-Cache: HIT

    Отправьте POST-запрос на этот файл с URL, который нужно очистить.

    curl -d "url=http://www.example.com/time.php" http://localhost/purge.php

    Скрипт выдаст true или false в зависимости от того, был очищен кш или нет. Обязательно исключите этот скрипт из кэширования, а также не забудьте ограничить доступ к нему.

    Tags: ,

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

    Т.е. клиент обращается к странице сайта — сервер обрабатывает запрос, сгенерированная страница отправляется клиенту вместе с определенным заголовком. Браузер сохраняет информацию локально и при повторном запросе самостоятельно отдает ее.

    Кэшируются изображения стили CSS и Javascript. Кэширование браузера Nginx реализуется за счет добавления заголовка Cache-control .

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

    Кэширование браузера Nginx

    В конфигурационном файле Nginx кэширование JS/CSS включается следующим образом (добавлены и другие расширения — на практике лучше кэшировать их все):

    server {

    location ~* \.(jpg|jpeg|gif|png|ico|css|bmp|swf|js|html|txt)$ {
    expires max;
    root /home/website/example.com/;
    }

    }

    expires max говорит о том, что TTL устанавливается в бесконечность и в случае если файлы на сервере будут изменены клиент об этом никогда не узнает поскольку повторный запрос отправлен не будет.

    expires (об этом заголовке речь пойдет ниже) определяет когда брузер обновит кэш, значение устанавливается в секундах.

    Обычно в конфиге сервера устанавливается именно значение expires max, затем в приложении при подключении css и js файлов определяются их версии, которые должны меняться каждый раз при обновлении содержимого.

    Указание заголовков, задающих кэширование на уровне приложения

    Сервер в этом случае будет воспринимать каждую новую версию как добавленный новый файл и будет кэшировать его.

    Вместе с Cache-Control часто указывается заголовок Expires — он принудительно задает дату и время, когда браузер сбросит существующий кэш; при следующем обращении к пользователя обновленные данные будут загружены в кэш повторно.

    Дополнительный заголовок HTTP Expires указывает дату и время, когда браузер должен обновить кэш (заголовки можно использовать совместно, Expires при использовании обоих заголовков имеет меньшее значение):

    Оба заголовка могут быть заданы в программном коде на уровне приложения.

    Включение кэширования в РНР

    Большая часть веб-проектов пишутся на языке РНР, в РНР HTTP заголовки Cache-control и Expires задаются следующим образом: