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 “.css.gzip$”>

AddType “text/css” .gzip

#
#AddEncoding gzip .gzip

# Force compression for mangled `Accept-Encoding` request headers 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
# 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>

End: Compression

Begin: Browser caching of resource files

This affects Frontend and Backend and increases performance.

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"

End: Browser caching of resource files

Begin: MIME types

Proper MIME types for all files

# 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

UTF-8 encoding

AddDefaultCharset utf-8

AddCharset utf-8 .atom .css .js .json .manifest .rdf .rss .vtt .webapp .webmanifest .xml

End: MIME types

Begin: Cross Origin

Send the CORS header for images when browsers request it.

SetEnvIf Origin ":" IS_CORS Header set Access-Control-Allow-Origin "*" env=IS_CORS

Allow cross-origin access to web fonts.

Header set Access-Control-Allow-Origin "*"

End: Cross Origin

Begin: Rewriting and Access

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

# 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]

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

Apache ≥ 2.3

Require all denied

Block access to vcs directories

RedirectMatch 404 /\.(?:git|svn|hg)/

End: Rewriting and Access

Begin: Miscellaneous

404 error prevention for non-existing redirected folders

Options -MultiViews

Make sure that directory listings are disabled.

Options -Indexes # Force IE to render pages in the highest available mode Header set X-UA-Compatible "IE=edge" Header unset X-UA-Compatible
# Reducing MIME type security risks
Header set X-Content-Type-Options "nosniff"

ETag removal

Header unset ETag 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.