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.
Related
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
This is my .htaccess
RewriteEngine On
# browser requests PHP
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^\ ]+)\.php
RewriteRule ^/?(.*)\.php$ /$1 [L,R=301]
# check to see if the request is for a PHP file:
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^/?(.*)$ /$1.php
RewriteCond %{REQUEST_URI} !^/(views|css|js|media|partials|php)
RewriteRule (.*) /views/$1
This is my project structure:
The idea is that all my HTML files are structured in the folder views, but I don't want my URL to be http://example.com/views/index but just http://example.com/index without the views-part.
This is working fine in the following case:
http://example.com/account/
But fails as soon as I try to access a file in the accounts-folder e.g: http://example.com/account/voeg-kind-toe
That results in a 404. Seems like this .htaccess solution only works for one-level directories.
Edit:
Interesting: If I place the bottom two lines on top (so placing the code to rewrite the view-part before the code to remove the php-extension); the php-extension works but the /views//part don't.
Create .htaccess like this
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(www.)?example.com$
RewriteCond %{REQUEST_URI} !^/views/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /views/$1
RewriteCond %{HTTP_HOST} ^(www.)?example.com$
RewriteRule ^(/)?$ views/ [L]
As mentioned in my comment above, providing there are no other directives/conflicts then this should still "work" in a roundabout way. However, there is an issue with the following directive in the order you have placed it:
# check to see if the request is for a PHP file:
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^/?(.*)$ /$1.php
You aren't testing for files in the /views subdirectory. But also, REQUEST_FILENAME does not necessarily contain what (I think) you think it does. When you request /account/voeg-kind-toe (an entirely virtual URL path) then REQUEST_FILENAME contains /account (it actually contains an absolute filesystem path, but I've kept it brief). So, the above is testing whether /account.php exists, not /account/voeg-kind-toe.php, or even /views/account/voeg-kind-toe.php - which is presumably the intention.
So, on the first pass, the above condition fails, no rewrite occurs, and processing continues...
The second rule then rewrites the request for /account/voeg-kind-toe to /views/account/voeg-kind-toe. Providing there are no further mod_rewrite directives, the rewrite engine then starts over. This time, /views/account/voeg-kind-toe is the input.
On the second pass, REQUEST_FILENAME is /views/account/voeg-kind-toe (since /views/account is a physical directory) and the request is rewritten to /views/account/voeg-kind-toe.php (since the filesystem check should be successful). Providing you have no other directives then processing should now stop.
This is working fine in the following case: http://example.com/account/
/account/ is simply rewritten to /views/account/ by the last rule.
Edit: Interesting: If I place the bottom two lines on top (so placing the code to rewrite the view-part before the code to remove the php-extension); the php-extension works but the /views//part don't.
The same process as above occurs, EXCEPT this all occurs in a single pass and so is less dependent on other directives that might occur later in the file.
I'm not sure what you mean by "the /views//part don't"?
Assuming you only have .php files within the /views directory and all URLs are intended to target the /views subdirectory and you don't need to reference directories directly then you could do this is a single directive and rewrite everything that does not contain (what looks like) a file extension to /views/<whatever>.php.
For example:
RewriteRule !\.\w{2,4}$ /views%{REQUEST_URI}.php [L]
The L (last) flag is required if you have other mod_rewrite directives that follow - to prevent additional processing.
This does mean you can't rely on the directory index. ie. You need to request /index (as in your example), not simply / to serve /views/index.php.
(You still need your first rule that removes .php from the requested URL - although this is only strictly necessary if you are changing an existing URL structure, where you previously used .php on the URLs.)
I have problem when I try to redirect and rewrite together.
I have site example.com/show_table.php?table=12 (max 99 tables). I wanted nice links, so I got this .htacces rw rule:
RewriteRule ^table/([0-9]{1,2})$ show_table.php?table=$1 [L,NC]
Now are links something like example.com/table/12 - it's definitely OK. But I want all old links redirect to new format. So I use Redirect 301, I added to .htaccess this code:
RewriteCond %{REQUEST_URI} show_table.php
RewriteCond %{QUERY_STRING} ^table=([0-9]{1,2})$
RewriteRule ^show_table\.php$ http://example.com/table/%1? [L,R=301,NC]
But when I visit example.com/show_table.php?table=12, I receive just redir-loop. I don't understant - the first is rewrite, the second is redirection, there ain't no two redirections. Do You see any error?
Thanks!
Instead of checking REQUEST_URI in the condition, you need to be checking in THE_REQUEST (which contains the full original HTTP request, like GET /show_table.php HTTP/1.1). When Apache performs the rewrite, it changes REQUEST_URI, so to the rewritten value, and that sends you into a loop.
# Match show_table.php in the input request
RewriteCond %{THE_REQUEST} /show_table\.php
RewriteCond %{QUERY_STRING} ^table=([0-9]{1,2})$
# Do a full redirection to the new URL
RewriteRule ^show_table\.php$ http://example.com/table/%1? [L,R=301,NC]
# Then apply the internal rewrite as you already have working
RewriteRule ^table/([0-9]{1,2})$ show_table.php?table=$1 [L,NC]
You could get more specific in the %{THE_REQUEST} condition, but it should be sufficient and not harmful to use show_table\.php as the expression.
You'll want to read over the notes on THE_REQUEST over at Apache's RewriteCond documentation.
Note: Technically, you can capture the query string in the same RewriteCond and reduce it to just one condition. This is a little shorter:
# THE_REQUEST will include the query string so you can get it here.
RewriteCond %{THE_REQUEST} /show_table\.php\?table=([0-9]{1,2})
RewriteRule ^show_table\.php$ http://example.com/table/%1? [L,R=301,NC]
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).
To newcomers: While trying to comprehensively describe my problem and phrase my questions I produced huge ammount of text. If you don't want to read the whole thing, my observations about (read "proof of") [L] flag not working the misconception, from which it all sprung, is located in Additional observations section. Why I misunderstood apparent behaviour is described in my Answer as well as solution to given problem.
Setup
I have following code in my .htaccess file:
# disallow directory indexing
Options -Indexes
# turn mod_rewrite on
Options +FollowSymlinks
RewriteEngine on
# allow access to robots file
RewriteRule ^robots.txt$ robots.txt [NC,L]
# mangle core request handler address
RewriteRule ^core/(\?.+)?$ core/handleCoreRequest.php$1 [NC,L]
# mangle web file adresses (move them to application root folder)
# application root folder serves as application GUI address
RewriteRule ^$ web/index.html [L]
# allow access to images
RewriteRule ^(images/.+\.(ico|png|bmp|jpg|gif))$ web/$1 [NC,L]
# allow access to stylesheets
RewriteRule ^(css/.+\.css)$ web/$1 [NC,L]
# allow access to javascript
RewriteRule ^(js/.+\.js)$ web/$1 [NC,L]
# allow access to library scripts, styles and images
RewriteRule ^(lib/js/.+\.js)$ web/$1 [NC,L]
RewriteRule ^(lib/css/.+\.css)$ web/$1 [NC,L]
RewriteRule ^(lib/(.+/)?images/.+\.(ico|png|bmp|jpg|gif))$ web/$1 [NC,L]
# redirect all other requests to application address
# RewriteRule ^(.*)$ /foo/ [R]
My web application (and its .htaccess file) is located in foo subfolder of DOCUMENT_ROOT (accessed from browser as http://localhost/foo/). It has PHP core part located in foo/core and JavaScript GUI part located in foo/web. As can be seen from the code above, I want to allow access only to single core script that handles all requests from GUI and to 'safe' web files and redirect all other requests to base application address (last commented directive).
Problem
Behaviour
It works until I try the last part by uncommenting the last redirecting directive. If I comment some more lines, the appropriate page parts stop working, etc.
However, when I uncomment last line, which should be performed only when matching of all previous rules fails (at least that's what I understand), page goes into redirection cycle (Firefox throws error page with something like "This page isn't redirecting properly"), because it's redirecting to http://localhost/foo/ again and again and again, forever.
Questions
What I don't understand is this processing of this rule:
RewriteRule ^$ web/index.html [L],
specifically the [L] flag. The flag apparently doesn't work for me. When the last line is commented, it correctly redirects, but when I uncomment it, it is always processed, even though rewriting should stop on [L] flag. Anyone got any ideas?
Also, on a sidenote, I'd be thrilled to know why my following attempt at fixing it doesn't work either:
RewriteEngine on
RewriteRule ^core/(\?.+)?$ core/handleCoreRequest.php$1 [NC,L]
RewriteRule ^(.*)$ web/$1 [L]
RewriteRule ^.*$ /foo/ [L]
This actually doesn't work at all. Even if I remove the last line, it still doesn't redirect anything correctly. How does the redirecting work in the first example, if it doesn't work in the second?
It would also be of great benefit to me, if anybody knew any way to actually debug these directives. I spend hours on this without even the slightest clue what could possibly be wrong.
Additional observations
After trying the advice given by bbadour (not that I haven't tried it before, but now that I had a second opinion, I gave it another shot) and it didn't work, I've come up with the following observation. By rewriting last line to this:
RewriteRule ^(.*)$ /foo/?uri=$1 [R,L]
or this
RewriteRule ^(.*)$ /foo/?uri=%{REQUEST_URI} [R,L]
and using Firebug's Net panel, I found out more evidence, that the [L] flag is clearly not working as expected in the previously mentioned RewriteRule ^$ web/index.html [L] rule (let's call it THE RULE from now on). In first case I get [...]uri=web/index.html, in second case [...]uri=/foo/web/index.html. That means that THE RULE gets executed (rewrites ^$ to web/index.html), but the rewriting doesn't stop there. Any more ideas, please?
After hours of searching and testing, I finally found the real problem and solution. Hopefully this will help somebody else too, when they come across the same problem.
Cause of observed behavior
.htaccess file is processed after every redirect (even without [R] flag),
which means that after the RewriteRule ^$ web/index.html [L] is processed, mod_rewrite correctly stops rewriting, goes to the end of the file, redirects correctly to /foo/web/index.html, and then the server starts processing .htaccess file for the new location, which is the same file. Now only the last rewrite rule matches and redirects back to /foo/ (this time with [R], so the redirect can be observed in browser) ... and the .htaccess file is processed again, and again, and again...
Once more for clarity: Because only the hard redirects can be observed, it seems like the [L] flag is ignored, but it is not so. Instead, the .htaccess is processed two times redirecting back and forth between /foo/ and /foo/web/index.html.
Solution
Disallow direct access to subfolder
To virtually move subdirectory to application root directory, additional complex conditional rewrites must be used. Variable THE_REQUEST is useful for distinguishing between hard and soft redirects:
RewriteCond %{THE_REQUEST} ^GET\ /foo/web/
RewriteRule ^web/(.*) /foo/$1 [L,R]
For this rewrite rule to be matched, two conditions must apply. First, on second line, the "local URI" must start with web/ (which corresponds with absolute web URI /foo/web/). Second, on first line, the real request URI must start with /foo/web/ too. Together this means, that the rule only matches when the file inside the web/ subfolder is requested directly from the browser, in which case we want to do a hard redirect.
Redirect to allowed content from root to subfolder (soft)
RewriteCond $1 !^web/
RewriteCond $1 ^(.+\.(html|css|js|ico|png|bmp|jpg|gif))?$
RewriteRule ^(.*)$ web/$1 [L,NC]
We want to redirect to allowed content only if we haven't done it already, hence the first condition. Second condition specifies mask for allowed content. Anything matching this mask will be softly redirected, possibly returning 404 error if the content doesn't exist.
Hide all content not in subfolder or not allowed
RewriteRule !^web/ /foo/ [L,R]
This will do a hard redirect to application root for all URIs not beginning with web/ (and remember, only requests that can begin with web/ at this point are internal redirects for allowed content.
Real example
My code shown in my "question" after using solution tips mentioned above gradually transformed into the following:
# disallow directory indexing
Options -Indexes
# turn mod_rewrite on
Options +FollowSymlinks
RewriteEngine on
# allow access to robots file
RewriteRule ^robots.txt$ - [NC,L]
# mangle core request handler address
# disallow direct access to core request handler
RewriteCond %{THE_REQUEST} !^(GET|POST)\ /asm/core/handleCoreRequest.php
RewriteRule ^core/handleCoreRequest.php$ - [L]
# allow access to request handler under alias
RewriteRule ^core/$ core/handleCoreRequest.php [NC,QSA,L]
# mangle GUI files adressing (move to application root folder)
# disallow direct access to GUI subfolder
RewriteCond %{THE_REQUEST} ^GET\ /foo/web/
RewriteRule ^web/(.*) /foo/$1 [L,R]
# allow access only to correct filetypes in appropriate locations
RewriteCond $1 ^$ [OR]
RewriteCond $1 ^(images/.+\.(ico|png|bmp|jpg|gif))$ [OR]
RewriteCond $1 ^(css/.+\.css)$ [OR]
RewriteCond $1 ^(js/.+\.js)$ [OR]
RewriteCond $1 ^(lib/js/.+\.js)$ [OR]
RewriteCond $1 ^(lib/css/.+\.css)$ [OR]
RewriteCond $1 ^(lib/(.+/)?images/.+\.(ico|png|bmp|jpg|gif))$
RewriteRule ^(.*)$ web/$1 [L,NC]
# hide all files not in GUI subfolder that are not whitelisted above
RewriteRule !^web/ /foo/ [L,R]
What I don't like about this approach is that the application root folder must be hardcoded in .htaccess file (as far as I know), so the file must be generated on application install, not simply copied.
To debug, try simplifying your regex, and the url you ask for (a part of the full url you wanna match), and see if it's working, now step by step, add more bits to the regex adn the testing url, till you find where things are stopping to work properly.
Try using:
RewriteRule ^(.*)$ /foo/ [R,L]
If it still loops, put a RewriteCond in front of it to skip the rule if it is already /foo/