Leverage browser caching with htaccess

From TYPO3 I was used to get a perfectly optimized .htaccess file which does all of the

  • compressing
  • cache expiration dates
  • mime type handling
  • access blocking

and so on.

With my current project, when I test page speed, I get an advise to do all sorts of things:

Is there something like a best practice .htaccess file for neos installations?

Hi Erich,

we can surely add a couple of the respective things to the provided .htaccess but generally see that more as a topic for devops/providers as most of it can be very generalised.

We’ll have a look at it though.

#####
#
# Example .htaccess file for TYPO3 CMS - for use with Apache Webserver
#
# This file includes settings for the following configuration options:
#
# - Compression
# - Caching
# - MIME types
# - Cross Origin requests
# - Rewriting and Access
# - Miscellaneous
# - PHP optimisation
#
# If you want to use it, you have to copy it to the root folder of your TYPO3 installation (if its
# not there already) and rename it to '.htaccess'. To make .htaccess files work, you might need to
# adjust the 'AllowOverride' directive in your Apache configuration file.
#
# IMPORTANT: You may need to change this file depending on your TYPO3 installation!
#            Consider adding this file's content to your webserver's configuration directly for speed improvement
#
# Lots of the options are taken from https://github.com/h5bp/html5-boilerplate/blob/master/dist/.htaccess
#
####


### Begin: Compression ###

# Compressing resource files will save bandwidth and so improve loading speed especially for users
# with slower internet connections. TYPO3 can compress the .js and .css files for you.
# *) Uncomment the following lines and
# *) Set $GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel'] = 9 for the Backend
# *) Set $GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] = 9 together with the TypoScript properties
#    config.compressJs and config.compressCss for GZIP compression of Frontend JS and CSS files.

#<FilesMatch "\.js\.gzip$">
#	AddType "text/javascript" .gzip
#</FilesMatch>
#<FilesMatch "\.css\.gzip$">
#	AddType "text/css" .gzip
#</FilesMatch>
#AddEncoding gzip .gzip

<IfModule mod_deflate.c>
	# Force compression for mangled `Accept-Encoding` request headers
	<IfModule mod_setenvif.c>
		<IfModule mod_headers.c>
			SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
			RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
		</IfModule>
	</IfModule>

	# Compress all output labeled with one of the following media types.
	#
	# (!) For Apache versions below version 2.3.7 you don't need to
	# enable `mod_filter` and can remove the `<IfModule mod_filter.c>`
	# and `</IfModule>` lines as `AddOutputFilterByType` is still in
	# the core directives.
	#
	# https://httpd.apache.org/docs/current/mod/mod_filter.html#addoutputfilterbytype

	<IfModule mod_filter.c>
		AddOutputFilterByType DEFLATE application/atom+xml \
			application/javascript \
			application/json \
			application/ld+json \
			application/manifest+json \
			application/rdf+xml \
			application/rss+xml \
			application/schema+json \
			application/vnd.geo+json \
			application/vnd.ms-fontobject \
			application/x-font-ttf \
			application/x-javascript \
			application/x-web-app-manifest+json \
			application/xhtml+xml \
			application/xml \
			font/eot \
			font/opentype \
			image/bmp \
			image/svg+xml \
			image/vnd.microsoft.icon \
			image/x-icon \
			text/cache-manifest \
			text/css \
			text/html \
			text/javascript \
			text/plain \
			text/vcard \
			text/vnd.rim.location.xloc \
			text/vtt \
			text/x-component \
			text/x-cross-domain-policy \
			text/xml
	</IfModule>

	<IfModule mod_mime.c>
		AddEncoding gzip svgz
	</IfModule>
</IfModule>

### End: Compression ###



### Begin: Browser caching of resource files ###

# This affects Frontend and Backend and increases performance.
<IfModule mod_expires.c>

	ExpiresActive on
	ExpiresDefault                                      "access plus 1 month"

	ExpiresByType text/css                              "access plus 1 year"

	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/vnd.microsoft.icon              "access plus 1 week"
	ExpiresByType image/x-icon                          "access plus 1 week"

	ExpiresByType text/x-component                      "access plus 1 month"

	ExpiresByType text/html                             "access plus 0 seconds"

	ExpiresByType application/javascript                "access plus 1 year"
	ExpiresByType application/x-javascript              "access plus 1 year"
	ExpiresByType text/javascript                       "access plus 1 year"

	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 audio/ogg                             "access plus 1 month"
	ExpiresByType image/bmp                             "access plus 1 month"
	ExpiresByType image/gif                             "access plus 1 month"
	ExpiresByType image/jpeg                            "access plus 1 month"
	ExpiresByType image/png                             "access plus 1 month"
	ExpiresByType image/svg+xml                         "access plus 1 month"
	ExpiresByType image/webp                            "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 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/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/x-font-woff               "access plus 1 month"
	ExpiresByType font/woff                             "access plus 1 month"
	ExpiresByType application/font-woff2                "access plus 1 month"

	ExpiresByType text/x-cross-domain-policy            "access plus 1 week"

</IfModule>

### End: Browser caching of resource files ###


### Begin: MIME types ###

# Proper MIME types for all files
<IfModule mod_mime.c>

	# Data interchange
	AddType application/atom+xml                        atom
	AddType application/json                            json map topojson
	AddType application/ld+json                         jsonld
	AddType application/rss+xml                         rss
	AddType application/vnd.geo+json                    geojson
	AddType application/xml                             rdf xml

	# JavaScript
	AddType application/javascript                      js

	# Manifest files
	AddType application/manifest+json                   webmanifest
	AddType application/x-web-app-manifest+json         webapp
	AddType text/cache-manifest                         appcache

	# Media files

	AddType audio/mp4                                   f4a f4b m4a
	AddType audio/ogg                                   oga ogg opus
	AddType image/bmp                                   bmp
	AddType image/svg+xml                               svg svgz
	AddType image/webp                                  webp
	AddType video/mp4                                   f4v f4p m4v mp4
	AddType video/ogg                                   ogv
	AddType video/webm                                  webm
	AddType video/x-flv                                 flv
	AddType image/x-icon                                cur ico

	# Web fonts
	AddType application/font-woff                       woff
	AddType application/font-woff2                      woff2
	AddType application/vnd.ms-fontobject               eot
	AddType application/x-font-ttf                      ttc ttf
	AddType font/opentype                               otf

	# Other
	AddType application/octet-stream                    safariextz
	AddType application/x-bb-appworld                   bbaw
	AddType application/x-chrome-extension              crx
	AddType application/x-opera-extension               oex
	AddType application/x-xpinstall                     xpi
	AddType text/vcard                                  vcard vcf
	AddType text/vnd.rim.location.xloc                  xloc
	AddType text/vtt                                    vtt
	AddType text/x-component                            htc

</IfModule>

# UTF-8 encoding
AddDefaultCharset utf-8
<IfModule mod_mime.c>
	AddCharset utf-8 .atom .css .js .json .manifest .rdf .rss .vtt .webapp .webmanifest .xml
</IfModule>

### End: MIME types ###



### Begin: Cross Origin ###

# Send the CORS header for images when browsers request it.
<IfModule mod_setenvif.c>
	<IfModule mod_headers.c>
		<FilesMatch "\.(bmp|cur|gif|ico|jpe?g|png|svgz?|webp)$">
			SetEnvIf Origin ":" IS_CORS
			Header set Access-Control-Allow-Origin "*" env=IS_CORS
		</FilesMatch>
	</IfModule>
</IfModule>

# Allow cross-origin access to web fonts.
<IfModule mod_headers.c>
	<FilesMatch "\.(eot|otf|tt[cf]|woff2?)$">
		Header set Access-Control-Allow-Origin "*"
	</FilesMatch>
</IfModule>

### End: Cross Origin ###



### Begin: Rewriting and Access ###

# You need rewriting, if you use a URL-Rewriting extension (RealURL, CoolUri).

<IfModule mod_rewrite.c>

	# Enable URL rewriting
	RewriteEngine On

	# Store the current location in an environment variable CWD to use
	# mod_rewrite in .htaccess files without knowing the RewriteBase
	RewriteCond $0#%{REQUEST_URI} ([^#]*)#(.*)\1$
	RewriteRule ^.*$ - [E=CWD:%2]

	# Rules to set ApplicationContext based on hostname
	RewriteCond %{HTTP_HOST} ^dev\.my\.web\.site$  [OR]
	RewriteCond %{HTTP_HOST} ^dev\.your\.web\.site$
	RewriteRule .? - [E=TYPO3_CONTEXT:Development]
	RewriteCond %{HTTP_HOST} ^my\.web\.stage$  [OR]
	RewriteCond %{HTTP_HOST} ^your\.web\.stage$
	RewriteRule .? - [E=TYPO3_CONTEXT:Production/Staging]
	RewriteCond %{HTTP_HOST} ^www\.mywebsite\.com$
	RewriteRule .? - [E=TYPO3_CONTEXT:Production]


	# Rule for versioned static files, configured through:
	# - $GLOBALS['TYPO3_CONF_VARS']['BE']['versionNumberInFilename']
	# - $GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename']
	# IMPORTANT: This rule has to be the very first RewriteCond in order to work!
	RewriteCond %{REQUEST_FILENAME} !-f
	RewriteCond %{REQUEST_FILENAME} !-d
	RewriteRule ^(.+)\.(\d+)\.(php|js|css|png|jpg|gif|gzip)$ %{ENV:CWD}$1.$3 [L]

	# Access block for folders
	RewriteRule _(?:recycler|temp)_/ - [F]
	RewriteRule fileadmin/templates/.*\.(?:txt|ts)$ - [F]
	RewriteRule ^(?:vendor|typo3_src|typo3temp/var) - [F]
	RewriteRule (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|Resources/Private|Tests?|Documentation|docs?)/ - [F]

	# Block access to all hidden files and directories with the exception of
	# the visible content from within the `/.well-known/` hidden directory (RFC 5785).
	RewriteCond %{REQUEST_URI} "!(^|/)\.well-known/([^./]+./?)+$" [NC]
	RewriteCond %{SCRIPT_FILENAME} -d [OR]
	RewriteCond %{SCRIPT_FILENAME} -f
	RewriteRule (?:^|/)\. - [F]

	# Stop rewrite processing, if we are in the typo3/ directory or any other known directory
	# NOTE: Add your additional local storages here
	RewriteRule ^(?:typo3/|fileadmin/|typo3conf/|typo3temp/|uploads/|favicon\.ico) - [L]

	# If the file/symlink/directory does not exist => Redirect to index.php.
	# For httpd.conf, you need to prefix each '%{REQUEST_FILENAME}' with '%{DOCUMENT_ROOT}'.
	RewriteCond %{REQUEST_FILENAME} !-f
	RewriteCond %{REQUEST_FILENAME} !-d
	RewriteCond %{REQUEST_FILENAME} !-l
	RewriteRule ^.*$ %{ENV:CWD}index.php [QSA,L]

</IfModule>

# Access block for files
# Apache < 2.3
<IfModule !mod_authz_core.c>
	<FilesMatch "(?i:^\.|^#.*#|^(?:ChangeLog|ToDo|Readme|License)(?:\.md|\.txt)?|^composer\.(?:json|lock)|^ext_conf_template\.txt|^ext_typoscript_constants\.txt|^ext_typoscript_setup\.txt|flexform[^.]*\.xml|locallang[^.]*\.(?:xml|xlf)|\.(?:bak|co?nf|cfg|ya?ml|ts|typoscript|tsconfig|dist|fla|in[ci]|log|sh|sql(?:\..*)?|sqlite(?:\..*)?|sw[op]|git.*|rc)|.*~)$">
		Order allow,deny
		Deny from all
		Satisfy All
	</FilesMatch>
</IfModule>
# Apache ≥ 2.3
<IfModule mod_authz_core.c>
	<If "%{REQUEST_URI} =~ m#(?i:/\.|/\x23.*\x23|/(?:ChangeLog|ToDo|Readme|License)(?:\.md|\.txt)?|/composer\.(?:json|lock)|/ext_conf_template\.txt|/ext_typoscript_constants\.txt|/ext_typoscript_setup\.txt|flexform[^.]*\.xml|locallang[^.]*\.(?:xml|xlf)|\.(?:bak|co?nf|cfg|ya?ml|ts|typoscript|tsconfig|dist|fla|in[ci]|log|sh|sql(?:\..*)?|sqlite(?:\..*)?|sw[op]|git.*|rc)|.*~)$#">
		Require all denied
	</If>
</IfModule>

# Block access to vcs directories
<IfModule mod_alias.c>
	RedirectMatch 404 /\.(?:git|svn|hg)/
</IfModule>

### End: Rewriting and Access ###



### Begin: Miscellaneous ###

# 404 error prevention for non-existing redirected folders
Options -MultiViews

# Make sure that directory listings are disabled.
<IfModule mod_autoindex.c>
	Options -Indexes
</IfModule>

<IfModule mod_headers.c>
	# Force IE to render pages in the highest available mode
	Header set X-UA-Compatible "IE=edge"
	<FilesMatch "\.(appcache|crx|css|eot|gif|htc|ico|jpe?g|js|m4a|m4v|manifest|mp4|oex|oga|ogg|ogv|otf|pdf|png|safariextz|svgz?|ttf|vcf|webapp|webm|webp|woff2?|xml|xpi)$">
		Header unset X-UA-Compatible
	</FilesMatch>

	# Reducing MIME type security risks
	Header set X-Content-Type-Options "nosniff"
</IfModule>

# ETag removal
<IfModule mod_headers.c>
	Header unset ETag
</IfModule>
FileETag None

### End: Miscellaneous ###


# Add your own rules here.

Sorry it broke up the file in frames. This is the default for TYPO3. I’m sure there are some points that help to find most of the specialities NEOS could support.

You can basically use the same file - the settings are not much different, since it’s basic Apache HTTP properties you are setting, and not necessarily TYPO3 dependent settings.

Did you try that and had something improving your score?

The optimizing of images is a different story, because if you tell fluid to display a <img tag with the original resource uploaded, it will present the original (perhaps XX MB large) file - and doesn’t do any comprimizing of images. Unless you take the Neos.Media package and use a image viewhelper and give it some settings, to scale the image

Yes, that I know.

Which part? And did you try the htaccess file from TYPO3 to see if it could serve as a starting point for a similar file for Neos CMS and Neos Flow?

I ment the part with the media Viewhelper. And in the meantime I advanced the .htaccess file and don’t get any problems. How can I add my .htaccess file the easiest way to this forum?

Create a pull request against this repository

By editing the .htaccess file

Is done. I hope I didn’t make any mistakes: https://github.com/neos/flow-development-collection/pull/1846

Hi Erich,

have you thought about using the Google PageSpeed module for Apache (or Nginx)? We’re using it for our website and it works fine.

Interesting!!

Never heard about that, I’ll check that with my technicians.