I try to echo current state of a URL being rewrited in .htaccess if query string contains DEBUG phrase:
RewriteCond %{QUERY_STRING} DEBUG
RewriteRule .+ echo.php?ip=%{REMOTE_ADDR}&url=$0&query=%{QUERY_STRING} [L]
and my echo.php script echoes expected URL.
But strangely when I change order of parameters in substitution to:
RewriteRule .+ echo.php?url=$0&ip=%{REMOTE_ADDR}&query=%{QUERY_STRING} [L]
echoed url is "echo.php" itself.
Is this expected behavior, and if so why?
When I test, I don't get the different behaviour you are stating. When I use the rule either way round, I get echo.php as the URL, when visiting example?DEBUG. And this is because rules in .htaccess will keep looping (because the whole directory processing starts again after the rewrite, including the rewrite rules) until the URL does not change. So since you are matching .+ you will always end up with echo.php unless some prior rule gets in the way and ends processing of that iteration before reaching your rule.
You can prove this to yourself by adding the [END] flag to the rule which stops any further mod_rewrite processing, and then you will get the behaviour you are expecting. The difference you are seeing must be due to your prior rules in some way.
As mentioned in the comments, a better way to debug mod_rewrite is to make use of the LogLevel rewrite:traceX option, with X being a number between 1 and 8. 3 is a good place to start, and increase from there if not enough info. There is a lot of info as you increase towards 8. You can only enable it in the main config. See the documentation. Make sure to switch it off again as all that logging affects performance. It does not interfere with any earlier LogLevel directives.
Related
I'm trying to integrate an open source forum in to my WordPress installation, I can figure out the next steps if I can just get a rewrite rule to work, I have the following so far:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^forum/qa\-theme/(.*) forum-embed/qa-theme/$1 [QSA,L]
RewriteRule ^forum/qa\-content/(.*) forum-embed/qa-content/$1 [QSA,L]
RewriteRule ^forum/([\w]+)$ forum/?url=$1 [QSA,L]
</IfModule>
The first two rules work, but the last one, I've tried all sorts of changes to this regular expression - I want to take whatever comes after forum/ and to put it in to a query string as the url parameter. I'm sure I'm tip-toeing around the expression - what am I missing?
Thanks in advance!
EDIT
It's also not clear how you are avoiding conflicts with the WordPress front-controller? Presumably you are placing these directives at the top of the .htaccess file, before the # BEGIN WordPress section? However, it may be simpler to create another .htaccess file inside the /forum subdirectory instead and this will (by default) override the WordPress directives.
A sound point, yes I was putting it above the # BEGIN WordPress, but I will make a .htaccess in the forum directory.
You say you've "tried all sorts of changes to this regular expression", but this regex certainly won't match your first example. The \w shorthand character class excludes slashes and hyphens.
True, this was a bad example to show where I was up to on my question, but I've also tried:
^forum/(.+)$
^forum/([a-z-A-Z-0-9-/]+)$
/forum/ is presumably a filesystem directory - this itself can't handle the request, it requires further rewriting to an actual file
I don't understand -- the first two rules work, and I can navigate to all pages, including forum/ -- index.php is the default file in the config, why must this rule be an exception?
RewriteRule ^forum/([\w]+)$ forum/?url=$1 [QSA,L]
Example 1: forum/2/test-question => forum/?url=2/test-question
You say you've "tried all sorts of changes to this regular expression", but this regex certainly won't match your first example. The \w shorthand character class excludes slashes and hyphens. If you want to match "whatever comes after forum/", then you could just use (.+) (like your previous examples, except + instead of * to avoid a rewrite loop, ie. to avoid matching /forum/). For example:
RewriteRule ^forum/(.+) forum/?url=$1 [QSA,L]
However, forum/?url=whatever is still not a valid end-point (as #RavinderSingh13 has pointed out in comments). /forum/ is presumably a filesystem directory - this itself can't handle the request, it requires further rewriting to an actual file (perhaps you are expecting mod_dir to issue a subrequest for the DirectoryIndex?). For example, should it be /forum/index.php?url=whatever?
It's also not clear how you are avoiding conflicts with the WordPress front-controller? Presumably you are placing these directives at the top of the .htaccess file, before the # BEGIN WordPress section? However, it may be simpler to create another .htaccess file inside the /forum subdirectory instead and this will (by default) override the WordPress directives.
You should remove the <IfModule> wrapper since it's not required here.
UPDATE:
/forum/ is presumably a filesystem directory - this itself can't handle the request, it requires further rewriting to an actual file
I don't understand -- the first two rules work, and I can navigate to all pages, including forum/ -- index.php is the default file in the config, why must this rule be an exception?
We don't know what requests the first two rules are expected to handle, but I assume they are just rewriting static files?
When you request the directory /forum/ then mod_dir must later issue a subrequest for the DirectoryIndex document. When you rewrite the request to /forum then mod_dir must still perform this additional processing later. In the meantime rewrite processing loops in .htaccess and /forum/ is passed back through the rewrite engine. This may or may not work - it can result in other conflicts - at the very least it is additional/unnecessary processing. You should rewrite directly to the file that handles the request to cut out this additional processing. In the same way the WordPress code block rewrites the request to /index.php, not /.
To clarify, when you request /forum/ only then the above directive is not triggered and mod_dir issues a subrequest for /forum/index.php. There is no url parameter.
Updated directives
However, if rewriting to /forum/index.php, you'll need additional checks to avoid /forum/index.php being caught by the same rule and resulting in a rewrite loop (500 error).
For example, try the following instead:
RewriteRule ^forum/index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^forum/(.+) forum/index.php?url=$1 [QSA,L]
The condition that checks against REQUEST_FILENAME may be optional, depending on whether there are any static resources served from this directory tree?
Alternatively, if your URLs do not contain dots then you may get away with a more restrictive regex instead to avoid matching URLs containing dots. For example:
RewriteRule ^forum/([^.]+)$ forum/index.php?url=$1 [QSA,L]
/forum/.htaccess
If moving these directives to the /forum/.htaccess file you would rewrite them as follows (and remove the RewriteBase directive entirely):
RewriteEngine On
RewriteRule ^qa-theme/(.*) /forum-embed/qa-theme/$1 [L]
RewriteRule ^qa-content/(.*) /forum-embed/qa-content/$1 [L]
RewriteRule ^([^.]+)$ index.php?url=$1 [QSA,L]
The QSA flag is not required on the first two directives since the query string is passed through by default. (Although if these are rewriting static resources then you wouldn't expect a query string to be passed anyway?)
No need to backslash-escape the hyphen in the regex, since it carries no special meaning when used outside of a character class. Likewise, the dot carries no special meaning when used inside a character class so does not need to be backslash-escaped in the last rule above.
I'm trying to rewrite URLs such as
/product/16/var1/value1/var2/value2...
to this
index.php?page=product&id=16&var1=value1&var2=value2...
In other words, I would like to have a "main parameter" translated to an id (and I can do this), but I would also like to have, from that point on, couples of "directories" translated recursively to key-value pairs.
Is this possible with Apache mod_rewrite?
In the absence of the [L] flag, any mod_rewrite rule will apply repeatedly to any URI which corresponds to the rule's rewrite conditions and pattern.
Knowing this, we can build a mod_rewrite rule which looks for any URIs with query strings beginning in a certain way and then repeatedly harvests the folder-names of that URI (two at a time) to build the rest of the query string.
See example below:
In the root folder of
http://example.com/
save an .htaccess file with the following mod_rewrite directives:
RewriteEngine On
RewriteRule ^(product)/([0-9]{2})/(.*) http://%{HTTP_HOST}/$3/index.php?page=$1&id=$2
RewriteCond %{QUERY_STRING} ^(page=product&id=[0-9]{2}.*)
RewriteRule ^([^/]+)/([^/]+)/(.*/)?index.php$ http://%{HTTP_HOST}/$3index.php?%1&$1=$2
Using the above:
http://example.com/product/16/var1/value1/var2/value2/
becomes
http://example.com/index.php?page=product&id=16&var1=value1&var2=value2
and
http://example.com/product/16/var1/value1/var2/value2/var3/value3/var4/value4/
becomes
http://example.com/index.php?page=product&id=16&var1=value1&var2=value2&var3=value3&var4=value4
I know .htaccess rules are parsed top to bottom but what if my URL matches two rules which one will be used and why?
I have simple rules like
^(.*)$ index.php?pag=cms&title=$1
^store/(.*)$ index.php?pag=store&id=$1
Basically any URL will match the first rule so what happens with other ones?
If the URL matches two rules, it's the first one that rewrites. This is not to say that the second rule doesn't fire. It does but it fails to match because subsequent rules fire on the output of the rule preceding it.
If somehow you don't want the rewrite to fall-through and stop at the first matching rule you can mark the rule as last by using the [L] flag.
^(.*)$ index.php?pag=cms&title=$1 [L]
^store/(.*)$ index.php?pag=store&id=$1 # won't fire now
I am trying to create a filter system for products and keep the URL's friendly, i am using a map file for the filter params and the following re-write code.
The url could contain, one param or 10 params and will be bulit like so
Start URL: www.domain.com/climbing-frames/
First Param: www.domain.com/climbing-frames_rockwall/
Second Param: www.domain.com/climbing frames_rockwall_rope-ladder/
And so on.....
The rewrite rule so far
RewriteMap features txt:features.txt
RewriteCond ${features:$1|NOT_FOUND} !NOT_FOUND
RewriteRule ^climbing-frames(?:_([^/]+))(?:_([^/]+))(?:_([^/]+))(?:_([^/]+))/ /get-features.cfm?(?1(param1=${features:$1|0}))(?2(,${features:$2|0}))(?3(,${features:$3|0}))(?4(,${features:$4|0}))
This works if 4 prams are entered but not if only two params are entered. This is driving me crazy!
Any clues would be greatly appreciated.
Jason
I think you're close - there might be a looping approach that would be better, but I do tend to go with the way you're going.
I see how you're doing the non-capturing underscore, then capturing the next characters. Instead of looking for not-slash, look for not-underscore. With the not-slash, it's going to grab the whole thing in the first group. That is, look for underscore-not-underscore.
Then (getting to the real fix), each section needs to be optional - the rule above is requiring the four sections. So add a ? to the end of each one, as:
RewriteRule ^climbing-frames(?:_([^_]+))(?:_([^_]+))?(?:_([^_]+))?(?:_([^_]+))?/
I left the first group required, since it practically should be there, and the RewriteCond is checking for it anyway.
I cannot wrap my head around URL rewriting. What I want to do seems very simple but I am having problems getting the results I want.
I would like allow users to type www.mysite.com/search/real with an optional / at the end. This would take them to www.mysite.com/content/search_real_property.asp
That's it. Here is the rule I have right now. The problem with this is it will keep stacking.
RewriteRule ^(search) content/search_real_property.asp
So this would work /search/real but so would search/real/search/real/search/real/
and others.
Assuming there are no other issues, you've turned the rewrite engine on (RewriteEngine On) and that you're either adding the rewrite in httpd-vhosts.conf or an .htaccess file in the root of the web tree (so that any path issues are resolved)... then the issue is merely one of Regular Expression pattern matching. Though I'm a bit perplexed by ASP running on what appears to be an Apache server (assuming this IS mod rewrite we're talking about).
So, all you really want is to terminate the match - something like:
RewriteEngine On
RewriteRule ^search/real/?$ /content/search_real_property.asp
That will fix it to /search/real (with or without a trailing slash, the ? means match the preceding character 0 or 1 times) to /content/search_real_property.asp. As the $ sign denotes the line terminator (EOL effectively) there must be nothing after "real" (except perhaps that 1 forward slash).
For greater flexibility you might want to look at what you can actually do with regular expressions, for instance...
RewriteEngine On
RewriteRule ^search/([^/]*)/?$ /content/search_real_property.asp?query=$1
Which would allow you to take any string and pass it in the address bar as a variable called query (Request.QueryString('query') IIRC).
Try: http://www.regular-expressions.info/ for more info.