What's going on with my mod_rewrite? - apache

I have a simple mod_rewrite system set up on my site which basically converts
http://site.com/file -> http://site.com/file.php
Here's the .htaccess file
Options -MultiViews
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www.site.com
RewriteRule ^(.*)$ http://site.com/$1 [R=301]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-z]+)/?$ http://site.com/$1.php [L]
This was working for a long time and then a couple of days ago I realized that while the RewriteRule was working, it was actually changing my URL in the status bar.
For instance, it would redirect /photos to /photos.php, but it would also change the URL to show the .php. This has never happened before and I'm not sure what happened to trigger the change.
Any ideas?

The first rewrite rule needs the [L] flag. From the mod_rewrite documentation for the [R] flag:
You will almost always want to use [R] in conjunction with [L] (that is, use [R,L]) because on its own, the [R] flag prepends http://thishost[:thisport] to the URI, but then passes this on to the next rule in the ruleset, which can often result in 'Invalid URI in request' warnings.
In this case, you don't get a warning, but appending the ".php" extension happens before issuing the redirect rather than when the second, redirected request comes in.
Also, remove the scheme and domain name from the substitution in the second rewrite rule. A full URL can cause an implicit redirect. From the documentation for RewriteRule:
The Substitution of a
rewrite rule is the string that replaces the original URL-path that
was matched by Pattern. The Substitution may
be a:
[...]
Absolute URL
If an absolute URL is specified,
mod_rewrite checks to see whether the
hostname matches the current host. If it does, the scheme and
hostname are stripped out and the resulting path is treated as
a URL-path. Otherwise, an external redirect is performed for
the given URL. To force an external redirect back to the
current host, see the [R] flag below.

Related

htaccess redirect outcome not as expected

I hope someone can help me with the following problem.
I have a multiple language site with the language as a folder like
example.com/se/post
I want to get the language separated by domain like example.se.
So far no problem with a DNS alias and WPML plugin.
The problem I have is that I want to redirect example.com/se/post to example.se/post. I try to use this rule in the .HTACCESS file but it changes the URL to example.se/se with the /se that I do not need. I'm not very familiar with the rewrite engine in .HTACCESS file.
<IfModule mod_headers.c>
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(www\.)?nofairytales\.nl$ [NC]
RewriteCond %{REQUEST_URI} ^/sv(/.*)?$ [NC]
RewriteRule ^(.*)$ http://www.nofairytales.se%{REQUEST_URI} [L,R=301]
</IfModule>
RewriteCond %{HTTP_HOST} ^(www\.)?nofairytales\.nl$ [NC]
RewriteCond %{REQUEST_URI} ^/sv(/.*)?$ [NC]
RewriteRule ^(.*)$ http://www.example.se%{REQUEST_URI} [L,R=301]
This is close... you are capturing the URL-path (/post part) in the preceding condition but not using it in the substitution string. Instead, you are using REQUEST_URI which contains the full root-relative URL-path.
You are also matching sv in the URL-path, but redirecting to se in the TLD. The following should resolve the issue (with minimal changes):
RewriteCond %{REQUEST_URI} ^/se(/.*)?$ [NC]
RewriteRule ^(.*)$ http://www.example.se%1 [L,R=301]
Where %1 is a backreference to the captured subpattern in the preceding condition (the /post part).
However, You don't need the second (or even the first) condition(s), as it can be all done in the RewriteRule directive. There wouldn't seem to be a need to check the requested hostname, since if the language subdirectory is in the URL-path then it would seem you should redirect anyway to remove it.
For example, the following should be sufficient to redirect a single language code:
# Language "se"
RewriteRule ^se(?:/(.*))?$ https://www.example.se/$1 [R=301,L]
The non-capturing group that contains the slash delimiter ensures that we always have a trailing slash on the target URL (after the hostname). The first rule above requires the user-agent to "correct" the redirect response when the slash after the hostname is omitted (which it does).
For multiple languages you can modify the same rule with regex alternation. For example:
# All supported languages
RewriteRule ^(se|uk|us|au|id)(?:/(.*))?$ https://www.example.$1/$2 [R=301,L]
This assumes all language codes map to a TLD using the same code. If not then you can implement a "mapping" (lang code -> TLD) in the rule itself or use a RewriteMap if you have access to the server config. This could also provide a "default" TLD.
You could be more generic and allow any two-character language code in the regex. eg. ^([a-z]{2})(?:/(.*))?$. And simply pass this through to the TLD. However, a request for an unknown language (eg. /xx/post) - which might have resulted from an error on your site - will now result in either a malformed redirect (since the domain won't resolve) or worse, a redirect to a competitor lying in wait. And this might go undetected unless you run an analysis of your redirects. So, being more restrictive with the regex/rule may be advisable.

The requested URL was not found on this server .htacess

I am trying to rewrite URL like:
example.com/speciality_details.php?id=23&name=ent
TO
example.com/specialities/23/ent
But I am getting this error:
Not Found
The requested URL was not found on this server.
Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.
This is my .htaccess file
RewriteEngine on
RewriteRule ^doctor/([0-9]+)/([^/.]+)$ doctor_details.php?id=$1&name=$2 [NC,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/specialities/([0-9]+)/([^/.]+)$ speciality_details.php?id=$1&name=$2 [NC,L]
The first RewriteRule working but the second one is not working
Please help me to know what the problem is. How should I rewrite the code?
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/specialities/([0-9]+)/([^/.]+)$ speciality_details.php?id=$1&name=$2 [NC,L]
Just as in the first rule (which is "working"), you should not be matching a slash prefix on the URL-path. And the preceding condition (RewriteCond directive) is superfluous, since a URL of the form /specialities/23/ent could not possibly match a physical file (could it?).
In .htaccess, the URL-path matched by the RewriteRule pattern does not start with a slash since the directory-prefix (that always ends with a slash) has already been removed.
So, the rule should look like the following instead (and no RewriteCond directive):
RewriteRule ^specialities/([0-9]+)/([^/.]+)$ speciality_details.php?id=$1&name=$2 [NC,L]
This would match a URL of the form example.com/specialities/23/ent, as per your example. And assumes the file being rewritten to is speciality_details.php in the document root.
The NC (nocase) flag should also be superfluous, unless you are expecting mixed case versions of sPeCiAlItIeS? But if you are then that is better resolved with a redirect since the rewrite would potentially result in a duplicate content (SEO) issue.
Make sure you clear your browser cache before testing.
Although, from your earlier question edits it looks like you had already tried this without the slash prefix, but at the time you had /speciality/23/ent, not /specialities/23/ent as the example request URL - which would obviously not match.

.htaccess and rewrite from subdirectories inside unknown parent directory

So I have an htaccess file in a subdirectory and whenever I try to rewrite the url, it redirects to the document_root and not the subdirectory where the htaccess resides. Now, under normal circumstances, I'd rewrite it with the path to the subdirectory with path/to/subdirectory, but I won't know what the exact path will be. Is there a way either, through an Apache environment variable or something else, to write out that path?
Edit:
Here's the .htaccess file so far.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
#RewriteRule (.*-file) a/b/c/$1.file
RewriteRule (.*-file) $1.file
So, I'm trying to, if the request contains the word file, I want to match the entire request prior to the word file and redirect there. This is so that if a request is to
example.com/a/b/c/file[any characters here].file
the request will be redirected to the right file. To reiterate, the problem is that I am trying to redirect within the subdirectory. So when I say Rewrite $1, I want that to include the entire request and not just what matched in the REQUEST_FILENAME. And the reason I need it to do that is because I can't simply put a/b/c/$1.file since I won't know for absolute certainty the a/b/c part.
Edit 2: Examples:
So, an example is that I'd send a request like:
example.com/a/b/c/fileacs.file
And want to redirect to:
example.com/a/b/c/file.file
Where I do not know a/b/c/. I have an actual regex and set of rules for the real-world use of this redirect, so don't mind the ridiculous nature of this example.
But currently it's redirecting to:
example.com/file.file
Which does not exist and even if it did, I do not want to redirect there. I've read about Rewrite Context, but can't find out anything substantial about it nor if it's the cause for this. Thank you, in advance.
You can use this rule to capture any path before fileacs.file and use that as bach-reference in RewriteRule:
RewriteEngine On
RewriteCond ^(.*)/file[^.]+\.file$ [NC]
RewriteRule ^ %1/file.file [L,R=302]
The solution is to use the a RewriteCond on the %{REQUEST_URI} (thanks anubhava!) that checks matches the entire request except for the %{REQUEST_FILENAME} without capturing it, using a lookahead. Write out this with the %1 followed by the desired filename. See the following:
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} (.*)-file(?!\.file) [NC]
RewriteRule ^ %1.file [L,R=301]
The %1 now holds the path up to the directory that the .htaccess is stored, plus any prefix to the filename. This doesn't match the remainder of the request, but rather looksahead to ensure you're not actually requesting the file you would like to redirect to (causing a loop).

Mod Rewrite -- redirect all content from subdirectory

I have a scenario where there is a a site with subdirectories and content etc originally in a subdirectory /main
The site and all content has been moved back to the root and is working fine
We need to rewrite so that any http call to /main/, /main/page1, /main/page2 etc is redirected back to the / directory but the uri /page1, /page2 etc
This is what we have so far
RewriteCond %{REQUEST_URI} ^/main/.*
RewriteRule ^/main/(.*) /$1 [L]
Any comments welcome
Thanks very much
In .htaccess context, the url that is matched in the first parameter of RewriteRule doesn't include a leading slash and doesn't include the query string. Having a leading slash will cause the rule to never match. In your case your RewriteCond is unnecessary, as it matches exactly what the RewriteRule would match. Change your rule to the following url and it should work. Please note that this is an internal rewrite (the client won't see this change). If you need a redirect (the client will display the url without main in the address bar), add the [R] flag to the rule.
RewriteRule ^main/(.*)$ $1 [L]
See the documentation.

.htaccess mod_rewrite linking to wrong page

I have in my .htaccess the following code:
RewriteEngine On
RewriteRule ^/?([^/\.]+)/?$ $1.php [L]
RewriteRule ^/?([^/\.]+).php$ $1/ [R,L]
RewriteRule ^/?([^/\.]+)/?$ $1.php [L] is working fine. What this is doing is taking a url like http://www.example.com/whatever and making it read the page as http://www.example.com/whatever.php.
However, what I'd like to be able to do is take a url like http://www.example.com/whatever.php and automatically send it to http://www.example.com/whatever, hence the second line of the code. However, this isn't working. What its doing now, is as soon as it comes across a link ending in .php, the url becomes http://localhost/C:/Sites/page/whatever/, and pulling a 403: Forbidden page.
All I want to know is what I can to so that http://www.example.com/whatever.php will be read as http://www.example.com/whatever, and that if http://www.example.com/whatever.php is entered into the URL bar, it will automatically redirect to http://www.example.com/whatever.
Does that make any sense?
EDIT
Ok, so it appears I wasn't all too clear.. basically, I want /whatever/ to read as whatever.php while the URL still stays as /whatever/, right? However, if the URL was /whatever.php, I want it to actually redirect the users URL to /whatever/, and then once again read it as whatever.php. Is this possible?
If you're rules are inside an .htaccess file, you can omit the leading slash when you match against a URI:
RewriteRule ^([^/\.]+)/?$ /$1.php [L]
Also note that a leading slash is included in the target (/$1.php), this makes sure /whatever/ gets rewritten to /whatever.php. When you redirect, if you are missing this leading slash, apache prepends the document root to it. Thus /whatever.php gets redirected to the document root C:/Sites/page/whatever/. Even if you include the leading slash, this will never work because you're going to cause a redirect loop:
Enter "http://www.example.com/whatever.php" in your address bar
apache redirects you to "http://www.example.com/whatever/"
apache gets the URI whatever/ and applies the first rule and the URI gets rewritten to /whatever.php
The URI gets put through the rewrite engine again
the URI /whatever.php matches the second rule and redirects the browser to "http://www.example.com/whatever/"
repeat steps 3-5
You need to add a condition that the actual request is for /whatever.php:
RewriteCond %{THE_REQUEST} ^(GET|POST|HEAD)\ /([^/\.]+)\.php
RewriteRule ^ /%2/ [R,L]
So altogether, you'll have:
RewriteEngine On
RewriteRule ^([^/\.]+)/?$ /$1.php [L]
RewriteCond %{THE_REQUEST} ^(GET|POST|HEAD)\ /([^/\.]+)\.php
RewriteRule ^ /%2/ [R,L]
You're making a relative path substitution in a per-directory context (.htaccess is a per-directory context). This requires RewriteBase. Per-directory rewrites are done in a later stage of processing, when URLs have been mapped to paths. But the rewrite must produce a URL, which is processed again. I think without the RewriteBase to supply the URL prefix, you end up with a filesystem prefix instead of the URL. That may be why you're getting the C:/Sites thing. Try RewriteBase. But after a correct RewriteBase to specify the correct URL prefix to be tacked in front to the relative rewritten part, I'm afraid you will have the rewrite loop, because you're rewriting whatever.php to whatever; and whatever to whatever.php.
Reference: http://httpd.apache.org/docs/current/rewrite/tech.html