# Check if Accept header is image/webp, if an image is requested in "classic" folder with a classic format, and check if the corresponding webp image exists. If yes, rewrite the requested classic image URI to the WebP image URI.
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} (.*)images/classic/(.*)\.(png|jpg|gif)$
RewriteCond %1images/webp/%2\.webp -f
RewriteRule .* images/webp/%2.webp [L]
You should add a mecanism to redirect HTTP to HTTPS too. It's adviced to use VirtualHost:
<IfModule mod_rewrite.c>
# Force HTTPS
# Inspired from https://stackoverflow.com/questions/13376219/htaccess-redirect-http-to-https/
RewriteEngine on
RewriteCond %{HTTPS} !on
# Header used by proxies or load balancers (CDN):
RewriteCond %{HTTP:X-Forwarded-Proto} !https
# Some load balancer use other methods:
# RewriteCond %{HTTPS} !1
# RewriteCond %{SERVER_PORT} !443
# RewriteCond %{HTTP:X-Forwarded-SSL} !on
# RewriteCond %{HTTP:CF-Visitor} '"scheme":"http"'
# RewriteCond %{ENV:HTTPS} !on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
<IfModule mod_headers.c>
# Force HTTPS
Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"
</IfModule>
<IfModule mod_headers.c>
# Force HTTPS only for www.example.com
SetEnvIf Host "^www\.example\.com$" StrictTransportSecurity
Header always set Strict-Transport-Security "max-age=63072000; preload" env=StrictTransportSecurity
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine on
# Determine the RewriteBase automatically and set it as environment variable.
# If you are using Apache aliases to do mass virtual hosting or installed the
# project in a subdirectory, the base path will be prepended to allow proper
# resolution of the files and to redirect to the correct URI. It will
# work in environments without path prefix as well, providing a safe, one-size
# fits all solution. But as you do not need it in this case, you can comment
# the following 2 lines to eliminate the overhead.
RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
RewriteRule ^(.*) - [E=BASE:%1]
RewriteCond %{REQUEST_URI} !^%{ENV:BASE}/subfolder/
RewriteRule ^(.*)$ %{ENV:BASE}/subfolder/$1 [L]
</IfModule>
<IfModule rewrite_module>
RewriteEngine on
RewriteCond %{DOCUMENT_ROOT} ^(.*)$ [NC]
RewriteRule ^ - [E=doc_root:%1]
# Will add an header `X-Debug` with value equal DOCUMENT_ROOT (see the syntax `%{xxxx}e`)
Header append X-Debug "%{doc_root}e"
# Add cookie `test` to `1`
RewriteRule ^ - [CO=test:1:%{HTTP_HOST}]
# Test cookie `test` == `1`
RewriteCond %{HTTP:Cookie} \test=1(;|$)
# Skip next RewriteRule (use 2 instead 1 to skip more, 3... or use L)
RewriteRule ^ - [S=1]
# Test if env var `test` equal `1`
RewriteCond %{ENV:test} ^1$
RewriteRule...
</IfModule>
# Use the front controller as index file. It serves as a fallback solution when
# every other rewrite/redirect fails (e.g. in an aliased environment without
# mod_rewrite). Additionally, this reduces the matching process for the
# start page (path "/") because otherwise Apache will apply the rewriting rules
# to each configured DirectoryIndex file (e.g. index.php, index.html, index.pl).
DirectoryIndex app.php
# By default, Apache does not evaluate symbolic links if you did not enable this
# feature in your server configuration. Uncomment the following line if you
# install assets as symlinks or if you experience problems related to symlinks
# when compiling LESS/Sass/CoffeScript assets.
# Options FollowSymlinks
# Disabling MultiViews prevents unwanted negotiation, e.g. "/app" should not resolve
# to the front controller "/app.php" but be rewritten to "/app.php/app".
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine on
# Determine the RewriteBase automatically and set it as environment variable.
# If you are using Apache aliases to do mass virtual hosting or installed the
# project in a subdirectory, the base path will be prepended to allow proper
# resolution of the app.php file and to redirect to the correct URI. It will
# work in environments without path prefix as well, providing a safe, one-size
# fits all solution. But as you do not need it in this case, you can comment
# the following 2 lines to eliminate the overhead.
RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
RewriteRule ^(.*) - [E=BASE:%1]
# Sets the HTTP_AUTHORIZATION header removed by Apache
RewriteCond %{HTTP:Authorization} .
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect to URI without front controller to prevent duplicate content
# (with and without `/app.php`). Only do this redirect on the initial
# rewrite by Apache and not on subsequent cycles. Otherwise we would get an
# endless redirect loop (request -> rewrite to front controller ->
# redirect -> request -> ...).
# So in case you get a "too many redirects" error or you always get redirected
# to the start page because your Apache does not expose the REDIRECT_STATUS
# environment variable, you have 2 choices:
# - disable this feature by commenting the following 2 lines or
# - use Apache >= 2.3.9 and replace all L flags by END flags and remove the
# following RewriteCond (best solution)
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^app\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=301,L]
## Not part of Symfony
# Ignore /admin/ and /mobile/ subfolders
# https://stackoverflow.com/a/24319167/470117
RewriteCond "%{ENV:BASE}/admin/ %{REQUEST_URI}" "(^[^ ]*) \1" [OR]
RewriteCond "%{ENV:BASE}/mobile/ %{REQUEST_URI}" "(^[^ ]*) \1"
RewriteRule ^ - [L]
## /Not part of Symfony
# If the requested filename exists, simply serve it.
# We only want to let Apache serve files and not directories.
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
# Rewrite all other queries to the front controller.
RewriteRule ^ %{ENV:BASE}/app.php [L]
</IfModule>
<IfModule !mod_rewrite.c>
<IfModule mod_alias.c>
# When mod_rewrite is not available, we instruct a temporary redirect of
# the start page to the front controller explicitly so that the website
# and the generated links can still be used.
RedirectMatch 302 ^/$ /app.php/
# RedirectTemp cannot be used instead
</IfModule>
</IfModule>
///WAP-redirect, based upon accepted file type
RewriteCond %{HTTP_ACCEPT} (x-)*(application|text)/(x-)*(vnd[-.])*(wap[-.]|wml)+
RewriteRule ^(index.html)*$ index.wml [L]
# http://www.mysite.com/keyword/j1_1/j2_2/j3_3/j4_4/j5_5/j6_6/j7_7/... -> http://www.mysite.com/index.php?j1=1&j2=2&j4=4&j5=5&j7=7...
# http://www.mysite.com/keyword/j2_2/j3_3/j4_4/j5_5/j6_6/j7_7/... -> http://www.mysite.com/index.php?j2=2&j4=4&j5=5&j7=7...
# Skip following section if not a "keyword/" request
rewriterule !^keyword/ - [S=9]
#
# Else copy/append keywords to user-defined variable "tQuery"
rewriterule ^keyword(/[^/]+)*/j1_([^/]+) - [NC,E=tQuery:%{ENV:tQuery}j1=$2]
rewriterule ^keyword(/[^/]+)*/j2_([^/]+) - [NC,E=tQuery:%{ENV:tQuery}&j2=$2]
rewriterule ^keyword(/[^/]+)*/j3_([^/]+) - [NC,E=tQuery:%{ENV:tQuery}&j3=$2]
rewriterule ^keyword(/[^/]+)*/j4_([^/]+) - [NC,E=tQuery:%{ENV:tQuery}&j4=$2]
rewriterule ^keyword(/[^/]+)*/j5_([^/]+) - [NC,E=tQuery:%{ENV:tQuery}&j5=$2]
rewriterule ^keyword(/[^/]+)*/j6_([^/]+) - [NC,E=tQuery:%{ENV:tQuery}&j6=$2]
rewriterule ^keyword(/[^/]+)*/j7_([^/]+) - [NC,E=tQuery:%{ENV:tQuery}&j7=$2]
#
# Strip leading "&" from tQuery (if any)
RewriteCond %{ENV:tQuery} ^&(.+)$
rewriterule ^keyword/ - [NC,E=tQuery:%1]
# Tweaked to look for city name (if any)
rewriterule ^keyword(_[^/]+)? - [NC,E=tQuery:%1]
# Rewrite the URL-path to index.php query format
# Tweaked to look for city name (if any)
rewriterule ^keyword(_[^/]+)? /index.php?%{ENV:tQuery} [NC,L]
# http://mysite.com/keyword/test/page2 -> http://mysite.com/data.php?method=keyword&criteria=test&page=2
# http://mysite.com/date/06-05-02 -> http://mysite.com/data.php?method=keyword&criteria=06-05-02
# Skip following section if not a "keyword/" request
rewriterule !^(keyword|date|category) - [skip=4]
# Else copy/append keywords to user-defined variable "tmpQuery"
RewriteRule ^(keyword|date|category).*$ - [env=tmpQuery:%{ENV:tmpQuery}method=$1]
RewriteRule ^(keyword|date|category)/([^/]*).*$ - [env=tmpQuery:%{ENV:tmpQuery}&criteria=$2]
RewriteRule ^(keyword|date|category)/([^/]*)/page(\d+).*$ - [env=tmpQuery:%{ENV:tmpQuery}&page=$3]
# Rewrite the URL-path to data.php query format
rewriterule ^(keyword|date|category).* /data.php?%{ENV:tmpQuery} [last]
# http://www.dracos.co.uk/code/apache-rewrite-problem/
/// Rewrite by accept language given by client
RewriteEngine on
RewriteCond %{HTTP:Accept-Language} (sv) [NC]
RewriteRule .* http://www.sverige.hms-solutions.com [R,L]
RewriteCond %{HTTP:Accept-Language} (nn) [NC]
RewriteRule .* http://www.norge.hms-solutions.com [R,L]
RewriteCond %{HTTP:Accept-Language} (da) [NC]
RewriteRule .* http://www.danmark.hms-solutions.com [R,L]
RewriteCond %{HTTP:Accept-Language} (en) [NC]
RewriteRule .* http://www.english.hms-solutions.com [R,L]
RewriteCond %{HTTP:Accept-Language} .* [NC]
RewriteRule .* http://www.international.hms-solutions.com [R,L]
# In the .htaccess
<IfModule mod_alias.c>
# Use status "gone" for 410 (HTTP status)
# Note: if you need to match query string, use mod_rewrite instead.
# See https://web.archive.org/web/20220820210007/https://simonecarletti.com/blog/2009/01/apache-query-string-redirects/
Redirect gone /a
Redirect gone /b
RedirectMatch gone \.gif$
</IfModule>
# This code sends the Set-Cookie header to create a cookie on the client with the value of a matching item in 2nd parantheses.
RewriteRule ^(.*)(de|es|fr|it|ja|ru|en)/$ - [co=lang:$2:.askapache.com:7200:/]
<IfModule mod_expires.c>
ExpiresActive on
# Perhaps better to allowlist expires rules? Perhaps.
ExpiresDefault "access plus 1 month"
# cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5)
ExpiresByType text/cache-manifest "access plus 0 seconds"
# Your document html
ExpiresByType text/html "access plus 0 seconds"
# CSS and JavaScript
ExpiresByType text/css "access plus 1 year"
ExpiresByType text/javascript "access plus 1 year"
# Data
ExpiresByType text/xml "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/ld+json "access plus 0 seconds"
ExpiresByType application/vnd.geo+json "access plus 0 seconds"
# Feed
ExpiresByType application/rss+xml "access plus 1 hour"
ExpiresByType application/atom+xml "access plus 1 hour"
# Favicon (cannot be renamed)
ExpiresByType image/x-icon "access plus 1 week"
# Manifest files
ExpiresByType application/manifest+json "access plus 1 year"
ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
ExpiresByType text/cache-manifest "access plus 0 seconds"
# Media: images, video, audio
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"
ExpiresByType video/ogg "access plus 1 month"
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
# Webfonts
ExpiresByType application/font-woff "access plus 1 month"
ExpiresByType application/font-woff2 "access plus 1 month"
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
ExpiresByType application/x-font-ttf "access plus 1 month"
ExpiresByType font/opentype "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
</IfModule>
# 1 YEAR
<FilesMatch "\.(ico|pdf|flv)$">
Header set Cache-Control "max-age=29030400, public"
</FilesMatch>
# 1 WEEK
<FilesMatch "\.(jpg|jpeg|png|gif|swf)$">
Header set Cache-Control "max-age=604800, public"
</FilesMatch>
# 2 DAYS
<FilesMatch "\.(xml|txt|css|js)$">
Header set Cache-Control "max-age=172800, proxy-revalidate"
</FilesMatch>
# 1 MIN
<FilesMatch "\.(html|htm|php)$">
Header set Cache-Control "max-age=60, private, proxy-revalidate"
</FilesMatch>
ETag removal
# FileETag None is not enough for every server.
<IfModule mod_headers.c>
Header unset ETag
</IfModule>
# Since we're sending far-future expires, we don't need ETags for
# static content.
# developer.yahoo.com/performance/rules.html#etags
FileETag None
This limitation of mod_deflate is prominently mentioned in the documentation, which recommends using mod_rewrite to rewrite requests to their compressed alternatives when appropriate. Although this method can work [...] it has the major drawback that you are reimplementing content negotiation (which mod_negotiation was designed to do) and are likely to get it wrong and lack features supported by mod_negotiation. Some common problems and pitfalls with this approach:
Sending an incorrect or missing Content-Encoding header.
Not sending the Vary header or setting it incorrectly (overwriting previous values for other headers which cause the response to vary).
Sending Content-Type: application/x-gzip instead of the underlying type.
Sending double-gzipped content due to forgetting to set no-gzip in the environment to exclude the response from mod_deflate.
Not respecting client preferences (i.e. quality values/qvalues). According to RFC 7231 (and RFC 2616 before it) clients can send a numeric value between 0 and 1 (inclusive) to express their relative preference for each encoding. An Accept-Encoding: gzip;q=0 header would signify that the client wants “anything but gzip”. Most mod_rewrite implementations would send them gzip. A more realistic example would be a client that sends Accept-Encoding: br;q=1, gzip;q=0.5, deflate;q=0.1 to signify that they prefer Brotli, then gzip, then deflate. Writing mod_rewrite rules which properly handle these sorts of expressed preferences is extremely difficult.
# If the browser accepts gzip/br and the requested file exists under pre-encoded version, then serve that version directly.
<IfModule mod_rewrite.c>
<IfModule mod_headers.c>
# Brotli
<IfModule mod_brotli.c>
RewriteEngine On
RewriteCond %{HTTP:Accept-Encoding} br
RewriteCond %{REQUEST_FILENAME}.br -f
RewriteRule (.*) $1.br [L,E=no-brotli:1,E=PRE_ENCODED_CODING:br]
</IfModule>
# Deflate / Gzip
<IfModule mod_deflate.c>
RewriteEngine On
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule (.*) $1.gz [L,E=no-gzip:1,E=PRE_ENCODED_CODING:gzip]
# Special case for *.svg -> *.svgz
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME} .svg$
RewriteCond %{REQUEST_FILENAME}z -f
RewriteRule (.*) $1z [L,E=no-gzip:1,E=PRE_ENCODED_CODING:gzip]
</IfModule>
# Special case for internal redirection (pre encoded) response only
# For "REDIRECT_" env var prefix see https://stackoverflow.com/questions/3050444
# Some directives support env vars conditions (Header does, but RemoveType doesn't)
<If "-n env('REDIRECT_PRE_ENCODED_CODING')">
# Debian (and related distributions) set AddType application/x-gzip gz in their default conf (/etc/apache2/mods-available/mime.conf)
# or you the Content-Type will be override to application/x-gzip
RemoveType gz
Header always set Content-Encoding %{REDIRECT_PRE_ENCODED_CODING}e
# Apache already append Accept-Encoding to Vary http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritecond
</If>
</IfModule>
</IfModule>
You need to restrict content negotiation by include directives in a <Directory>, <Files> or .htaccess for a subset of directories, file types.
The major drawback, only requests for files which do not exist are negotiated. That means you need to rename uncompressed files for an additional extension (ex: index.html.html and index.html.gz for https://example.com/index.html) which is not pratical.
# Let Apache choose media type, encoding, etc. based on client preferences.
# Need rewrite to force use pre encoded version because MultiViews only negotiates requests for files which do not exist.
<IfModule mod_mime.c>
<IfModule mod_rewrite.c>
# Brotli
<IfModule mod_brotli.c>
Options +MultiViews
RewriteEngine On
# Note: BR is a RFC 3066 language
RemoveLanguage br
AddEncoding br br
</IfModule>
# Deflate / Gzip
<IfModule mod_deflate.c>
Options +MultiViews
RewriteEngine On
# Case for * -> *.gz
# Debian (and related distributions) set AddType application/x-gzip gz in their default conf (/etc/apache2/mods-available/mime.conf)
RemoveType .gz
AddEncoding gzip gz
# Case for *.svg -> .svgz
AddEncoding gzip svgz
</IfModule>
</IfModule>
</IfModule>
# Protect files and directories from prying eyes.
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock))$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save)$">
Order allow,deny
</FilesMatch>
# PHP 5 settings, Apache 1 and 2.
<IfModule mod_php5.c>
php_flag magic_quotes_gpc off
php_flag magic_quotes_sybase off
php_flag register_globals off
php_flag session.auto_start off
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_flag mbstring.encoding_translation off
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine on
# Set "protossl" to "s" if we were accessed via https://. This is used later
# if you enable "www." stripping or enforcement, in order to ensure that
# you don't bounce between http and https.
RewriteRule ^ - [E=protossl]
RewriteCond %{HTTPS} on
RewriteRule ^ - [E=protossl:s]
# Make sure Authorization HTTP header is available to PHP
# even when running as CGI or FastCGI.
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Block access to "hidden" directories whose names begin with a period. This
# includes directories used by version control systems such as Subversion or
# Git to store control files. Files whose names begin with a period, as well
# as the control files used by CVS, are protected by the FilesMatch directive
# above.
#
# NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is
# not possible to block access to entire directories from .htaccess, because
# <DirectoryMatch> is not allowed here.
#
# If you do not have mod_rewrite installed, you should remove these
# directories from your webroot or otherwise protect them from being
# downloaded.
RewriteRule "(^|/)\." - [F]
# If your site can be accessed both with and without the 'www.' prefix, you
# can use one of the following settings to redirect users to your preferred
# URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
#
# To redirect all users to access the site WITH the 'www.' prefix,
# (http://example.com/... will be redirected to http://www.example.com/...)
# uncomment the following:
# RewriteCond %{HTTP_HOST} .
# RewriteCond %{HTTP_HOST} !^www\. [NC]
# RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
#
# To redirect all users to access the site WITHOUT the 'www.' prefix,
# (http://www.example.com/... will be redirected to http://example.com/...)
# uncomment the following:
# RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
# RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]
</IfModule>
Special chars with RewriteRule
Ex: a file or folder named tést $a? (te%CC%81st%20%24a%3F) is translated internaly to te\xcc\x81st $a (discarding handling ?)
Potential solution: use flag B or double URL encoding ?