Compressão HTTP · 12 min read · Sep 15, 2025

mod_gzip - servindo conteúdo comprimido pelo servidor web Apache - Página 9

Autor: Michael Schröpl

Compressão via negociação

Usar uma função de compressão configurável como o mod_gzip deve sempre ser algum tipo de negociação de conteúdo, ou seja, servir diferentes conteúdos condicionalmente para a mesma URL solicitada, dependendo de informações específicas dentro dos cabeçalhos HTTP.

Por outro lado, o HTTP permite o armazenamento temporário de respostas a solicitações HTTP em caches, especialmente ao usar servidores proxy. Se agora

  1. um cliente HTTP envia uma solicitação,
  2. a resposta correspondente é servida em forma comprimida e armazenada por algum proxy e
  3. posteriormente, outro cliente HTTP envia uma solicitação para a URL em questão,

então o servidor proxy - não possuindo mais informações - tem um problema:

  • Tem o direito de servir o conteúdo em cache para este segundo cliente HTTP também, ou
  • deve encaminhar a solicitação para o servidor HTTP?

Pois apenas o servidor HTTP pode, em última análise, descobrir (com base em sua configuração contendo as regras de filtro correspondentes) se o segundo cliente HTTP pode receber dados de resposta comprimidos também.

A propósito, isso não é um efeito de usar um procedimento de compressão sozinho, mas um problema geral sobre o armazenamento em cache de dados HTTP cujo conteúdo não pode ser especificado de forma inequívoca por uma URL dentro do cache de um servidor proxy ou servidores semelhantes equipados com memória ao longo da rota de transporte. Isso inclui procedimentos de negociação de qualquer tipo, bem como o envio de informações adicionais nos cabeçalhos HTTP, como dados de autenticação ou cookies.

Requisitos de desempenho

Claro que se pode tentar evitar o problema negando explicitamente armazenar os dados da resposta correspondente (usando os cabeçalhos HTTP correspondentes Expires: e Pragma: no HTTP/1.0 e Cache-Control: no HTTP/1.1) para todos os servidores proxy existentes no caminho entre o cliente e o servidor.

Mas o objetivo da compressão é acelerar a transferência de dados (reduzindo o volume de dados) - e armazenar dados em cache serve ao mesmo objetivo (reduzindo acessos ao servidor HTTP). E uma otimização de desempenho não deve levar a outra que não seja mais utilizável, especialmente porque essas duas não se substituem, mas podem efetivamente se complementar no caso em questão.

Informações sobre parâmetros de negociação

A especificação HTTP contém a definição do Vary cabeçalho HTTP onde o servidor HTTP pode informar ao servidor proxy sobre

  • se a resposta foi o resultado único de uma solicitação de URL ou
  • se outros atributos de uma solicitação para a mesma URL poderiam levar a resultados diferentes.

Seu valor pode conter uma lista de nomes de outros cabeçalhos HTTP cujo conteúdo foi relevante para servir esta resposta específica para uma solicitação. Assim, o servidor HTTP pode até informar ao servidor proxy sobre quais cabeçalhos HTTP influenciaram a decisão sobre o conteúdo servido.

Quando um servidor proxy encaminha uma solicitação para um servidor HTTP e deseja armazenar a resposta em seu cache mais tarde, ele ainda deve estar na posse dos cabeçalhos HTTP da solicitação original quando a resposta do servidor HTTP chega.

Agora, se o servidor HTTP marca um conteúdo condicional de uma resposta pelo cabeçalho correspondente Vary, então

  • o servidor proxy deve armazenar em seu cache não apenas o conteúdo desta resposta, mas todas as informações relevantes dos cabeçalhos HTTP (cujos nomes foram enumerados na lista de valores do cabeçalho Vary da resposta) da solicitação também, e
  • não deve servir este conteúdo em cache como resposta a novas solicitações, a menos que as informações dos cabeçalhos HTTP correspondentes de tal solicitação subsequente pelo menos ‘correspondam’ às da solicitação original, ou seja, sejam semanticamente idênticas aos valores da solicitação original para cada um desses cabeçalhos.

Restrições resultantes para o servidor HTTP

As explicações anteriores mostraram como um servidor proxy pode lidar corretamente com a entrega condicional de respostas HTTP (sendo o resultado de uma Negociação de Conteúdo) e com a máxima utilização de seu efeito de cache ao mesmo tempo - assumindo que

  • o servidor HTTP fornece ao servidor proxy informações suficientes sobre os parâmetros de negociação e
  • o servidor proxy está na posse das informações correspondentes em caso de uma solicitação subsequente para a mesma URL por outro cliente HTTP.

Mas o último significa agora uma restrição para os graus de liberdade do processo de negociação. Pois se o servidor proxy deve decidir se pode servir seu conteúdo em cache ou não exclusivamente com base em informações dentro de uma solicitação HTTP, então as regras de negociação dos servidores HTTP não devem se referir a nada além do conteúdo dos cabeçalhos HTTP!

Mas, infelizmente, essa pré-condição não é cumprida pelo mod_gzip, pois das seis classes de regras de filtro fornecidas

  • duas ‘legais’ (reqheader e uri) referem-se exclusivamente ao conteúdo dos cabeçalhos HTTP, mas
  • quatro outras ‘ilegais’ (rspheader, handler, file e mime) referem-se a informações que estarão disponíveis apenas durante a avaliação da solicitação pelo servidor HTTP.

Portanto, se um servidor aprimorado com mod_gzip usa uma dessas regras de filtro ‘ilegais’, então o servidor proxy não pode mais ser capaz de decidir corretamente sobre a aplicabilidade de seu conteúdo em cache para responder a novas solicitações.

Fazer isso não ajuda muito o servidor proxy se o mod_gzip notificasse o servidor proxy sobre estar evidentemente sobrecarregado (fornecendo uma lista completa das classes de regras de filtro significativas para esta solicitação dentro de algum cabeçalho Vary: se isso fosse até legal). Tudo o que o servidor proxy poderia fazer seria usar a ocorrência de uma dessas quatro classes de regras de filtro ‘ilegais’ como critério para não armazenar o conteúdo da resposta.

Isso por si só não seria tão ruim - desde que o servidor HTTP se limite a usar nada além de regras ‘legais’, ele seria capaz de cooperar de forma otimizada com um servidor proxy.

Mas, infelizmente, fazer isso é impossível com mod_gzip 1.3.19.1a.

A incorporação do mod_gzip 1.3.19.1a na arquitetura Apache 1.3 é feita de uma maneira relativamente complexa:

  • Na fase de processamento 1, o mod_gzip verifica se deve estar interessado em lidar com os resultados desta solicitação e se preparar para isso - com base nas regras de quatro classes (reqheader, uri, file e handler, ou seja, duas classes de regras ‘legais’ e duas ‘ilegais’)
  • Na fase de processamento 2, o mod_gzip verifica se agora deve realmente comprimir o conteúdo da resposta (agora disponível) - com base nas regras de duas classes (rspheader e mime, ambas classes de regras ‘ilegais’).

Para a permissão bem-sucedida de uma solicitação de compressão, pelo menos o cumprimento de uma regra include de qualquer uma das duas fases é necessário (e o não cumprimento de todas as regras exclude).

Mas como ambas as classes de regras include da fase 2 são ‘ilegais’, cada lista de classes de regras de filtro relevantes para uma compressão bem-sucedida na implementação atual do mod_gzip deve pelo menos cobrir uma classe de regra ‘ilegal’.

Assim, é impossível fornecer a um servidor proxy informações que ele possa usar para decidir sobre a aplicabilidade de algum conteúdo em cache - as informações enviadas sempre ultrapassarão a compreensão do servidor proxy.

Cabeçalhos Vary no mod_gzip 1.3.19.2a e posteriores

A partir da versão 1.3.19.2a, o mod_gzip está enviando cabeçalhos Vary: - para cada e toda solicitação onde o módulo foi envolvido pelo menos uma vez (independentemente de os dados comprimidos terem sido servidos ou não).

Neste estado de pesquisa para o mod_gzip, cada solicitação (independentemente de a resposta ter sido realmente servida em forma comprimida) é potencialmente uma negociação:

  • pelo menos sobre o cabeçalho Accept-Encoding HTTP, e
  • possivelmente sobre outros cabeçalhos HTTP também (nomeadamente todos aqueles que ocorrem dentro das regras de filtro da classe reqheader)

Até agora, o mod_gzip ainda não é capaz de gerar o melhor possível, ou seja, o conjunto mínimo de cabeçalhos Vary: necessários - para isso seria necessário reescrever completamente o procedimento de avaliação de regras do mod_gzip.

Como primeiro passo, o módulo desde a versão 1.3.19.2a envia um cabeçalho Vary: que contém

  • o valor Accept-Encoding assim como
  • os nomes de cada cabeçalho sendo usado dentro de quaisquer regras reqheader,

porque cada uma dessas regras pode fazer a diferença para o resultado da negociação, e em cada um desses casos o resultado dependeria dos valores dos cabeçalhos HTTP recebidos. Em certos casos, isso pode ser excessivo (e então dificultar massivamente o armazenamento eficiente de conteúdo em cache), mas pelo menos é algo para começar.

Como uma melhoria a essa estratégia, o mod_gzip 1.3.26.1a não está enviando nenhum cabeçalho Vary: se a compressão da solicitação em questão foi recusada devido a uma regra mod_gzip_item_exclude do tipo

  • file,

  • uri ou

  • handler

  • uma vez que a avaliação dessa regra não poderia ter dependido dos cabeçalhos HTTP recebidos, e portanto, nesses casos, na verdade, nenhuma negociação (sobre dimensões que poderiam conter valores diferentes para diferentes solicitações HTTP) ocorreu.

Se você deseja que nenhum cabeçalho Vary: seja enviado para arquivos que você tem certeza de que nunca serão servidos em forma comprimida devido a outras regras de configuração, você deve desativar o mod_gzip para esses.

Um exemplo de não enviar cabeçalhos Vary: para imagens GIF que podem ser armazenadas em cache por algum proxy como o Squid 2.4 pode parecer assim:

  
 mod_gzip_on No  

Para versões futuras, as seguintes tarefas permanecem em aberto:

  • Reconhecer em todos os casos possíveis que a reação à solicitação atual nunca pode causar dados comprimidos a serem servidos porque alguma regra mod_gzip_item_exclude independente dos atributos da solicitação está sendo acionada.
  • Reconhecer que alguma negociação ocorreu que não pode ser descrita por uma lista de nomes de cabeçalhos HTTP - nesse caso, Vary: * deve ser enviado (e a documentação do mod_gzip deve apontar explicitamente que essas diretivas devem ser usadas apenas se absolutamente necessário, pois seu uso terá um efeito negativo no trabalho de proxies de cache).
  • Verificar se constelações são possíveis onde apenas algum subconjunto de nomes de cabeçalhos de todas as regras reqheader é necessário em um cabeçalho Vary: - quanto menos nomes houver, menos variantes precisam ser armazenadas no cache do proxy em paralelo.

Negociação sobre outras dimensões além dos cabeçalhos HTTP

Em casos muito especiais, ou seja, ao usar certas diretivas de configuração, alguma negociação é feita pelo mod_gzip sobre dimensões que não podem ser expressas nem mesmo em termos de nomes de cabeçalhos HTTP. Isso se aplica às diretivas

  • mod_gzip_min_http (versão mínima do HTTP necessária) assim como
  • mod_gzip_handle_methods (métodos HTTP a serem tratados)

Em ambos os casos, o mod_gzip não pode explicar a um proxy o que foi feito ao informar os nomes dos cabeçalhos HTTP. A reação apropriada de acordo com a especificação HTTP/1.1 é enviar o cabeçalho Vary: *.

O mod_gzip 1.3.26.1a está enviando um cabeçalho Vary: ** se a diretiva mod_gzip_min_http* foi usada.

Quanto à diretiva mod_gzip_handle_methods, atualmente parece não estar absolutamente claro se duas solicitações HTTP para a mesma URI, mas usando diferentes métodos HTTP, realmente pedem a mesma entidade HTTP - isso decidirá se um cabeçalho Vary: * terá que ser enviado ao usar essa diretiva também, e será uma questão a ser resolvida em lançamentos futuros.

Mas, como neste caso um servidor proxy não pode entender o tipo de negociação realizada, não está autorizado a armazenar respostas que tenham essa marca dentro de algum cache.

Assim, usar uma dessas diretivas desabilita completamente o cache proxy de cada uma e todas as respostas enviadas por este servidor HTTP, seja em forma comprimida ou não comprimida. Portanto, aconselhamos você a não usar uma dessas diretivas mais.

O UserAgent como caso especial

Armazenar variantes de diferentes parâmetros de negociação em paralelo em um cache proxy pode ser razoável se apenas alguns poucos valores possíveis puderem realmente ocorrer - como no caso de Content-Encoding. Se houver um grande número de valores possíveis, então o armazenamento paralelo de variantes não é mais viável.

Exatamente isso se aplica ao nome do UserAgent como identificação do cliente HTTP. Cada subversão de um navegador está enviando uma string UserAgent complexa que contém não apenas o nome e a versão dos navegadores, mas também outras informações (idioma nacional, nome e versão do sistema operacional etc.). Existem centenas de strings UserAgent conhecidas - e além disso, um número de mecanismos para manipular essa string UserAgent. Alguns navegadores (como o Opera) permitem até que o usuário selecione explicitamente o conteúdo dessas strings UserAgent para se passar por um navegador diferente (porque muitos criadores de páginas da web tecnicamente incompetentes constroem seu site com base no nome de um navegador e excluem desnecessariamente alguns navegadores dele, ou simplesmente porque seu usuário não quer mostrar desnecessariamente detalhes sobre o equipamento do computador que usa, para preservar sua privacidade).

Quão razoável servir páginas web comprimidas condicionalmente com base na identidade de um cliente HTTP pode ser em alguns casos (como em relação aos numerosos bugs do Netscape 4), ainda assim a desvantagem de usar as strings UserAgent como base de uma negociação HTTP será que o conteúdo desse cabeçalho HTTP, por um lado, é muito variável para se tirar conclusões confiáveis e, por outro lado, contém muitos valores diferentes para que qualquer proxy de cache possa algum dia ser capaz de manter em paralelo os resultados das solicitações para a mesma URL para todas essas variantes de negociação.

A partir da versão 1.3.19.2a, o mod_gzip está enviando um cabeçalho Vary: descrevendo o cabeçalho HTTP User-Agent: como parâmetro da negociação, se uma diretiva correspondente foi usada na configuração. Mas a probabilidade de uma solicitação sucessiva conter um valor User-Agent: exatamente idêntico (para que esse cliente possa, portanto, receber o conteúdo já armazenado) é muito baixa.

Na verdade, o servidor HTTP trataria até mesmo grandes conjuntos de UserAgents (que são considerados funcionalmente equivalentes devido à sua configuração) de forma idêntica durante a negociação - mas o cabeçalho Vary: não permite que o servidor HTTP informe ao proxy de cache quais partes das strings UserAgent foram avaliadas pelo servidor HTTP como conteúdo significativo durante a negociação. O servidor proxy só pode saber que o UserAgent teve algum papel - e, ciente disso, o proxy deve tratar UserAgents individuais como sendo diferentes, mesmo que o servidor HTTP não agisse assim.

Portanto, usar regras de filtro que avaliam o cabeçalho HTTP UserAgent levará a desabilitar totalmente qualquer cache para pacotes de resposta criados dessa forma. O usuário do mod_gzip deve estar absolutamente ciente desse efeito - e, portanto, usar outros métodos de filtro (com um número menor de valores possíveis diferentes) se possível, para fornecer o mesmo tipo de diferenciação entre esses Clientes HTTP.

Localização original deste documento:

http://www.schroepl.net/projekte/mod_gzip/cache.htm

Share: X/Twitter LinkedIn

Receba novas postagens na sua caixa de entrada

Sem spam. Cancele a assinatura a qualquer momento.