How to Specify "Vary: Accept-Encoding" header in .htaccess - .htaccess

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

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.

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

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

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.

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"

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 ...)
}

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.

Related

Font not loading - CORS says header contains no Access Control but there is

The problem: I am using html generated from one site that is being pushed to another site (different domains). All is working well except the font (used mainly for icons) is not showing up. I am receiving a CORS error as described further below.
I have added the following code to my .htaccess file on the site where the fonts are stored that allows fonts to be access across any domain:
<FilesMatch ".(eot|ttf|otf|woff)">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
I checked the header using cUrl:
curl -I https://mywebsite.com/fonts/flatpack.woff?tzy7cr
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 23 Jun 2017 18:33:58 GMT
Content-Type: text/plain
Content-Length: 142020
Connection: keep-alive
X-Accel-Version: 0.01
Last-Modified: Fri, 23 Jun 2017 17:49:02 GMT
ETag: "1a474c-22ac4-552a4378235b7"
Accept-Ranges: bytes
X-Powered-By: PleskLin
Access-Control-Allow-Origin: *
The Access Origin response tells me that the font should be readable but I'm still getting this error from the requesting website:
Access to Font at https://mywebsite/fonts/flatpack.woff?tzy7cr' from
origin 'http://anotherwebsite.com' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://anotherwebsite.com' is therefore not allowed
access.
Thoughts or suggestions???
Edit: Here is a live link to a test page that fails to load the icon fonts.
Almost got it right:
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Methods: "GET"
You need to add the header not set. I'd also add the method to be sure.
Premium font purchased by some other website can be abused another site. That is the reason of complication at browser level coding. Image will not suffer such issue. Font related CORS is complicated by types of fonts, browsers and bugs. Unless you are using paid origin pull CDN or known font provider (free or paid), it is practical to serve font from own server for the sake of making sure that font loads on all browsers, all devices. It is worthy to read :
official W3 doc about CORS,
Mozilla doc,
MaxCDN's guide,
W3's CSS font doc,
this old bug report
this pull request
There are three options from the above resources for giving you a correct answer. You need to test from webpagetest dot org from different user agents & devices and try to watch the video of screenshot.
One :
SetEnvIf Origin "https?://(.*\.(mozilla|allizom)\.(com|org|net))" CORS=$0
Header set Access-Control-Allow-Origin %{CORS}e env=CORS
<FilesMatch "\.(ttf|woff|eot)$">
Header append vary "Origin"
ExpiresActive On
ExpiresDefault "access plus 1 year"
</FilesMatch>
Two (Single domain, HTTPS) :
<FilesMatch "\.(ttf|otf|eot|woff)$">
SetEnvIf Origin "^http(s)?://(.+\.)?anotherwebsite\.com$" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
</FilesMatch>
Three (Multiple domains) :
<FilesMatch "\.(ttf|otf|eot|woff)$">
<IfModule mod_headers.c>
SetEnvIf Origin "http(s)?://(www\.)?(anotherwebsite.com|cdn.anotherwebsite.com|blahblah.anotherwebsite.com)$" AccessControlAllowOrigin=$0
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
</IfModule>
</FilesMatch>
Also make sure that proper MIME types are present :
AddType application/vnd.ms-fontobject .eot
AddType application/x-font-opentype .otf
AddType image/svg+xml .svg
AddType application/x-font-ttf .ttf
AddType application/font-woff .woff
AddType application/font-woff2 .woff2
Make sure to run Apache configtest before restarting. You may need to activate some module.
I do not want to take credit for the answer, but this worked for me, thanks to this guy:
https://crunchify.com/how-to-fix-access-control-allow-origin-issue-for-your-https-enabled-wordpress-site-and-maxcdn/
Add below lines to .htaccess file and put it inside your (font) folder:
<ifmodule mod_headers.c="">
SetEnvIf Origin "^(.*\.domain\.com)$" ORIGIN_SUB_DOMAIN=$1
Header set Access-Control-Allow-Origin "%{ORIGIN_SUB_DOMAIN}e" env=ORIGIN_SUB_DOMAIN
Header set Access-Control-Allow-Methods: "*"
Header set Access-Control-Allow-Headers: "Origin, X-Requested-With, Content-Type, Accept, Authorization"
</ifmodule>

Magento: compressing image not working effectively in .htaccess

I have the version 1.7.0.2 of magneto.I have enabled the gzip compression css htaccess js and css compression showing 100% on web page test org http://www.webpagetest.org/result/140812_2H_JQX/ but the problem is that image compression is only 38% i just want increase image compression.
I tried lots of code and extensions but not getting any improvement on image compression.
Any one can tell me how can i compress the image via .htaccess or using any extensions.
I am using bellow code in htaccess file
<IfModule mod_deflate.c>
############################################
## enable apache served files compression
## http://developer.yahoo.com/performance/rules.html#gzip
# Insert filter on all content
SetOutputFilter DEFLATE
# Insert filter on selected content types only
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
BrowserMatch ^Mozilla/4 gzip-only-text/html
# Netscape 4.06-4.08 have some more problems
BrowserMatch ^Mozilla/4\.0[678] no-gzip
# MSIE masquerades as Netscape, but it is fine
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
# Don't compress images
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
# Make sure proxies don't deliver the wrong content
Header append Vary User-Agent env=!dont-vary
</IfModule>
Short answer is; You're not going to find a method to easily compress images properly using only .htaccess.
If you want compression levels similar to the PageSpeed plugin you might want to put your attention to the following documentation:
https://developers.google.com/speed/docs/best-practices/payload?hl=fr#CompressImages
Method 1
I'm going to take a guess and assume the plugins you tried to use either not doing what they advertise (it happens with Magento community plugins) or used a tool you either didn't have installed or needed to install, but this is the automatic method.
In the linked article under "Use an image compressor" there are several tools outlined which can be used to compress images on linux environments. You could build a Magento plugin to automate that process. Short of that there isn't anything to do this easily.
Method 2
What you can do however is have the images which PageSpeed optimizes be saved in a configured directory. Now the link for that is broken but you can find that here:
https://developers.google.com/speed/docs/insights/OptimizeImages
After running PageSpeed you can then just upload the images to their respective directories.
I would suggest the second method, as the first method isn't exactly easy and requires you to be able to control the live hosting environment to install plugins/tools you need.
Also, don't bite down on PageSpeed scores too much, seeing that report makes me wonder how many plugins you installed on Magento that are now causing a lot of overhead. Note that any plugin slows down Magento a bit, and it's not fast in and on itself either, so other methods / caching are way more valuable than micro-managing image sizes. Time to first byte is easier to optimize than image compression.

ExpiresByType not working but <FilesMatch> IS in .htaccess?

I've been rather perplexed by this and was hoping someone might be able to shed some light.
In my htaccess file, I have:
<IfModule mod_expires.c>
ExpiresActive on
ExpiresByType text/html "access plus 4 hours"
</IfModule>
However, it doesnt seem to be working when I check the headers.
I get "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"
But if I use THIS code:
# 4 HOURS
<FilesMatch "\.(html|htm)$">
Header set Cache-Control "max-age=14400, must-revalidate"
</FilesMatch>
It DOES pick up the cache.
max-age=14400, must-revalidate
Am I missing something? Is one for the server and one for the browser? I mean, I'd prefer to use the first since it came with boilerplate if they are both the same.
Thanks in advance!
I don't know if you already have the solution but are you sure mod_expire is enabled on your server? On ubuntu sudo apache2ctl -M.
You can also try to comment out <IfModule mod_expires.c> and </IfModule> and restart apache. It will crash if mod_expires is not enabled.
To enable mod_expire:
sudo a2enmod expires
mod_expires will create the Expire header and should modify the max-age=14400 part of the Cache-Control header only. For the must-revalidate part you will have to use Header.

Responding to google page speed suggestion regarding compression

Google page speed tool tells me this: "Compressing resources with gzip or deflate can reduce the number of bytes sent over the network"
and of course lists all my .js and .css files.
Researching here eventually led me to this question:
How to Specify "Vary: Accept-Encoding" header in .htaccess
Which seems to say that for just .js and .css files all I would need to do is this:
<IfModule mod_deflate.c>
#The following line is enough for .js and .css
AddOutputFilter DEFLATE js css
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(js|css)$">
Header append Vary: Accept-Encoding
</FilesMatch>
</IfModule>
Can someone confirm that this is the current "best practice" for this objective and that it is failsafe, assuming the user is on a modern browser (e.g. not < IE7 for example)
Thanks!

htaccess - How to force the client's browser to clear the cache?

For my site I have the following htaccess rules:
# BEGIN Gzip
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/text text/html text/plain text/xml text/css application/x-javascript application/javascript
</IfModule>
# END Gzip
# BEGIN EXPIRES
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 10 days"
ExpiresByType text/css "access plus 1 month"
ExpiresByType text/plain "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/x-icon "access plus 1 year"
</IfModule>
# END EXPIRES
I've just updated my site and it looked all screwy until I cleared my cache. How can I force the client's browser to clear the cache after an update so that the user can see the changes?
You can force browsers to cache something, but
You can't force browsers to clear their cache.
Thus the only (AMAIK) way is to use a new URL for your resources. Something like versioning.
As other answers have said, changing the URL is a good cache busting technique, however it is alot of work to go through a bigger site, change all the URLs and also move the files.
A similar technique is to just add a version parameter to the URL string which is either a random string / number or a version number, and target the changed files only.
For instance if you change your sites CSS and it looks wonky until you do a force refresh, simply add ?ver=1.1 to the CSS import at the head of the file. This to the browser is a different file, but you only need to change the import, not the actual location or name of the file.
e.g:
<link href="assets/css/style.css" rel="stylesheet" type="text/css" />
becomes
<link href="assets/css/style.css?ver=1.1" rel="stylesheet" type="text/css" />
Works great for javascript files also.
I got your problem...
Although we can clear client browser cache completely but you can add some code to your application so that your recent changes reflect to client browser.
In your <head>:
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
You can not force the browsers to clear the cache.
Your .html file seems to be re-loaded sooner as it expires after 10 days.
What you have to do is to update your .html file and move all your files to a new folder such as version-2/ or append a version identifier to each file such as mypicture-2.jpg. Then you reference these new files in your .html file and the browser will load them again because the location changed.
You can tell the browser never cache your site by pasting following code in the header
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
And to prevent js, css cache, you could use tool to minify and obfuscate the scripts which should generate a random file name every time. That would force the browser to reload them from server too.
Hopefully, that helps.
In my case, I change a lot an specific JS file and I need it to be in its last version in all browsers where is being used.
I do not have a specific version number for this file, so I simply hash the current date and time (hour and minute) and pass it as the version number:
<script src="/js/panel/app.js?v={{ substr(md5(date("Y-m-d_Hi")),10,18) }}"></script>
I need it to be loaded every minute, but you can decide when it should be reloaded.
Adding 'random' numbers to URLs seems inelegant and expensive to me. It also spoils the URL of the pages, which can look like index.html?t=1614333283241 and btw users will have dozens of URLs cached for only one use.
I think this kind of things is what .htaccess files are meant to solve at the server side, between your functional code an the users.
I copy/paste this code from here that allows filtering by file extension to force the browser not to cache them. If you want to return to normal behavior, just delete or comment it.
Create or edit an .htaccess file on every folder you want to prevent caching, then paste this code changing file extensions to your needs, or even to match one individual file.
If the file already exists on your host be cautious modifying what's in it.
(kudos to the link)
# DISABLE CACHING
<IfModule mod_headers.c>
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires 0
</IfModule>
<FilesMatch "\.(css|flv|gif|htm|html|ico|jpe|jpeg|jpg|js|mp3|mp4|png|pdf|swf|txt)$">
<IfModule mod_expires.c>
ExpiresActive Off
</IfModule>
<IfModule mod_headers.c>
FileETag None
Header unset ETag
Header unset Pragma
Header unset Cache-Control
Header unset Last-Modified
Header set Pragma "no-cache"
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Expires "jue, 1 Jan 1970 00:00:00 GMT"
</IfModule>
</FilesMatch>
You can set "access plus 1 seconds" and that way it will refresh the next time the user enters the site. Keep the setting for one month.
Now the following wont help you with files that are already cached, but moving forward, you can use the following to easily force a request to get something new, without changing the actual filename.
# Rewrite all requests for JS and CSS files to files of the same name, without
# any numbers in them. This lets the JS and CSS be force out of cache easily
# by putting a number at the end of the filename
# e.g. a request for static/js/site-52.js will get the file static/js/site.js instead.
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^static/(js|css)/([a-z]+)-([0-9]+)\.(js|css)$ /site/$1/$2.$4 [R=302,NC,L]
</IfModule>
Of course, the higher up in your folder structure you do this type of approach,
the more you kick things out of cache with a simple change.
So for example, if you store the entire css and javascript of your site in one main folder
/assets/js
/assets/css
/assets/...
Then you can could start referencing it as "assets-XXX" in your html, and use a rule like so to kick all assets content out of cache.
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^assets-([a-z0-9]+)/(.*) /$2 [R=302,NC,L]
</IfModule>
Note that if you do go with this, after you have it working, change the 302 to a 301, and then caching will kick in. When it's a 302 it wont cache at the browser level because it's a temporary redirect. If you do it this way, then you could bump up the expiry default time to 30 days for all assets, since you can easily kick things out of cache by simply changing the folder name in the login page.
<IfModule mod_expires.c>
ExpiresActive on
ExpiresDefault A2592000
</IfModule>
The most straight forward is to add filetime to the request.
eg
myfile.txt?2014-10-30-13:12:33
versioning by date.
Change the name of the .CSS file
Load the page and then change the file again in the original name it works for me.
This worked for me.
look for this:
DirectoryIndex index.php
replace with this:
DirectoryIndex something.php index.php
Upload and refresh page. You will get a page error.
just change it back to:
DirectoryIndex index.php
reupload and refresh page again. I checked this on all of my devices and, it worked.
Use the mod rewrite with R=301 - where you use a incremental version number:
To achieve > css/ver/file.css => css/file.css?v=ver
RewriteRule ^css/([0-9]+)/file.css$ css/file.css?v=$1 [R=301,L,QSA]
so example, css/10/file.css => css/file.css?v=10
Same can be applied to js/ files. Increment ver to force update, 301 forces re-cache
I have tested this across Chrome, Firefox, Opera etc
PS: the ?v=ver is just for readability, this does not cause the refresh

Resources