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