Apache rewrite rule leading slash - apache

Leading slash first argument: ignored?
What's the syntax difference between
RewriteRule help help.php?q=noslash [L] #1
RewriteRule /help help.php?q=withslash [L] #2
If I hit http://localhost/help, it goes to #1, if I hit http://localhost//help it still goes to #1.
Am I right in saying the leading slash in the first argument to RewriteRule is essentially ignored?
Leading slash second argument: error?
Also, why doesn't this rewrite rule work?
RewriteRule help /help.php [L] #1
Putting a leading slash in front of the second arg actually creates a 500 error for the server. Why?
I should note I'm using a .htaccess file to write these rules in

Strangely enough,
RewriteRule ^/help help.php?q=2 [L]
The above rule fails and never matches.
This rule:
RewriteRule ^help help.php?q=1 [L]
Matches http://localhost/help, http://localhost//help and http://localhost///help
It appears RewriteRule never sees leading slashes of the path, and as TheCoolah said they are collapsed (to 0.. when using a .htaccess file anyway) no matter how many there are.
For the second part of the question,
RewriteRule ^help /help.php
I'm getting the answer from Definitive Guide to Apache Mod_rewrite
... a rewrite target that does not begin with http:// or another protocol
designator is assumed to be a file system path. File paths that do not begin with a slash are interpreted as being relative to the directory in which the rewriting is taking place.
So /help.php looks in the root of the system for a file called help.php, which on my system it cannot find.
To make /help.php appear as a relative URL (relative to the root of the site) you can use the [PT] directive:
RewriteRule ^/help /help.php [PT]
That directs http://localhost/help to http://localhost/help.php.

Regarding double slashes: Most Web servers silently collapse multiple slashes into a single slash early in the request processing pipeline. This is true for at least Apache, Tomcat and Jetty. Most Unix-based file systems work the same way. If you really want to check for this, you need to do something like:
RewriteCond %{REQUEST_URI} ^(.*)//(.*)$

help matches "help" anywhere in the path.
/help matches nothing since the rewriterule directive omits the leading slash for matching purposes (i.e., you must use ^, not / or ^/, to reference the current directory).
(This can be very confusing if you've used %{REQUEST_URI} in rewritecond because %{REQUEST_URI} does begin with a trailing slash. When matching against %{REQUEST_URI}, ^ and ^/ are equivalent and a directory name will always be preceded by a slash character regardless of whether or not it is in the top-level directory.)
The server error is caused by an infinite loop. "help" becomes "/help.php" which is then matched by the same directive that did the rewriting. So, after the first match, "/help.php" becomes "/help.php" infinitely resulting in a URL that can't be resolved.
I believe such loops can be fixed with the end flag (i.e., [end]), but that flag requires Apache 2.3.9+ whereas Apache 2.2 seems to be more common in deployment. It'd probably be better to just fix the regular expression anyway; ^help$ would seem to be the better choice here.

The way RewriteRule works is that if the given regular expression matches any part of the path part of the URL (the part after the host and port but before the query string), then the entire path part is completely replaced with the given substitution. This explains the behaviour you're seeing in the first part of your question.
I'm not sure what could be causing the 500 errors on the second part; maybe the collapsing of doubled slashes doesn't happen after the rewrite engine has run and then generates a server error.

The reason for the 500 Error is an infinitive Loop:
help gets rewritten to /help
/help gets stripped to help
help gets rewritten to /help
etc. until the MaxRewrites limit is hit -> 500
Whereas if the rule rewrites help to help, Apache is smart enough to abort rewriting at that point.

Related

mod_rewrite misbehaves if regex matches existing file, but only in .htaccess file

I've got an .htaccess rule like this:
RewriteRule ^z/([^/]+)$ y.html?$1 [NC,L]
This rule works fine. But if I change the rule slightly, to:
RewriteRule ^y/([^/]+)$ y.html?$1 [NC,L]
When I try to load y/anything I get a 404 and the following message in the error log:
File does not exist: /var/www/y.html/anything
The only difference I can see is that z.html does not exist, but y.html does. At first I thought maybe the initial transform was triggering a recursive re-write, but I don't see how this could be. It should rewrite:
`y/anything`
to
`y.html?anything`
Which does NOT have a slash in it. In fact, the only problem with the re-written URL is that it has a slash where I specified a question mark. What is going on here?
It gets stranger. If I change the rewrite URL, e.g. to
RewriteRule ^y/([^/]+)$ /q.html?$1 [NC,L]
it STILL is telling me /var/www/y.html/anything not found, not q.html..
If I move y.html to y.js on the server, then it tells me /var/www/y.js/anything is not found. It really seems like it is somehow matching /dir/ and changing it to an existing file. Is there a default rule somewhere in apache that might do this?
I tried a hard reload in the browser, which had no effect.
Update: I tried to use RewriteLog to see what was going on with the re-writing. However, to do this I had move my rewrite entries to the VirtualHost section of my main config. After I did this the pattern matching completely stopped until I changed my rule to:
RewriteRule ^/y/([^/]+)$ /y.html?$1 [NC,L]
After making this change, everything works as expected. So why can't I get it to work in the .htaccess file? Neither regex works properly there (with or without the leading slash).
For anyone who runs into this problem in the future, the issue I was having was that MultiViews was interfering with my URL resolution. When I removed "MultiViews" from the list of options for the <Location/> in the <VirtualHost> the issue went away.
Having the following in the htaccess file
RewriteEngine On
RewriteRule ^z/([^/]+)$ /y.html?$1 [NC,L]
RewriteRule ^y/([^/]+)$ /y.html?$1 [NC,L]
will show the contents of y.html
You might want to do a hard refresh of the page because if it was previously redirected to /y.html/anything, but then changed to /y.html?anything it might still be in a cache on your browser. Double check with your other browsers that the same thing is happening on them.

Apache rewrite rule for optional query parameter

Even after lengthy perusal, the Apache rewriterule documentation continues to confound me.
Currently I am using the following .htaccess URL rewrite rule:
RewriteRule ^([_0-9a-zA-Z-]+)\.html$ index.php?a=p&p=$1 [nc]
This rewrites something like
http://www.website.com/thepagename.html
into
http://www.website.com/index.php?a=p&p=thepagename
which works fine.
Now I need to modify this to allow for an optional query parameter that may or nay not be tacked on to the original (unrewritten) URL. E.g.:
http://www.website.com/thepagename.html?req=login
or even
http://www.website.com/thepagename.html?req=login&usr=johndoe
must be rewritten into:
http://www.website.com/index.php?a=p&p=thepagename&req=login
and
http://www.website.com/index.php?a=p&p=thepagename&req=login&user=johndoe
respectively, without breaking the original rewrite (i.e. without the optional query parameters tacked onto the unrewritten URL).
Try as I might, I cannot work out the correct syntax. Can anyone point me in the right direction?
Tnx!
// FvW
You only have to add ˋQSA` flag (Query String Append)
RewriteRule ^([_0-9a-zA-Z-]+)\.html$ index.php?a=p&p=$1 [L,QSA,NC]
More info (and examples) here

mod_rewrite and dot at END of URI

I have been developing a shop, which uses mod_rewrite to allow us to make the URIs more readable, for instance:
http://www.example.com/shop/Tools
Will be rewritten to
http://www.example.com/index.php?area=shop&folder=Tools
My rewrite rule is as follows:
RewriteRule ^shop/([^?#]+) index.php?area=shop&folder=$1 [NC,QSA,L]
However, this breaks when the folder name ends in . (dot), as I discovered when testing with a folder name ending in "etc."
It seems any trailing dots are totally removed before $_GET has been populated. If I put another character after the dot, it's fine, if the URI ends in any number of dots, they are removed
Is there a way to stop this from happening?
You don't need to exclude "?" and "#" in RewriteRule since it operates only on URI (path), without query-string or anchor.
So, this is enough:
RewriteRule ^shop/(.+) index.php?area=shop&folder=$1 [NC,QSA,L]
That being said, this does not change the fact that dots get stripped.
This may be a result of MultiViews being on. This option makes Apache try and resolve the URIs disregarding extensions.
So, add this as well:
Options -Multiviews

How can I get mod_rewrite to match a rule just once

I have the following URL...
http://localhost/http.mygarble.com/foundationsofwebprogramming/86
...that I want to convert into the following:
http://localhost/http.mygarble.com/php/blog.php?subdomain=foundationsofwebprogramming&page=posts&label=86
I thought I could achieve this with the following rule:
RewriteRule ([^/]+)/([^/]+)$ php/blog.php?subdomain=$1&page=post&label=$2 [NC,L]
However what I find is that this rule is applied repeatedly, resulting in an internal server error. I understand that when the URI is transformed using this rule, the resulting URI will also match the rule, and therefore it is applied again ad-infinitum.
My previous (admittedly rather hazy) understanding was that the [L] flag would stop further processing, although I now understand that this simply means that only the remainder of the rules are skipped, and does not stop the rewrite engine running through the rules again.
I can fix this problem by adding the following condition...
RewriteCond $0 !php/blog.php
RewriteRule ([^/]+)/([^/]+)$ php/blog.php?subdomain=$1&page=post&label=$2 [NC,L]
...or by writing a more specific regular expression. But what I really want to do is find a way of stopping the rewrite engine from attempting ANY further matches once this rule is matched once. Is this possible?
Many thanks.
Usually 2 methods are used.
The first one is a Rewrite Condition testing that the requested file is not a real file. When internal recursion arise your php/blog.php is a real file and rewriterule is not executed the 2nd time. Side-effect is that any request for a file which exists won't be rewritten (which can be good side effect)
RewriteCond %{REQUEST_FILENAME} !-f
The second solution is to check you're not in an internal redirection with:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
Side effect of this 2nd solution is that the rewriteRule cannot be applied if some other rules are applied before (if you want some internal redirection to run after a first pass of rewriting in fact).
Edit
For completion I will add a third method: the [NS] or [nosubreq] tag seems to be doing the same thing. Preventing the rule usage after an internal redirection.
And the third method is to upgrade apache to 2.3.9 or higher and use [END] flag instead of [L].
No side effects

URL rewriting with mod_rewrite to provide RESTful URLs

The web server is Apache. I want to rewrite URL so a user won't know the actual directory. For example:
The original URL:
www.mydomainname.com/en/piecework/piecework.php?piecework_id=11
Expected URL:
piecework.mydomainname.com/en/11
I added the following statements in .htaccess:
RewriteCond %{HTTP_HOST} ^(?!www)([^.]+)\.mydomainname\.com$ [NC]
RewriteRule ^(w+)/(\d+)$ /$1/%1/%1.php?%1_id=$2 [L]
Of course I replaced mydomainname with my domain name.
.htaccess is placed in the site root, but when I access piecework.mydomainname.com/en/11, I got "Object not found".(Of course I replaced mydomainname with my domain name.)
I added the following statements in .htaccess:
RewriteRule ^/(.*)/en/piecework/(.*)piecework_id=([0-9]+)(.*) piecework.mydomainname.com/en/$3
Of course I replaced mydomainname with my domain name.
.htaccess is placed in the site root, but when I access piecework.mydomainname.com/en/11, I got "Object not found".(Of course I replaced mydomainname with my domain name.)
What's wrong?
Try using RewriteLog in your vhost or server-conf in order to check the rewriting process. Right now you just seem to guess what mod_rewrite does.
By using RewriteLogLevel you can modify the extend of the logging. For starters I'd recommend level 5.
PS: Don't forget to reload/restart the server after modifying the config.
Here's a quick overview of what's happening:
RewriteCond %{HTTP_HOST} ^(?!www)([^.]+)\.mydomainname\.com$ [NC]
First, the question mark is supposed to be at the end.
$1 would (should) match anything that is not 'www' 0 or 1 times.
$2 matches anything that is not a character 1 or more times, which theoretically would match a blank space there but likely would never match anything.
Then it requires '.mydomainname.com' after those two groupings.
Your first two conditions are looking for two separate groupings.
I'm not sure exactly how you're trying to set up your structure, but here is how I would write it based on your original and expected URL's:
RewriteCond %{HTTP_HOST} !^www\.mydomainname\.com$ [NC]
RewriteCond %{HTTP_HOST} ^(\w+)\.mydomainname\.com$ [NC]
RewriteRule ^(\w+)/(\d+)$ /$1/%1/%1.php?%1_id=$2 [L]
Basically, your first condition is to make sure it's not the URL beginning with 'www' (it's easier to just make it a separate rule). The second condition makes it check any word that could possibly be in front of your domain name. Then your rewrite rule will redirect it appropriately.
Get rid of the last .htaccess line there in your question...
Someone please correct me if I typed something wrong. I don't remember if you have to have the '\' in front of '\w' and '\d' but I included them anyways.
You are doing it backwards. The idea is that you will give people the friendly address, and the re-write rule will point requests to this friendly, non-existent page to the real page without them seeing it. So right now you have it only handling what to do when they go to the ugly URL, but you are putting in the friendly URL. since no rule exists for when people put the friendly URL directly, apache looks for it and says "Object not Found"
So add a line:
RewriteRule piecework.mydomainname.com/en/(*.) ^/$3/en/piecework/$3?piecework_id=([0-9]+)(.*)
Sorry, that's quite right, but the basic idea is, if they put in the URL you like, Apache is ready to redirect to the real page without the browser seeing it.
Update
I'm way to sleepy to do regex correctly, so I had just tried my best to move your example around, sorry. I would try something more simple first just to get the basic concept down first. Try this:
RewriteRule www.mydomainname.com/en/piecework/piecework\.php\?piecework_id\=11 piecework.mydomainname.com/en/11
At the very least, it will be easier to see what isn't working if you get errors.