81

Google PageSpeed says I should "Specify a Vary: Accept-Encoding header" for JS and CSS. How do I do this in .htaccess?

StackOverflowNewbie
  • 35,023
  • 98
  • 252
  • 421
  • 22
    Not sure why this was closed, just because it doesn't involve a programming language does not mean it's not related to coding. – BlueRaja - Danny Pflughoeft May 22 '13 at 19:51
  • @BlueRaja-DannyPflughoeft I agree, this has been viewed over 65100 times so I think it's a well searched topic not to mention I answer htaccess questions everyday. – Panama Jack Jan 29 '14 at 03:46
  • I know this is hold as hell, but the header `Vary: Accept-Encoding` will tell the browser to store in cache a version of the page based on the `Content-Encoding` header. You should see http://stackoverflow.com/questions/1975416/trying-to-understand-the-vary-http-header and https://developers.google.com/speed/docs/best-practices/caching?hl=sv&csw=1#LeverageProxyCaching – Ismael Miguel Feb 13 '14 at 13:00
  • 2
    @IsmaelMiguel it will actually tell the client (and more important, any caching servers on the way), that the file differs for each variant of the client`s `Accept-Encoding` request header value, rather than each `Content-Encoding` variant of the server response header value. – aularon Apr 03 '14 at 16:26
  • @BlueRaja-DannyPflughoeft Because users with a lot of reputation and users seeking reputation build reputation by hunting down questions to close. Frustrating that it so often leads to questions being closed that shouldn't be. – Dan Nissenbaum Jan 08 '17 at 15:29

7 Answers7

90

I guess it's meant that you enable gzip compression for your css and js files, because that will enable the client to receive both gzip-encoded content and a plain content.

This is how to do it in apache2:

<IfModule mod_deflate.c>
    #The following line is enough for .js and .css
    AddOutputFilter DEFLATE js css

    #The following line also enables compression by file content type, for the following list of Content-Type:s
    AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml

    #The following lines are to avoid bugs with some browsers
    BrowserMatch ^Mozilla/4 gzip-only-text/html
    BrowserMatch ^Mozilla/4\.0[678] no-gzip
    BrowserMatch \bMSIE !no-gzip !gzip-only-text/html 
</IfModule>

And here's how to add the Vary Accept-Encoding header: [src]

<IfModule mod_headers.c>
  <FilesMatch "\.(js|css|xml|gz)$">
    Header append Vary: Accept-Encoding
  </FilesMatch>
</IfModule>

The Vary: header tells the that the content served for this url will vary according to the value of a certain request header. Here it says that it will serve different content for clients who say they Accept-Encoding: gzip, deflate (a request header), than the content served to clients that do not send this header. The main advantage of this, AFAIK, is to let intermediate caching proxies know they need to have two different versions of the same url because of such change.

aularon
  • 10,724
  • 3
  • 33
  • 41
  • I don't think this is it. My JS and CSS are already compressed. PageSpeed still complaining. – StackOverflowNewbie Sep 04 '10 at 06:56
  • 3
    I think mod_deflate is [supposed](http://httpd.apache.org/docs/2.0/mod/mod_deflate.html#proxies) to send the Vary header by default. – Matthew Flaschen Sep 04 '10 at 16:22
  • I have done what you have mentioned above. The .js files still are not being compressed. – Andy N May 23 '11 at 22:06
  • @Andy; maybe your server doesn't have the "mod_deflate.c" module. – aularon Jun 01 '11 at 11:32
  • Hi, this didn't work for me either. I also tried putting the FilesMatch/Header lines within the mod_deflate.c block (since everything else is being gzip'd, I know it works). But still, pingdom is reporting that I should add the header.. – grok_in_full Apr 15 '12 at 08:36
  • 3
    Apache 2.2 does not require the mod_headers section in the answer above. mod_deflate already does what you need. http://httpd.apache.org/docs/2.2/mod/mod_deflate.html – Ari Maniatis Aug 17 '12 at 03:11
  • "Proxy cache servers should always store both the uncompressed and the compressed version of a file, because some clients do not accept GZip compressed files. However, a lot of them will not unless there is a Vary: Accept-Encoding header, so we will make sure to send one using mod_headers." - https://contao.org/en/news/optimizing-contao-for-google-page-speed.html – Chaoley Jul 08 '13 at 08:42
  • Some public proxies have bugs that do not detect the presence of the Content-Encoding response header. This can result in compressed versions being delivered to client browsers that cannot properly decompress the files. Set the Vary: Accept-Encoding response header. This instructs the proxies to cache two versions of the resource: one compressed, and one uncompressed. The correct version of the resource is delivered based on the client request header. - https://developers.google.com/speed/docs/best-practices/caching – Chaoley Jul 08 '13 at 08:45
  • I was getting this suggestion from pagespeed too and I used just the second piece of code above: .... and It worked like a charm.. Note: that i enabled gzip compression in my cpanel first... thanks – Sarah Feb 19 '15 at 18:07
4

I'm afraid Aularon didn't provide enough steps to complete the process. With a little trial and error, I was able to successfully enable Gzipping on my dedicated WHM server.

Below are the steps:

  • Run EasyApache within WHM, select Deflate within the Exhaustive Options list, and rebuild the server.

  • Once done, goto Services Configuration >> Apache Configuration >> Include Editor >> Post VirtualHost Include, select All Versions, and then paste the mod_headers.c and mod_headers.c code (listed above in Aularon's post) on top of on another within the input field.

  • Once saved, I was seeing a 75.36% data savings on average! You can run a before and after test by using this HTTP Compression tool to see your own results: http://www.whatsmyip.org/http_compression/

Hope this works for you all!

  • Matt
Matt D.
  • 41
  • 2
3

To gzip up your font files as well!

add "x-font/otf x-font/ttf x-font/eot"

as in:

AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml x-font/otf x-font/ttf x-font/eot
j0k
  • 21,914
  • 28
  • 75
  • 84
Tom
  • 31
  • 1
1

This was driving me crazy, but it seems that aularon's edit was missing the colon after "Vary". So changing "Vary Accept-Encoding" to "Vary: Accept-Encoding" fixed the issue for me.

I would have commented below the post, but it doesn't seem like it will let me.

Anyhow, I hope this saves someone the same trouble I was having.

Alexander
  • 22,498
  • 10
  • 56
  • 73
  • 2
    Are you sure this makes a difference? In the 2.2 docs none of the examples include the colon: http://httpd.apache.org/docs/2.2/mod/mod_headers.html – Nic Cottrell Oct 14 '12 at 16:50
1

if anyone needs this for NGINX configuration file here is the snippet:

location ~* \.(js|css|xml|gz)$ {
    add_header Vary "Accept-Encoding";
    (... other headers or rules ...)
}
Community
  • 1
  • 1
user319730
  • 61
  • 1
  • 5
1

Many hours spent to clarify what was that. Please, read this post to get the advanced .HTACCESS codes and learn what they do.

You can use:

Header append Vary "Accept-Encoding"
#or
Header set Vary "Accept-Encoding"
T.Todua
  • 44,747
  • 17
  • 195
  • 185
0

No need to specify or even check if the file is/has compressed, you can send it to every file, On every request.

It tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server.

<ifModule mod_headers.c>
  Header unset Vary
  Header set Vary "Accept-Encoding, X-HTTP-Method-Override, X-Forwarded-For, Remote-Address, X-Real-IP, X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Server"
</ifModule>
  • the unset is to fix some bugs in older GoDaddy hosting, optionally.