Перейти к содержанию

dynamic-etag: Модуль NGINX для добавления ETag к динамическому контенту

Установка

Вы можете установить этот модуль в любом дистрибутиве на базе RHEL, включая, но не ограничиваясь:

  • RedHat Enterprise Linux 7, 8, 9 и 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 и Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-dynamic-etag
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-dynamic-etag

Включите модуль, добавив следующее в начало файла /etc/nginx/nginx.conf:

load_module modules/ngx_http_dynamic_etag_module.so;

Этот документ описывает nginx-module-dynamic-etag v0.2.3, выпущенный 18 сентября 2025 года.


Coverity Scan Buy Me a Coffee

Этот модуль NGINX наделяет ваш динамический контент автоматическим заголовком ETag. Он позволяет клиентским браузерам отправлять условные GET запросы к динамическим страницам. Таким образом, это экономит пропускную способность и обеспечивает лучшую производительность!

Предостережения сначала!

Этот модуль является настоящим хаком: он вызывает фильтр заголовков из фильтра тела и т.д.

Оригинальный автор отказался от него, сказав:

Он никогда не работал должным образом.

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

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

Сказав это, тесты, которые я добавил, показывают, что вся эта штука работает!

Обратите внимание, что запросы HEAD не будут возвращать никакой ETag, потому что у нас нет данных для обработки, так как NGINX справедливо отбрасывает тело для этого метода запроса.

Считайте это особенностью или ошибкой :-) Если мы уберем это, то все запросы HEAD будут иметь один и тот же ETag (хэш на пустоту), что определенно хуже.

Таким образом, убедитесь, что вы проверяете заголовки вот так:

curl -IL -X GET https://www.example.com/

А не так:

curl -IL https://www.example.com/

Еще стоит упомянуть, что нет особого смысла применять динамический ETag на странице, которая изменяется при каждом перезагрузке. Например, я обнаружил, что не использую динамический ETag с пользой из-за <?= antispambot(get_option('admin_email')) ?> в header.php моей темы WordPress, так как в этой функции:

выборка случайная и меняется каждый раз, когда функция вызывается

Чтобы быстро проверить, меняется ли ваша страница при перезагрузке, используйте:

diff <(curl http://www.example.com) <(curl http://www.example.com)

Теперь, когда мы закончили с "теперь вы знаете" и т.д., вы можете продолжить пробовать эту штуку :)

Синопсис

http {
    server {
        location ~ \.php$ {
            dynamic_etag on;
            fastcgi_pass ...;
        }
    }
}

Директивы конфигурации

dynamic_etag

  • синтаксис: dynamic_etag on|off|$var
  • по умолчанию: off
  • контекст: http, server, location

Включает или отключает автоматическое применение ETag.

dynamic_etag_types

  • синтаксис: dynamic_etag_types <mime_type> [..]
  • по умолчанию: text/html
  • контекст: http, server, location

Включает автоматическое применение ETag для указанных MIME типов в дополнение к text/html. Специальное значение * соответствует любому MIME типу. Ответы с MIME типом text/html всегда включаются.

dynamic_etag_strength

  • синтаксис: dynamic_etag_strength strong|weak|$var
  • по умолчанию: strong
  • контекст: http, server, location

Контролирует, являются ли сгенерированные ETag сильными или слабыми. Слабые ETag полезны для динамического контента, где семантическое равенство должно учитываться, даже если байты различаются (например, временные метки, рандомизированные атрибуты). При использовании $var сопоставьте со значениями strong или weak.

Примечание: Эти директивы недействительны в контексте if. Предпочитайте использовать $var с map, чтобы добиться условного поведения.

Пример с map:

map $arg_w $etag_strength {
    default strong;
    1       weak;
}

location /example {
    dynamic_etag on;
    dynamic_etag_types text/html;
    dynamic_etag_strength $etag_strength;
    proxy_pass http://backend;
}

Советы

Вы можете использовать директиву map для условного включения динамического ETag на основе URL, например:

map $request_uri $dyn_etag {
    default "off";
    /foo "on";
    /bar "on";
}
server {
   ...
   location / {
       dynamic_etag $dyn_etag;
       fastcgi_pass ...
   }
}

README оригинального автора

Попытка обработки ETag / If-None-Match на проксированном контенте.

Я планирую использовать это для фронтенда сервера Varnish, использующего много ESI.

Это работает, но... имейте в виду, что это моя первая попытка разработки плагина для nginx, и работа с заголовками после прочтения тела не была именно в инструкции.

Любые комментарии и/или улучшения и/или форки приветствуются.

Спасибо http://github.com/kkung/nginx-static-etags/ за... вдохновение.

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-dynamic-etag.