-------------------------------------------------------------------------------
Rewrite rules are a pain.
WARNING: "Options +FollowSymlinks" Must be enabled for mod_rewrite to work!
Some servers may enable this at root level but disallow it in .htaccess
Causing a 500 error. If that is the case just remove it and the rewrite
rules will continue to work.
Offical Guide
https://httpd.apache.org/docs/current/rewrite/intro.html
Best guides so far...
https://www.whoishostingthis.com/compare/mod-rewrite__trashed/cheat-sheet/
+ it's resource references at the bottom
http://borkweb.com/story/apache-rewrite-cheatsheet
(this has a flow chart, on how URL is processed)
http://dk.co.nz/seo
https://www.cheatography.com/davechild/cheat-sheets/mod-rewrite/
http://findthingy.com/htaccess-rewriterule-cheat-sheet/
https://serverfault.com/questions/214512/
https://www.askapache.com/htaccess/modrewrite-tips-tricks/
-------------------------------------------------------------------------------
Notes and problems
* If the URL refers to a existing file/directory/symlink that can not be
accessed, then the server will give a 'Forbidden 403 Error" regardless!
* RewriteCond is part of the first RewriteRule that follows and no other.
Repeat the Condition for each rule needing them, or use 'Skip' flag.
Multiple conditions can be given.
* Limit Rules...
RewriteRule !\.(html|php)$ - [S=5]
... next 5 rules only apply to html and php requests! ...
* A substitution target of '-' means no change to URI file path.
* Rewrite on ".htaccess" or other per-directory configuration
* Based on the path relative to that directory (never a leading '/')
* To test against the full path use %{REQUEST_URI} in a Cond.
* Flags
[L] Last rule if success
[C] Chain rules success to next rule
[S=#] Skip the next # rules
[R,L] [R=301,L] Redirect client to this URL (always include 'L')
[F] Forbidden (403) (target should be '-', no modification)
[G] Gone (410) (target should be '-', no modification)
[P] Do a proxy request for resulting URL
[NC] Not Case sensitive
[NE] do not escape characters using hex codes in result
[OR] Or this condition to next condition
[QSA] include parameters to query string in result
[N] restart rewrite rules (can cause loops)
[PT] Result is a URI to be re-parsed from the start.
Without this a '/path/to/file' may retrieve that
file from DocumentRoot, regardless of Aliases, etc...
EG: return an otherwise forbidden 'dot' file!
EG: a redirect to '/icons' or '/cgi-bin' needs this.
This is IMPLIED in per-directory contexts!
* WARNING: Regex is PCRE, and as such '.' in the regex will also match '/'
* The regex in RewriteRule is looked at FIRST, then the RewriteCond
That way any backrefs can be used (using $#) in RewriteCond.
Only after RewriteCond succeeds, is the substitution pattern is performed
so backrefs in the Cond can then be used there (using %#).
See "apache_rewrite_backrefs.jpg" for a graphical example of how this works.
Also see diagram in http://borkweb.com/story/apache-rewrite-cheatsheet
-------------------------------------------------------------------------------
Debugging...
When things are not working add...
#
# ONLY FOR TESTING REWRITE RULES!!!!!
#
RewriteLog "/tmp/rewrite.log"
#RewriteLogLevel 9
RewriteLogLevel 3
# In Apache 2.4 use
# ???
Now load the page, immediately hit 'STOP' on the browser and restart
your apache within a couple of seconds.
As setting of 1 has almost no information, 5 is useful, 3 will probably have
enough info, 9 is gigabytes of information and can seriously impact the server
Alturnative...
Output some string, as a Redirect URL, then look at result in client
Examples..
# Output the variable %{HTTP_REFERER}
RewriteRule . "http://__%{HTTP_REFERER}__" [R,L,NE]
# Or testing a condition
RewriteCond "%{HTTP_REFERER}" "s357751\.elf\." [NC]
RewriteRule . "http://__match__" [R,L,NE]
# The above let ne discover that...
# this works
RewriteCond "%{HTTP_REFERER}" "https?://s357751\.elf\.ict\." [NC]
# this does not
RewriteCond "%{HTTP_REFERER}" "https?://%{SERVER_NAME}" [NC]
Seem you can not use %{..} variables in the conditional part!
-------------------------------------------------------------------------------
Redirect for a moved file or suffix
(withing ".htaccess" files)
# FAILS - Simple redirect...
#Redirect permanent script.hints general.txt
# This will redirect suffixes for this and all sub-directories!
RedirectMatch "^(.*)\.hints$" "$1.txt"
# HOWEVER the full URI is given for matching...
# That measn it always starts with '/'
# You can check what it w using curl with this...
RedirectMatch "^(.*)\.hints$" "$1==$1==.txt"
# The URI is completely replaced by target so ^ is needed
# Be sure to include '$' in match too
# File Rename...
RedirectMatch "^(.*)/script\.hints$" "$1/general.txt"
# Sub-Directory Rename...
# Is the '(.*)/' prefix actually needed (unless also renaming subdirs)?
RedirectMatch "^(.*)/old_dir$" "$1/newdir/"
RedirectMatch "^(.*)/old_dir/(.*)$" "$1/newdir/$2"
# with a file rename as well...
RedirectMatch "^(.*)/old_dir/old_file$" "$1/newdir/new_file"
# -----------------
# The Rewrite engine in per-directory context (.htaccess) matches
# against the URI with the path removed (never starts with '/').
# The path removed is the 'base' and can be changed using RewriteBase
Options +FollowSymLinks
RewriteEngine on
# Check match results... (redirect back to client to make it visible)
RewriteRule "^(.*)\.hints$" "==$1==.txt" [R,L]
# the following gets the file directly without redirecting the client...
# The previous RedirectMatch for Suffix changing is thus equivelent to
RewriteRule "^(.*)\.hints$" "$1.txt"
# To stop it changing suffixes in a subdirectory use...
RewriteRule "^([^/]*)\.hints$" "$1.txt"
# redirect a moved file
RewriteRule ^oldname\.hints$ newname.txt
#------------------
# General Rewrite Rule redirection
#
# Portable filename redirections for ".htaccess" files.
# The Chaining rules is used to replace the current PATH base prefix
# with the actual REQUEST_URI to this directory, making it portable.
#
Options +FollowSymLinks
RewriteEngine on
# Make it work more like RedirectMatch
# suffix redirect
RewriteRule \.hints$ %{REQUEST_URI} [C]
RewriteRule ^(.*)\.hints$ $1.txt [R,L]
# filename redirect
RewriteRule ^client_hints\.txt$ %{REQUEST_URI} [C]
RewriteRule ^(.*)/client_hints\.txt$ $1/mail_sending.txt [R=301,L]
# The above may be improved using Rewrite Conditions and %# substitution
WARNING: directory lookup without '/' will redirect to one with '/'
before .htaccess redirects are applied! which may cause problems.
-------------------------------------------------------------------------------
Redirect non-existant files
If the request does not map to a file, or directory, temporary redirect it
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ /error_no_such_file.html [R=302,L,QSD]
UNTESTED
Using 'S' skip flag...
# Is the request for a non-existent file?
RewriteCond "%{REQUEST_FILENAME}" "!-f"
RewriteCond "%{REQUEST_FILENAME}" "!-d"
# If so, skip these two RewriteRules
RewriteRule ".?" "-" [S=2]
RewriteRule "(.*\.gif)" "images.php?$1"
RewriteRule "(.*\.html)" "docs.php?$1"
-------------------------------------------------------------------------------
Deny access to specific files and sub-directories (.htaccess)
For example anything starting with a '.' or the "cache"
# Restrict access to sub-directories
RewriteRule ^\..*/ - [F,L]
RewriteRule ^cache/ - [F,L]
Note if you tried to access cache without the final '/' you will get a
redirect to add the '/' (informing user of the sub-directories existence)
but the redirect will then be "403 Forbidden".
NOTE you can do this with "RedirectMatch" but that needs the full URI
path which is no good for a ".htaccess" file
-------------------------------------------------------------------------------
Map directory from HTTP to HTTPS (that is force SSL)
NOTE: this does not work in ".htaccess" files that also require authentication
and will outright fail if authentication enforces SSL using "SSLRequireSSL".
The problem is that in ".htaccess" any authentication will be performed BEFORE
the rewrite!
=======8<--------
# This will enable the Rewrite capabilities
RewriteEngine On
# This checks to make sure the connection is not already HTTPS
# There is no space between '!=' and 'on'
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{SERVER_PORT} ^80$
# This rule will redirect users from HTTP to HTTPS protocols
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [R,L]
=======8<--------
If Authentication is required you are better of enforcing the SSL requirement
in the server configuration, so that it is enforced BEFORE the authentication
via ".htaccess".
That is in server configuration...
=======8<--------
...
# Force use of HTTPS
RewriteEngine On
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{SERVER_PORT} ^80$
RewriteRule ^ https://%{SERVER_NAME}/%{REQUEST_URI} [R,L]
...
=======8<--------
Or for a whole VHost website..
=======8<--------
...
RewriteEngine On
RewriteRule ^ https://%{SERVER_NAME}/%{REQUEST_URI} [R,L]
...
-------------------------------------------------------------------------------
# Redirect directory - regardless of directory/server location!
#
# Note the follow standard method for directory renames..
#
# RewriteRule ^olddir/(.*)$ newdir/$1 [R,L]
#
# However for a users home directory it fails, basically due to expandsion
# of any "alias" or "~name" in the URL, to a directory that is different
# to the original URI path recieved.
#
# As such we need to replace the current directoy path with the
# original URI before we can modify and redirect the browser.
#
# Normally this is solved by using a "RewriteBase", but that hardcodes the
# location, which may be different for the same file served or installed on
# different machines.
#
# The ::: marker is needed to tie the two rules together.
#
RewriteRule ^olddir/.*$ :::%{REQUEST_URI}::: [C]
RewriteRule ^:::(.*)/olddir/(.*)::: $1/newdir/$2 [R,L]
# [C] chains the rules so next only applys if this rule worked
# For testing use [R] instead of [R=301] until you have finished testing.
#
# Once the browser has been "redirected permanently" (301) to the wrong
# address, if you then go on to continue testing the rule, your browser will
# still be redirected to the old address (because it's a browser thing), and
# you may even go on to fix, and then break the rule all over again without
# ever knowing it. Changes to 301 redirects can take a long time to show up in
# your browser.
-------------------------------------------------------------------------------
Images can only be used on this servers pages
(by Referre which hacked clients could lie about)
Note [F] send a Forbidden 403 Error
[NC] no case
[OR] or with next cond
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://${SERVER_NAME}(/|$) [NC]
RewriteRule \.(gif|jpg|jpeg|bmp|zip|rar|mp3|flv|swf|png|css|pdf)$ - [F]
or multiple servers using a common image repository
RewriteCond %{HTTP_REFERER} ^https?://this-server\.com(/|$) [NC,OR]
RewriteCond %{HTTP_REFERER} ^https?://that-server\.com(/|$) [NC]
RewriteRule \.(gif|jpg|jpeg|bmp|zip|rar|mp3|flv|swf|png|css|pdf)$ - [F]
UNTESTED
-------------------------------------------------------------------------------
# Make all access use a specific web server address
# EG redirect "example.com" to "www.example.com"
=======8<--------
RewriteCond %{HTTP_HOST} !^www\.example\.com [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteRule ^/(.*) https://www.example.com/$1 [L,R=301]
=======8<--------
Or require the "www" without hardcoding
=======8<--------
RewriteBase /
RewriteCond %{HTTP_HOST} !^www\.[a-z-]+\.[a-z]{2,6} [NC]
RewriteCond %{HTTP_HOST} ([a-z-]+\.[a-z]{2,6})$ [NC]
RewriteRule ^/(.*)$ http://www.%1/$1 [R=301,L]
=======8<--------
Redirect top level to a php script with path!
=======8<--------
RewriteEngine On
RewriteRule ^/$ https://%{HTTP_HOST}/index.php/pneumonia [R=301]
=======8<--------
Note....
flags: NC - case insensitive
C - chain to following rule/condition
L - last no more rules
R - redirect client - equivelent to R=302
Alternative (sub-directory)...
# Redirect any URL not accessing a site via a specific host to use
# that specific host ALWAYS
#
# EG: anything not using www.my.domain.com is redirected to that domain
#
# From Online .htaccess Editor
# http://www.htaccesseditor.com/en.shtml#a_WWW
#
Options +FollowSymlinks
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(example\.com/~anthony)(:80)? [NC]
RewriteRule ^(.*) http://www.example.com/~anthony/$1 [R=301,L]
-------------------------------------------------------------------------------
Site Specific Redirect...
# Redirect any access from www.cit.gu.edu.au for a sub-directory
# to its new offical web site http://www.imagemagick.org/Usage/
#
# This way the same file can be distributed without change to BOTH sites
#
# Any sub-path and query string is also added to the new URL.
# R = Redirect to external, L = last rewrite, QSA = include query string
#
RewriteEngine On
RewriteCond %{SERVER_NAME} ^www\.cit\.gu\.edu\.au$
RewriteRule ^(.*) http://www.imagemagick.org/Usage/$1 [R,L,QSA]
-------------------------------------------------------------------------------
For more cookbook examples see...
http://corz.org/serv/tricks/htaccess2.php
# 'flatting' script arguments, so they look like a directory structure.
# That is making a link look like a path when path components are script
# arguments
RewriteRule ^blog/([0-9]+)-([a-z]+) http://corz.org/blog/index.php?archive=$1-$2 [NC]
thus http://corz.org/blog/2003-nov
internally means http://corz.org/blog/index.php?archive=2003-nov
OR Shortening long URL's
Remove the 'www' from the URL host Note the use of %1 and $1
Options +FollowSymlinks
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.(.*) [NC]
RewriteRule ^(.*)$ http://%1/$1 [R=301,NC,L]
-------------------------------------------------------------------------------