Сжатие контента · 9 min read · Sep 15, 2025
mod_gzip - обслуживание сжатого контента веб-сервером Apache - Страница 9
Автор: Майкл Шрёпл
Сжатие через согласование
Использование настраиваемой функции сжатия, такой как mod_gzip, в конечном итоге всегда должно быть каким-то образом согласованием контента, т.е. обслуживанием различного контента условно для одного и того же запрашиваемого URL, в зависимости от конкретной информации внутри HTTP-заголовков.
С другой стороны, HTTP позволяет временное хранение ответов на HTTP-запросы в кэшах, особенно при использовании прокси-серверов. Если теперь
- HTTP-клиент отправляет запрос,
- соответствующий ответ обслуживается в сжатом виде и хранится некоторым прокси,
- затем другой HTTP-клиент отправляет запрос на указанный URL,
то прокси-сервер, не обладая дополнительной информацией, сталкивается с проблемой:
- Имеет ли он право обслуживать кэшированный контент этому второму HTTP-клиенту, или
- должен ли он перенаправить запрос на HTTP-сервер?
Поскольку только HTTP-сервер в конечном итоге может выяснить (на основе своей конфигурации, содержащей соответствующие правила фильтрации), может ли второй HTTP-клиент также получить сжатые данные ответа.
Кстати, это не эффект использования только процедуры сжатия, а общая проблема кэширования HTTP-данных, содержание которых не может быть однозначно определено URL внутри кэша прокси-сервера или аналогичных серверов памяти, оснащенных в транспортном маршруте. Это включает процедуры согласования любого типа, а также отправку дополнительной информации в HTTP-заголовках, такой как данные аутентификации или куки.
Требования к производительности
Конечно, можно попытаться избежать проблемы, явно отказавшись кэшировать данные соответствующего ответа (используя соответствующие HTTP-заголовки Expires: и Pragma: в HTTP/1.0 и Cache-Control: в HTTP/1.1) для всех прокси-серверов, существующих на пути между клиентом и сервером.
Но цель сжатия - ускорить передачу данных (уменьшая объем данных) - и кэширование данных служит той же цели (уменьшая обращения к HTTP-серверу). И одна оптимизация производительности не должна приводить к тому, что другая становится непригодной, особенно поскольку эти два процесса не заменяют друг друга, а могут эффективно дополнять друг друга в рассматриваемом случае.
Информация о параметрах согласования
Спецификация HTTP содержит определение Vary HTTP-заголовка, где HTTP-сервер может информировать прокси-сервер о
- был ли ответ уникальным результатом запроса URL или
- могут ли другие атрибуты запроса для того же URL привести к различным результатам.
Его значение может содержать список имен других HTTP-заголовков, содержание которых было актуально для обслуживания именно этого ответа на запрос. Таким образом, HTTP-сервер может даже информировать прокси-сервер о каких HTTP-заголовках повлияло на решение о предоставляемом контенте.
Когда прокси-сервер перенаправляет запрос на HTTP-сервер и хочет позже сохранить ответ в своем кэше, он все еще должен иметь HTTP-заголовки оригинального запроса, когда ответ HTTP-сервера приходит.
Теперь, если HTTP-сервер помечает условный контент ответа соответствующим Vary заголовком, то
- прокси-сервер должен хранить в своем кэше не только содержание этого ответа, но и всю соответствующую информацию о HTTP-заголовках (имена которых были перечислены в списке значений Vary HTTP-заголовка ответа) из запроса, и
- он не должен обслуживать этот кэшированный контент в качестве ответа на дальнейшие запросы, если информация соответствующих HTTP-заголовков такого последующего запроса хотя бы “совпадает” с теми, что были в оригинальном запросе, т.е. является семантически идентичной значениям оригинального запроса для каждого из этих заголовков.
Результирующие ограничения для HTTP-сервера
Предыдущие объяснения показали, как прокси-сервер может правильно обрабатывать условную доставку HTTP-ответов (являющихся результатом согласования контента) и с максимальным использованием его эффекта кэширования одновременно - при условии, что
- HTTP-сервер предоставляет прокси-серверу достаточную информацию о параметрах согласования и
- прокси-сервер располагает соответствующей информацией в случае последующего запроса на тот же URL от другого HTTP-клиента.
Но последнее означает ограничение для степеней свободы процесса согласования. Потому что если прокси-сервер должен решать, может ли он обслуживать свой кэшированный контент или нет исключительно на основе информации в HTTP-запросе, то правила согласования HTTP-серверов не должны ссылаться ни на что, кроме содержания HTTP-заголовков!
Но, к сожалению, это предварительное условие не выполняется mod_gzip, поскольку из шести классов правил фильтрации,
- два “законных” ( reqheader и uri) исключительно ссылаются на содержание HTTP-заголовков, но
- четыре других “незаконных” ( rspheader, handler, file и mime) ссылаются на информацию, которая будет доступна только во время оценки запроса HTTP-сервером.
Таким образом, если сервер с поддержкой mod_gzip использует одно из этих “незаконных” правил фильтрации, то прокси-сервер не сможет больше правильно решить о применимости своего кэшированного контента для ответа на дальнейшие запросы.
В этом случае прокси-серверу также не сильно поможет, если mod_gzip уведомит прокси-сервер о том, что он явно перегружен (предоставив полный список классов правил фильтрации, значимых для этого запроса, внутри какого-то Vary: заголовка, если это вообще было бы законно). Все, что прокси-сервер мог бы сделать, это использовать появление одного из этих четырех “незаконных” классов правил фильтрации в качестве критерия для отказа от кэширования содержания ответа.
Это само по себе не было бы так уж плохо - пока HTTP-сервер ограничивает себя использованием только “законных” правил, он сможет оптимально сотрудничать с прокси-сервером.
Но, к сожалению, сделать это невозможно с mod_gzip 1.3.19.1a.
Встраивание mod_gzip 1.3.19.1a в архитектуру Apache 1.3 осуществляется относительно сложным образом:
- На этапе обработки 1 mod_gzip проверяет, должен ли он вообще интересоваться обработкой результатов этого запроса и подготовиться к этому - на основе правил четырех классов ( reqheader, uri, file и handler, т.е. два “законных” и два “незаконных” класса правил)
- На этапе обработки 2 mod_gzip проверяет, должен ли он теперь фактически сжать (теперь доступное) содержание ответа - на основе правил двух классов ( rspheader и mime, оба “незаконные” классы правил).
Для успешного разрешения запроса на сжатие требуется выполнение хотя бы одного include правила из любой из обеих фаз (и невыполнение всех exclude правил).
Но поскольку оба include класса правил из фазы 2 являются “незаконными”, каждый список соответствующих классов правил фильтрации для успешного сжатия в текущей реализации mod_gzip должен по крайней мере охватывать один “незаконный” класс правила.
Таким образом, невозможно предоставить прокси-серверу информацию, которую он может использовать для принятия решения о применимости какого-либо кэшированного контента - предоставленная информация всегда будет превышать понимание прокси-сервера.
Vary заголовки в mod_gzip 1.3.19.2a и выше
Начиная с версии 1.3.19.2a, mod_gzip отправляет Vary: заголовки - для каждого запроса, в котором модуль был задействован хотя бы один раз (независимо от того, были ли обслужены сжатые данные или нет).
На этом этапе исследований для mod_gzip каждый запрос (независимо от того, был ли ответ фактически обслужен в сжатом виде) потенциально является согласованием:
- по крайней мере о Accept-Encoding HTTP-заголовке, и
- возможно, о других HTTP-заголовках также (а именно все те, которые встречаются в правилах фильтрации класса reqheader)
На данный момент mod_gzip еще не способен генерировать наилучший возможный, т.е. минимальный набор необходимых Vary: заголовков - для этого необходимо полностью переписать процедуру оценки правил mod_gzip.
В качестве первого шага модуль с версии 1.3.19.2a отправляет Vary: заголовок, который содержит
- значение Accept-Encoding, а также
- имена каждого заголовка, используемого в любых правилах reqheader,
поскольку каждое из этих правил может повлиять на результат согласования, и в каждом из этих случаев результат будет зависеть от значений полученных HTTP-заголовков. В некоторых случаях это может быть слишком много (и тогда значительно затруднить эффективное кэширование контента), но, по крайней мере, это что-то, с чего можно начать.
В качестве улучшения этой стратегии mod_gzip 1.3.26.1a не отправляет Vary: заголовок, если сжатие запрашиваемого запроса было отклонено из-за правила mod_gzip_item_exclude типа
file,
uri или
handler
поскольку оценка этого правила не могла зависеть от полученных HTTP-заголовков, и поэтому в этих случаях фактически никакое согласование (по параметрам, которые могут содержать различные значения для различных HTTP-запросов) не имело места.
Если вы хотите, чтобы Vary: заголовки не отправлялись для файлов, которые вы уверены, что никогда не будут обслуживаться в сжатом виде из-за других конфигурационных правил, вам нужно отключить mod_gzip для этих файлов.
Пример того, как не отправлять Vary: заголовки для GIF-изображений, которые могут быть закэшированы некоторым прокси, как Squid 2.4, может выглядеть так:
mod_gzip_on No
Для будущих версий остаются открытыми следующие задачи:
- Признание во всех возможных случаях, что реакция на текущий запрос никогда не может вызвать обслуживание сжатых данных, потому что срабатывает какое-то правило mod_gzip_item_exclude, независимое от атрибутов запроса.
- Признание того, что произошло какое-то согласование, которое не может быть описано списком имен HTTP-заголовков - в этом случае Vary: * должен быть отправлен (и документация для mod_gzip должна явно указывать, что эти директивы следует использовать только в случае крайней необходимости, так как их использование будет иметь негативный эффект на работу кэшированных прокси).
- Двойная проверка, возможны ли конфигурации, при которых требуется только некоторый подмножество имен заголовков из всех правил reqheader в Vary: заголовке - чем меньше имен, тем меньше вариантов должно храниться в кэше прокси параллельно.
Согласование по другим параметрам, чем HTTP-заголовки
В очень особых случаях, т.е. при использовании определенных директив конфигурации, mod_gzip выполняет согласование по параметрам, которые даже не могут быть выражены в терминах имен HTTP-заголовков. Это относится к директивам
- mod_gzip_min_http (минимальная требуемая версия HTTP) а также
- mod_gzip_handle_methods (HTTP-методы, которые следует обрабатывать)
В обоих случаях mod_gzip не может объяснить прокси, что было сделано, сообщив имена HTTP-заголовков. Соответствующая реакция в соответствии со спецификацией HTTP/1.1 - отправка Vary: * HTTP-заголовка.
mod_gzip 1.3.26.1a отправляет Vary: ** заголовок, если была использована директива mod_gzip_min_http*.
Что касается директивы mod_gzip_handle_methods, в настоящее время, похоже, еще не совсем ясно, запрашивают ли два HTTP-запроса для одного и того же URI, но с использованием различных HTTP-методов, фактически одну и ту же HTTP-сущность - это решит, нужно ли будет отправлять Vary: * заголовок при использовании этой директивы, и это будет вопрос, который нужно решить в будущих релизах.
Но поскольку в этом случае прокси-сервер не может понять тип выполненного согласования, он не имеет права хранить ответы, несущие этот знак, в каком-либо кэше.
Таким образом, использование одной из этих директив полностью отключает кэширование прокси для каждого ответа, отправленного этим HTTP-сервером, независимо от того, в сжатом или несжатом виде. Поэтому мы советуем вам больше не использовать одну из этих директив.
UserAgent как особый случай
Хранение вариантов различных параметров согласования параллельно в кэше прокси может быть разумным, если фактически может возникнуть лишь несколько возможных значений - например, в случае Content-Encoding. Если существует большое количество возможных значений, то параллельное хранение вариантов больше не является целесообразным.
Это как раз относится к имени UserAgent как идентификатору HTTP-клиента. Каждая подсборка браузера отправляет сложную строку UserAgent, которая содержит не только имя и версию браузера, но и дополнительную информацию (национальный язык, имя и версию операционной системы и т.д.). Существует сотни известных строк UserAgent - и помимо этого множество механизмов для манипуляции этой строкой UserAgent. Некоторые браузеры (например, Opera) даже позволяют пользователю явно выбирать содержание этих строк UserAgent, чтобы выдать себя за другой браузер (поскольку многие технически некомпетентные создатели веб-страниц строят свои сайты на основе имени браузера и ненужным образом исключают некоторые браузеры из него, или просто потому, что их пользователь не хочет ненужным образом показывать детали о своем компьютерном оборудовании, ради сохранения своей конфиденциальности).
Насколько разумно обслуживать сжатые веб-страницы условно в зависимости от идентичности HTTP-клиента в некоторых случаях (например, в отношении многочисленных ошибок Netscape 4), все же недостатком использования строк UserAgent в качестве основы для HTTP-согласования будет то, что содержание этого HTTP-заголовка, с одной стороны, слишком варьируется, чтобы делать надежные выводы, а с другой стороны, содержит слишком много различных значений, чтобы какой-либо кэшированный прокси когда-либо мог хранить параллельно результаты запросов для одного и того же URL для всех этих вариантов согласования.
Начиная с версии 1.3.19.2a, mod_gzip отправляет Vary: заголовок, описывающий HTTP-заголовок User-Agent: как параметр согласования, если в конфигурации была использована соответствующая директива. Но вероятность того, что последующий запрос будет содержать точно идентичное значение User-Agent: (так что этот клиент может, следовательно, получить уже сохраненное содержание) очень низка.
На самом деле HTTP-сервер будет обрабатывать даже большие наборы UserAgents (которые предполагаются функционально эквивалентными из-за его конфигурации) одинаково во время согласования - но Vary: заголовок не позволяет HTTP-серверу сообщить кэшированному прокси, какие части строк UserAgent были оценены HTTP-сервером как значительное содержание во время согласования. Прокси-сервер может только узнать, что UserAgent играл некую роль - и, осознавая это, прокси должен рассматривать отдельные UserAgents как разные, даже если HTTP-сервер не будет действовать так.
Таким образом, использование правил фильтрации, оценивающих HTTP-заголовок UserAgent, приведет к полному отключению любого кэширования для ответных пакетов, созданных таким образом. Пользователь mod_gzip должен быть абсолютно осведомлен об этом эффекте - и поэтому использовать другие методы фильтрации (с меньшим количеством возможных различных значений), если это вообще возможно, чтобы обеспечить тот же тип дифференциации между этими HTTP-клиентами.
Оригинальное местоположение этого документа:
Get new posts in your inbox
No spam. Unsubscribe anytime.