Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Make your Website faster - a safe htaccess way
#
# Sources:
# http://stackoverflow.com/questions/7704624/how-can-i-use-gzip-compression-for-css-and-js-files-on-my-websites
# http://codex.wordpress.org/Output_Compression
# http://www.perun.net/2009/06/06/wordpress-websites-beschleuinigen-4-ein-zwischenergebnis/#comment-61086
# http://www.smashingmagazine.com/smashing-book-1/performance-optimization-for-websites-part-2-of-2/
# http://gtmetrix.com/configure-entity-tags-etags.html
# http://de.slideshare.net/walterebert/die-htaccessrichtignutzenwchh2014
# http://de.slideshare.net/walterebert/mehr-performance-fr-wordpress
# https://andreashecht-blog.de/4183/
#
<IfModule mod_deflate.c>
# Insert filters / compress text, html, javascript, css, xml:
# mod_deflate can be used for Apache v2 and later and is the recommended GZip mechanism to use
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/vtt
AddOutputFilterByType DEFLATE text/x-component
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/js
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/x-httpd-php
AddOutputFilterByType DEFLATE application/x-httpd-fastphp
AddOutputFilterByType DEFLATE application/atom+xml
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/ld+json
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/font-sfnt
AddOutputFilterByType DEFLATE application/x-web-app-manifest+json
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE font/sfnt
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
# Exception: Images
SetEnvIfNoCase REQUEST_URI \.(?:gif|jpg|jpeg|png)$ no-gzip dont-vary
# Drop problematic browsers
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
# Make sure proxies don't deliver the wrong content
<IfModule mod_headers.c>
Header append Vary User-Agent env=!dont-vary
</IfModule>
</IfModule>
# mod_gzip is an external extension and last updated 2015, so
# if available please use mod_deflate instead
# If you are stuck on Apache v1.3 you can use mod_zip to enable Gzip
# as mod_deflate is available for Apache v2 or later only.
<IfModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file \.(html?|txt|css|js|php|pl)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
</IfModule>
## EXPIRES CACHING ##
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 week"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/atom+xml "access plus 1 hour"
ExpiresByType application/rdf+xml "access plus 1 hour"
ExpiresByType application/rss+xml "access plus 1 hour"
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/ld+json "access plus 0 seconds"
ExpiresByType application/schema+json "access plus 0 seconds"
ExpiresByType application/vnd.geo+json "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType text/xml "access plus 0 seconds"
ExpiresByType image/x-icon "access plus 1 month"
ExpiresByType image/vnd.microsoft.icon "access plus 1 month"
ExpiresByType text/html "access plus 1 minute"
ExpiresByType text/javascript "access plus 1 month"
ExpiresByType text/x-javascript "access plus 1 month"
ExpiresByType application/javascript "access plus 1 months"
ExpiresByType application/x-javascript "access plus 1 months"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType image/bmp "access plus 1 month"
ExpiresByType image/webp "access plus 1 month"
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/ogg "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
ExpiresByType text/plain "access plus 1 month"
ExpiresByType text/x-component "access plus 1 month"
ExpiresByType application/manifest+json "access plus 1 week"
ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
ExpiresByType text/cache-manifest "access plus 0 seconds"
ExpiresByType application/pdf "access plus 1 month"
ExpiresByType application/x-shockwave-flash "access plus 1 month"
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
ExpiresByType font/eot "access plus 1 month"
ExpiresByType font/opentype "access plus 1 month"
ExpiresByType application/x-font-ttf "access plus 1 month"
ExpiresByType application/font-woff "access plus 1 month"
ExpiresByType application/font-woff2 "access plus 1 month"
ExpiresByType application/x-font-woff "access plus 1 month"
ExpiresByType font/woff "access plus 1 month"
</IfModule>
## EXPIRES CACHING ##
#Alternative caching using Apache's "mod_headers", if it's installed.
#Caching of common files - ENABLED
<IfModule mod_headers.c>
# 1 Month
<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
Header set Cache-Control "max-age=2592000, public"
</FilesMatch>
# 2 DAYS
<FilesMatch "\.(xml|txt)$">
Header set Cache-Control "max-age=172800, public, must-revalidate"
</FilesMatch>
# 2 HOURS
<FilesMatch "\.(html|htm)$">
Header set Cache-Control "max-age=7200, must-revalidate"
</FilesMatch>
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(js|css|xml|gz|html|ttf)$">
Header append Vary: Accept-Encoding
</FilesMatch>
</IfModule>
# Set Keep Alive Header
# This *just* sets the header - maybe your hoster is not allowing this feature
# Please check if it is working with tools like http://www.webpagetest.org
<IfModule mod_headers.c>
Header set Connection keep-alive
</IfModule>
# If your server don't support ETags deactivate with "None" (and remove header)
<IfModule mod_expires.c>
<IfModule mod_headers.c>
Header unset ETag
</IfModule>
FileETag None
</IfModule>
@zakynthinos
Copy link

zakynthinos commented Apr 2, 2016

Clear code. Thanks a lot..

@marfert
Copy link

marfert commented Apr 26, 2016

@Zodiac1978 Would it be safe to not exclude SVG from being compressed? I tried that and I got rid of Google PageSpeed Insights message that some of my included SVGs are not compressed.

@Zodiac1978
Copy link
Author

Zodiac1978 commented Jun 7, 2016

@marfert Yes, SVG can be compressed. I will remove SVG from the exception list too. Thanks for the heads up!

@fengler-it
Copy link

fengler-it commented Jul 27, 2016

Cool. Gave me 2 % better performance! :-)

@fengler-it
Copy link

fengler-it commented Sep 29, 2016

Hi. Very nice code.
I use it and still pingdom tells me

Leverage browser caching
The following cacheable resources have a short freshness lifetime. Specify an expiration at least one week in the future for the following resources:

https://assets.pinterest.com/js/pinit.js
https://assets.pinterest.com/js/pinit_main.js?0.9677980106789619
https://connect.facebook.net/en_US/all.js
https://apis.google.com/js/plusone.js
https://platform.twitter.com/widgets.js
https://fonts.googleapis.com/css?family=Oswald%3A400%2C700%2C300&ver=4.6.1

<<<<

Why?


Update: Found out myself.
Had to add following line:

AddOutputFilterByType DEFLATE text/javascript
<<<

@fengler-it
Copy link

fengler-it commented Sep 29, 2016

Works on two sites, but one site gives me an "Internel Server Error 500", when adding following line:

Header append Vary User-Agent env=!dont-vary
<<<

@Vvvetal-90
Copy link

Vvvetal-90 commented Sep 30, 2016

PageSpeed Insights Errors:

/fonts/RobotoBold/RobotoBold.woff (expiration not specified)
/fonts/RobotoRegular/RobotoRegular.woff (expiration not specified)
/js/common.js (expiration not specified)
/js/libs.min.js (expiration not specified)

How it fix?

@Zodiac1978
Copy link
Author

Zodiac1978 commented Feb 10, 2017

@fengler-it These assets are all from an external server, so your change shouldn't have any impact.

@fengler-it I missed to add a check to this line. Is fixed now. Thanks for the heads up!

@Vvvetal-90 Woff is already compressed, no need to do it again, see: https://gist.github.com/Zodiac1978/3145830#gistcomment-1681099
About js files: Have you tried AddOutputFilterByType DEFLATE text/javascript?

@ginocremer
Copy link

ginocremer commented Jul 13, 2017

Hey Thorsten, my Javascripts weren't correctly cached, I think you should update your gist with the following (after that everything has been cached perfectly):

AddOutputFilterByType DEFLATE text/javascript

Edit: Sorry, just read your post above. Isn't it a good idea to update the gist anyway?

@Zodiac1978
Copy link
Author

Zodiac1978 commented Jul 19, 2017

@ginocremer I've added the line. Thanks for the reminder!

@bestfitbybrazil
Copy link

bestfitbybrazil commented Oct 15, 2017

Hi Zodiac1978. I just stepped into this and have been having trouble with fluctuating CPU performance jumping anywhere from 30% to beyond 77% in an instance. Not a whole lot of traffic when this is happening. This is my complete .htaccess file now.

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

<IfModule mod_expires.c>
# Enable expirations
ExpiresActive On 
# Default directive
ExpiresDefault "access plus 1 month"
# My favicon
ExpiresByType image/x-icon "access plus 1 year"
# Images
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
# CSS
ExpiresByType text/css "access plus 1 month"
# Javascript
ExpiresByType application/javascript "access plus 1 year"
</IfModule>
# END WordPress

i added the ifmodule mod_expires portion. can you give me complete i can use so as to cover everything else. Still getting low performance from test on gtMetrics. Here's message i get now.

There are 12 static components without a far-future expiration date.

https://fonts.googleapis.com/css?family=Poppins%3A300%2C400%2C500%2C600%2C700%7CLibre+Baskerville%3A400italic&subset=latin%2Clatin-ext&ver=4.8.2
https://bestfitbybrazil.com/wp-content/plugins/wp-spamshield/js/jscripts.php
https://www.livehelpnow.net/lhn/widgets/chatbutton/lhnchatbutton-current.min.js
https://apis.google.com/js/platform.js?onload=renderBadge
https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit2
https://www.google-analytics.com/analytics.js
https://translate.googleapis.com/translate_static/css/translateelement.css
https://translate.googleapis.com/translate_static/js/element/main.js
https://loader.wisepops.com/get-loader.js?v=1&user_id=2166
https://popup.wisepops.com/my-wisepop?uid=2166
https://translate.googleapis.com/translate_a/l?client=te&alpha=true&hl=en&cb=_callbacks____0j8stitkp
https://www.livehelpnow.net/lhn/scripts/livehelpnow.min.aspx?lhnid=19096&iv=0&ivid=0&d=0&ver=5.3&rnd=0.8954146259070273

The second "High priority" fix needed is:

Make fewer HTTP requests F (0) CONTENT HIGH
What's this mean?This page has 82 external Javascript scripts. Try combining them into one.This page has 39 external stylesheets. Try combining them into one.

@APlusDesign
Copy link

APlusDesign commented Nov 14, 2017

Why not combine some of the rules, for example.

#Alternative caching using Apache's "mod_headers", if it's installed.
#Caching of common files - ENABLED
<IfModule mod_headers.c>
	<FilesMatch "\.(ico|pdf|flv|swf|js|css|gif|png|jpg|jpeg|txt|html|htm)$">
		Header set Cache-Control "max-age=2592000, public"
	</FilesMatch>
	<FilesMatch "\.(js|css|xml|gz)$">
    	Header append Vary Accept-Encoding
  	</FilesMatch>
  	# Set Keep Alive Header
	# This *just* sets the header - maybe your hoster is not allowing this feature
	# Please check if it is working with tools like http://www.webpagetest.org
	Header set Connection keep-alive
</IfModule>

@Zodiac1978
Copy link
Author

Zodiac1978 commented Nov 15, 2017

@bestfitbybrazil 11 of 12 of these files are not on your server, but on 3rd-party-sites, so you cannot cache them with a server setting on your server. ;) The second one is on your server, but is dynamically generated (PHP file generating JS I assume). Not meant to be cached.

82 JS files + 39 CSS files are really a huge amount of files. You should definitely try to get rid of them. Caching is good, but doesn't solve the issue of having too much files loaded.

If you have reduced this to a smaller amount of files you can try to combine them with a tool like Autoptimize.

@Zodiac1978
Copy link
Author

Zodiac1978 commented Nov 15, 2017

@APlusDesign Nice idea, but I prefer the longer, more readable version. :)

@PSAiridas
Copy link

PSAiridas commented Mar 1, 2018

how to fix?!
The following cacheable resources have a short freshness lifetime. Specify an expiration at least one week in the future for the following resources:
http://..../wp-content/themes/Life/svg/f_discord1.svg

@Zodiac1978
Copy link
Author

Zodiac1978 commented Mar 28, 2018

This should not happen because of https://gist.github.com/Zodiac1978/3145830#file-htaccess-L63
Please check the MIME type the server is using for this SVG @PSAiridas

@mortensassi
Copy link

mortensassi commented May 19, 2018

Thanks for sharing!
I'm using Wordpress with VueJS and i can't get the wp-json responses gzipped
Copied this htaccess to both my / where the Frontend is located and for /be where my wordpress is located

This is what pingdom is telling me:

The following cacheable resources have a short freshness lifetime. Specify an expiration at least one week in the future for the following resources:
https://site.com/be/wp-json/wp-api-menus/v2/menu-locations/footer-menu
https://site.com/be/wp-json/wp-api-menus/v2/menu-locations/header-menu
https://site.com/be/wp-json/wp/v2/projects
The following resources are missing a cache validator. Resources that do not specify a cache validator cannot be refreshed efficiently. Specify a Last-Modified or ETag header to enable cache validation for the following resources:
https://site.com/be/wp-json/wp-api-menus/v2/menu-locations/footer-menu
https://site.com/be/wp-json/wp-api-menus/v2/menu-locations/header-menu
https://site.com/be/wp-json/wp/v2/projects
The following publicly cacheable, compressible resources should have a "Vary: Accept-Encoding" header:
https://site.com/be/wp-json/wp-api-menus/v2/menu-locations/footer-menu
https://site.com/be/wp-json/wp-api-menus/v2/menu-locations/header-menu
https://site.com/be/wp-json/wp/v2/projects

@codeclinic
Copy link

codeclinic commented Sep 16, 2018

mod_deflate is now the recommended option over mod_gzip.

@Zodiac1978
Copy link
Author

Zodiac1978 commented Jan 6, 2019

Hi @mortensassi this should be working if your host is using mod_deflate because of these lines:
https://gist.github.com/Zodiac1978/3145830#file-htaccess-L30-L31

@Zodiac1978
Copy link
Author

Zodiac1978 commented Jan 6, 2019

Hi @codeclinic - what do you recommend to change (as this gist is using mod_gzip and mod_deflate)?

@yashodhank
Copy link

yashodhank commented Jun 23, 2019

mod_gzip is legacy module Apache 1.3 and bellow.
mod_deflate is compatible with Apache 2.0 and above.

@Zodiac1978
Copy link
Author

Zodiac1978 commented Jun 23, 2019

@yashodhank Thanks for the explanation. I will add some more explanations as comments!

@wlarch
Copy link

wlarch commented Jul 16, 2020

Thanks for the Gist ! Very helpful.
In our case, we used the "modification" flag for the mod_expires.

Example :

ExpiresByType text/javascript "modification plus 1 year"

@Zodiac1978
Copy link
Author

Zodiac1978 commented Mar 26, 2021

Update: Added some more font mime types: font/sfnt, application/font-sfnt, font/otf and font/ttf

@Barnabas2
Copy link

Barnabas2 commented Aug 10, 2021

If using CDN, will editing .htaccess have any effect? Since the request is served by the CDN and not the server?

@Zodiac1978
Copy link
Author

Zodiac1978 commented Aug 11, 2021

@Barnabas2 No, if the item is loaded from an external CDN the settings from your server (htaccess) have, of course, no effect.

@cutcool
Copy link

cutcool commented Jan 5, 2022

Hello, according to google page speed I don't have the compression of the following files enabled
…css/bootstrap.min.css , …remixicon/remixicon.css, …jquery/jquery.min.js and more but checking the compression in https://www.websiteplanet.com/es/webtools/gzip-compression/ compression is active ... why could google pagespeed not recognize compression?

@Zodiac1978
Copy link
Author

Zodiac1978 commented Jan 6, 2022

@cutcool I recommend to use https://www.webpagetest.org/ - this will give you more insights. If you use this .htaccess settings from above this shouldn't happen if those files are hosted on your server, because CSS and JS files should be compressed.

Are those files really hosted on your server and not from a CDN? Is your server using the mod_deflate module? You can check via phpinfo() (under "Loaded Modules"). (Or mod_gzip if you are on Apache <=1.3)

@monty88
Copy link

monty88 commented Sep 19, 2022

Hello, thank you for sharing that

But, is that work with WordPress ?

@Zodiac1978
Copy link
Author

Zodiac1978 commented Sep 19, 2022

@monty88 Yes, it is working with WordPress.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment