How should I write these rewrite rules in my .htaccess file - linux

I have this in my .htaccess file:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^admin\.domain1\.com$ [OR]
RewriteCond %{HTTP_HOST} ^admin\.domain2\.com$ [OR]
RewriteCond %{HTTP_HOST} ^admin\.domain3\.com$
RewriteRule (.*) /path/to/directory/$1 [L]
RewriteCond %{HTTP_HOST} ^admin\.domain4\.com$
RewriteRule (.*) /different/path/$1 [L]
The first block works, but the second block gives me the following error:
Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator at [no address given] to inform them of the time this error occurred, and the actions you performed just before this error.
More information about this error may be available in the server error log.
Apache/2.4.29 (Ubuntu) Server at admin.domain4.com.ar Port 80
What am I doing wrong??

The first block works, but the second block gives me an Internal Server error:
Both rules/blocks have the same "issue". The first rule will only "work" if you have another .htaccess file in the /path/to/directory/ that also uses mod_rewrite (perhaps to route the URL?) in order to prevent an internal rewrite loop (the probable cause of your Internal Server Error).
The easiest way to resolve this, if you are on Apache 2.4, is it simply use the END flag, instead of L. For example:
RewriteRule (.*) /different/path/$1 [END]
The L flag only stops the current round of processing, it does not stop all processing by the rewrite engine. On the second pass through the rewrite engine, the pattern (.*) also matches the rewritten request /different/path/<url> which gets further rewritten to /different/path/different/path/<url> etc. resulting in an endless loop, unless there is something to stop it.

Related

Aapche2 htaccess RewriteCond with ErrorDocument 404

Aapche2 htaccess RewriteCond
i found some wondering Condition that works , but i dont know why :)
Files
/error.php
/donate.php
/test/index.php
in a htaccess file i use
ErrorDocument 404 /error.php
RewriteEngine On
# WHY THIS LINE NEEDED TO GET IT WORKS
RewriteCond %{REQUEST_FILENAME} ^$
#
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) $1.php [L]
this .
http://localhost/donate calls internal http://localhost/donate.php
while
http://localhost/donate1 calls internal http://localhost/error.php
and
http://localhost/test/ call internal http://localhost/test/index.php
so far so good
but when i comment it out
#RewriteCond %{REQUEST_FILENAME} ^$
then i get internal server error while call
/donate1 and not the /error.php
can someone explain the steps , why this happens ?
#RewriteCond %{REQUEST_FILENAME} ^$
Because your directives are not actually doing what you think they are doing. In fact, with that "hacky" condition uncommented they are not doing anything at all, except to prevent the 500 Internal Server Error (which is due to an internal rewrite loop because the rule is strictly incorrect).
That condition checks if the REQUEST_FILENAME server variable is empty. It is never empty, so always fails, so the RewriteRule directive that follows is never triggered.
You could remove your mod_rewrite directives entirely and you'll get the same results.
http://localhost/donate calls internal http://localhost/donate.php
It's most probably MultiViews (mod_negotiation) that is rewriting /donate to /donate.php. Not the directives you posted (which, as I mentioned, don't actually do anything).
http://localhost/test/ call internal http://localhost/test/index.php
This is caused by mod_dir (DirectoryIndex). Again, nothing to do with the directives you posted.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) $1.php [L]
then i get internal server error while call /donate1 and not the /error.php
Because when you request /donate1 the above directives trigger an internal rewrite loop (which results in a 500 Internal Server Error response). /donate1 to /donate1.php to /donate1.php.php to /donate1.php.php.php etc. (see below).
MultiViews does not apply here because there is no file that /donate1 can perceivably map to, eg. /donate1.php or /donate1.html or some other recognised resource, with a different file extension, that returns a text/html mime-type.
When you request /donate1 the following happens.
/donate1 does not map to a directory (1st condition) or a file (2nd condition) so is internally rewritten by this rule to donate1.php. Which is incorrect (but that is what this rule does).
The L flag then causes the current round of processing to stop and the rewrite engine starts over, passing the rewritten URL, ie. donate1.php back into the mix.
/donate1.php does not map to a directory or file so is rewritten to donate1.php.php.
The rewrite engine starts over...
/donate1.php.php does not map to a directory or file so is rewritten to donate1.php.php.php.
The rewrite engine starts over...
etc.
This repeats until 10 (default) internal rewrites are reached and the server "breaks" with a 500 error response. The server error log would contain the details of this error, for example:
AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.
(Although very rarely would you ever need to change this internal redirect limit - it nearly always indicates an error in your script.)
Solution
You either remove your mod_rewrite directives entirely and just let MultiViews do its thing, OR you disable MultiViews and "correct" your mod_rewrite directives.
For example:
Options -MultiViews
ErrorDocument 404 /error.php
RewriteEngine On
# Rewrite extensionless URLs to ".php" if they exist
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule (.+) $1.php [L]
An optimisation... if your URLs (that map to .php files) don't contain dots then you could simply exclude URLs that contain dots so you don't unnecessarily test requests for your static resources (eg. image.jpg, styles.css, etc.) that already include a file extension (which naturally contain a dot before the file extension):
RewriteRule ([^.]+) $1.php [L]
Reference:
https://httpd.apache.org/docs/2.4/content-negotiation.html

Why is there a error 500 on a Subdomain to folder

This is what's inside the .htaccess file:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(.*)\.domain\.com
RewriteRule ^(.*)$ Subdomains/%1/$1 [L,NC,QSA]
When I'm on the domain (eg: x.example.com), it displays error 500. What am I doing wrong?
Example:
x.example.com -> show example.com/Subdomains/x but keep the URL of x.example.com
y.example.com -> show example.com/Subdomains/y but keep the URL of y.example.com
z.example.com -> show example.com/Subdomains/z but keep the URL of z.example.com
RewriteCond %{HTTP_HOST} ^(.*)\.example\.com
RewriteRule ^(.*)$ Subdomains/%1/$1 [L,NC,QSA]
Without any other directives to prevent it, the above will result in an internal rewrite loop, which will result in a 500 Internal Server Error response to the browser.
For example, if you request https://sub.example.com/foo then...
Request is rewritten to /Subdomains/sub/foo.
In a directory context (ie. htaccess) the rewriting process starts over, passing /Subdomains/sub/foo as in the input to the next round of processing...
Request is rewritten to /Subdomains/sub/Subdomains/sub/foo.
The rewriting process starts over...
Request is rewritten to /Subdomains/sub/Subdomains/sub/Subdomains/sub/foo.
The rewriting process starts over...
etc. until the server gives up. (Default is after 10 internal rewrites.)
The L flag does not stop all processing in a directory context (ie. htaccess). It simply stops the current round of processing. The rewrite process continues until the URL passes through unchanged.
The quick fix on Apache 2.4 is to simply replace the L flag with END. This causes all processing to stop. No further passes through the rewrite engine occur.
For example:
RewriteCond %{HTTP_HOST} ^(.*)\.example\.com
RewriteRule (.*) Subdomains/%1/$1 [END]
The NC and QSA flags are superfluous here.
Note that the regex ^(.*)\.example\.com matches any number of sub-subdomains, including www itself, if that is a concern?
Without using the END flag (Apache 2.4) then you would need to explicitly check that you have not already rewritten the URL. One way is to prevent any further rewrites if /Subdomains/.... has already been requested:
RewriteCond %{HTTP_HOST} ^(.*)\.example\.com
RewriteRule ^((?!Subdomains/).*)$ Subdomains/%1/$1 [L]
Note that if you have another .htaccess file located in the /Subdomains/<sub> subdirectory that also uses mod_rewrite then this will have also prevented a rewrite loop since mod_rewrite directives are not inherited by default.
UPDATE: Is there a way to check if the folder dosent exist then just redirect to domain.com?
Yes, you can do this, for example:
# If subdomain does not map to a subdirectory then redirect to root of main domain
RewriteCond %{HTTP_HOST} ^(.*)\.(example\.com)
RewriteCond %{DOCUMENT_ROOT}/Subdomains/%1 !-d
RewriteRule ^ https://%2/ [R=302,L]
# Otherwise, internally rewrite the request to subdirectory (if not already)
RewriteCond %{HTTP_HOST} ^(.*)\.example\.com
RewriteRule ^((?!Subdomains/).*)$ Subdomains/%1/$1 [L]
However, this is arguably detrimental to your users. A custom 404 response might be preferable.

Surprising rewriting of URL by htaccess rule

I've zeroed my problem and I've specific question.
With only the following code in the .httaccess why index2.php gets called if I type in my URL as www.mysite.com/url2 ?
RewriteEngine On
RewriteCond %{REQUEST_URI} (.html|.htm|.feed|.pdf|.raw)$ [NC]
RewriteRule (.*) index2.php [L]
I've also tested it at http://www.regextester.com and should not replace it with index2.php:
In the end I want this rule to skip any URL starting with /url2 or /url2/*.
EDIT: I've made screen recording of this problem: http://screenr.com/BBBN
You have this in your .htaccess:
RewriteEngine On
RewriteCond %{REQUEST_URI} (.html|.htm|.feed|.pdf|.raw)$ [NC]
RewriteRule (.*) index2.php [L]
What it does? it rewrites anything that ends with html, htm, feed , pdf , raw to index2.php. So, if you are getting results as your URL is ends with those extensions, then there are two possible answers:
There is another rewrite rule in an .htaccess in upper directories (or in server config files) that causes the URL to be rewritten.
Your URL actually ends with those extensions. have in mind, what you enter in your address bar, will be edited and rewritten. For example, if you enter www.mysite.com/url2 in your address bar and that file doesn't exist on server, your server will try to load the proper error document. So, if your error document is /404.html, it will be rewritten to index2.php at the end.
Update:
I think it's the case. create a file named 404.php in your document root. Inside your main .htaccess (in your document root), put this:
ErrorDocument 404 /404.php
delete all other ErrorDocument directives.
inside 404.php , put this:
<?php
echo 'From 404.php file';
?>
Logic behind it:
When you have a weird behavior in mod_rewrite, the best solution in my experience is using rewrite log. to enable rewrite log put this in your virtualhost or other server config directives you may choose:
RewriteLogLevel 9
RewriteLog "logs/RewriteLog.log"
be careful: the code above will enable rewrite log and start logging at highest level possible (logging everything). It will decrease your server speed and the log file will become huge very quickly. Do this only on your dev server.
Explanation: When you try to access www.mysite.com/url2, Apache gives your URL to rewrite module. Rewrite module checks if any of RewriteRules applies to your URL. Because you have one rule and it doesn't apply to your URL, it tries to load the normal file. But this file does not exit. So, Apache will do the next step which is showing the proper error message. When you set a custom error file, Apache will run the test against the new address. For example if error document is /404.html, Apache checks whether your rule applies to /404.html or not. Since it does, it will rewrite it.
The point to remember is apache will do this every time there is change in URL, whether the change is made by rewrite module or not!
The rule you list should work as you expect if this is the only rule. Fact is that theory is fun, but apparently it doesn't work as expected. Please note that . will match ANY CHARACTER. If you want to match the full stop/period character, you'll need to escape it. That's why I use \.(html|htm|feed|pdf|raw)$ instead of (.html|.htm|.feed|.pdf|.raw)$ below.
You can add another RewriteCond that simply doesn't match if the url starts with /url2, like below. This might not be a viable solution if there are lots of urls that shouldn't be matched.
RewriteCond %{REQUEST_URI} !^/url2
RewriteCond %{REQUEST_URI} \.(html|htm|feed|pdf|raw)$ [NC]
RewriteRule (.*) index2.php [L]
To get a better understanding of what is happening you can alter the rule to something like this. Now simply enter the urls you dont want to be matched in the url bar and inspect the url bar after the redirect happens. In the url-parameter you now see what url actually triggered this rule to match. This screencast shows you a similar version working with a sneaky rewriterule that is working away on the url.
#A way of finding out what is -actually- matched
RewriteCond %{REQUEST_URI} \.(html|htm|feed|pdf|raw)$ [NC]
RewriteCond %{REQUEST_URI} !/foo
RewriteRule (.*) /foo?url=$1 [R,L]
You can decide to match the %{THE_REQUEST} variable instead. This will always contain the request itself. If something else is rewriting the url, this variable doesn't change, meaning you can use this to overwrite any changes. Make sure the url won't be matching itself. You would get something like below. An example screencast can be found here.
#If it doesn't end on .html/htm/feed etc, this one won't match
RewriteCond %{THE_REQUEST} ^(GET|POST)\ /.*\.(html|htm|feed|pdf|raw)\ HTTP [NC]
RewriteCond %{REQUEST_URI} !^/index2\.php$
RewriteRule (.*) /index2.php [L]

mod_rewrite failing on uppercase dir

I have a very basic mod_rewrite in a .htaccess file which I'm sure worked last time I looked at it, but now it is doing strange things with the case of the REQUEST_URI. It's intended purpose is to rewrite sub-domains to a given file, passing the subdomain as a php var of bnurl. Here is my code:
RewriteCond %{REQUEST_URI}= "RSDEV/location/" [NC]
RewriteCond %{HTTP_HOST} .
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTP_HOST} ^([^.]+)\.mydomain\.co\.uk(:80)? [NC]
RewriteRule ^RSDEV/location/$ RSDEV/newmain.php?bnurl=%1&accesstype=new [NC,L]
Now, typing joebloggs.mydomain.co.uk/RSDEV/location/ into my web browser comes back with the response "The requested URL /RSDEV/location/ was not found on this server" which is a correct statement because /RSDEV/location/ is not a real directory, but why did it not rewrite to RSDEV/newmain.php?bnurl=joebloggs&accesstype=new as expected?
Now, the really strange thing here is that if I enter joebloggs.mydomain.co.uk/rsdev/location/ into my browser (note rsdev is now lowercase), it correctly rewrites as expected. The script newmain.php is in dir RSDEV (uppercase) so if it was going to fail, I would have expected it to fail the other way round with the lowercase rsdev.
As you can see, I have [NC] on each line. Is this my mod_rewrite code failing or some other mystical server force keeping me up all night?
Get rid of the line:
RewriteCond %{REQUEST_URI}= "RSDEV/location/" [NC]
The check is already being made in the rewrite rule's pattern. Not just that, the = is connected to the %{REQUEST_URI} variable, so the string ends up with a = at the end (it should really be next to the pattern).

.htacces RewriteRule server error 500

I have this
RewriteRule ^(.*) public/$1 [NC,L]
in my .htacces file and i get Internal Server Error 500
Can someone help me?
And explain why do i get it.
Because you wrote your rules in such way that they create infinite rewrite loop which Apache has to break at some point, hence the 500 Internal Server Error. If you check your error log you will see exact error message.
The [L] flag does not necessary mean "rewrite done" -- it just means "rewrite done on this iteration -- lets go again from start".
Very useful to read: RewriteRule Last [L] flag not working?
To solve your problem -- you need to add some condition so that already rewritten rules are not get rewritten again and again. These rules should do the job for you (one of the possible solutions -- it all depends on how your actual project is set up):
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_URI} !^/public/
RewriteRule ^(.*)$ public/$1 [L]
With this rule if URL is already rewritten or starts with /public/ straight away then no additional rewrite will occur.

Resources