Forum Moderators: phranque
I've written some mod_rewrite rules that will take static urls and make them dynamic.
/index/product_info/products_id/46
becomes
/index.php?main_page=product_info&products_id=46
However, I also want to be able to display it statically, so I need to be able to remove the R flag from one of the rules. That, however, provokes the 500 error.
Also, I want to reset the environmental variables used in this code.
----------------
# This rule should be triggered at the end, and it is this one I have problems with.
# The other rules are working fine.
RewriteCond %{THE_REQUEST} /index/.* [NC]
RewriteCond %{REQUEST_URI} /index\.php.* [NC]
# The following 3 conditionals are the bottlenecks here,
# these are just ideas for how it should be done.
RewriteCond %{ENV:main_page}!0 [NC]
RewriteCond %{ENV:test_cond}![/] [NC]
RewriteCond %{ENV:test_cond}!(([^/]+)/([^/]+))(.*) [NC]
# I want to be able to remove the R flag in the following line. Any ideas?
RewriteRule index\.php(.*) /index.php$1 [L,R,E=test_cond:0,E=qstring:0,E=main_page:0]
# The rest of the rules appears to work flawlessly.
RewriteCond %{ENV:test_cond} (.+) [NC]
RewriteCond %{ENV:test_cond} (/?([^/]+)/([^/]+)/?)(.*) [NC]
RewriteRule index\.php(.*) /index\.php\?main_page=%{ENV:main_page}%{ENV:qstring}&%2=%3 [E=qstring:%{ENV:qstring}&%2=%3,E=test_cond:%4,N]
RewriteCond %{REQUEST_URI} /index(/.*) [NC]
RewriteCond %{ENV:test_cond}!(.+) [NC]
RewriteRule ^index/([^/]+)/?(.*) /index\.php\?main_page=$1 [E=test_cond:$2,E=main_page:$1,E=qstring:,C]
RewriteCond %{ENV:test_cond} (.+) [NC]
RewriteCond %{ENV:test_cond} (([^/]+)/([^/]+)/?)(.*) [NC]
RewriteRule index\.php(.*) /index\.php\?main_page=%{ENV:main_page}%{ENV:qstring}&%2=%3 [E=qstring:%{ENV:qstring}&%2=%3,E=test_cond:%4,N]
-------------------
Any ideas?
RewriteRule index\.php - [E=test_cond:0,E=qstring:0,E=main_page:0,L]
In order to "clear" the envars, you could use the same kind of construct, i.e.
RewriteRule .* - [E=test_cond:0,E=main_page:0,E=qstring:0,L]
Note that you can't use the [L] flag on any preceding rules that you want to run this line of code, since [L], when invoked, will stop mod_rewrite processing for this HTTP request. The reason your [R] flag worked is that it created a 302 redirect response, ending the current HTTP request, and causing the user-agent to start a new HTTP request using the URL returned in the server's 302 response. This new request would then be processed in a new server context, where the flags you test in the first rule have not yet been defined.
Jim
Yes, that's correct. Sorry I forgot to mention it.
>> The "-" token means "leave the requested URL as-is
The reason I used an url instead of a dash (-) is because I want to be able to redirect if I want to. If I use a dash, it won't redirect.
But then, if I use an url and remove the R, it's an infinite loop. I think it should be possible to create some conditionals to prevent that, but can't figure out a good way of doing it.
I want this to be the ending rule, where I can decide if to redirect or not, and to clear envvars.
A good design rule to use is that mod_rewrite will loop unles you do something to prevent it. If the old and new URLs are different, then it won't loop. But if they are the same, then you need to test something else such as the query string so that you can tell you have already "been through the loop" once and should not redirect again.
Jim
I've been trying to use %{QUERY_STRING} as well, but I don't think it's empty, because even if it originally had no query, that's exactly what those other rules are doing; adding to the querystring. I'm sure I'll find a way, though.
My brain has been working a little overtime (mod_rewrite & regexp-newbie), so I thought I'd look here for some advice first, but don't worry, I'll figure it out :)
What I'm trying to do is quite simple;
My site navigation is based on querystrings in the form of index.php?main_page=bla&page=3&whatever=blabla.
What this rewrite does, is transforming the url
index/<value of main_page>/<some var>/<value of some var>/<some other var>/<value of some other var>/etc.
into
index.php?main_page=somepage&vars'n'values...
like it should be.
First I tried to parse the entire queries in one rewrite-rule, but valid variables range only from $1 to $9, and I needed more, so I needed to spread the parsing on several rules. Then I thought about creating some kind of loop, and eventually came up with the chaos of code that I have now. There's probably a better way, but I can't think of any.
You wouldn't happen to know of any good tutorials covering flags such as E and QSA? I know what they do, but could sure need some hints and tips on how to make good use of them. I have searched but found nothing except the poor description in the httpd docs.
If the code seems somewhat newbieish, it's very correct, I just really begun learning mod_rewrite a couple of days ago. I've been somehow familiar with it for a couple of months, but didn't know anything about regular expressions until I read an article 'learning regexp by examples' by one Dario Gomes from phpbuilder.
Mind if I link to it, so others can learn too?
[phpbuilder.com...]
Regexp wasn't so freightening anymore after reading that article. :)
Anyway, thanks for your time, and I'm sure I'll find a solution :)
I don't know of any tutorials. The functions are simple, though, [E] to define a variable and [QSA] to append a new bit of query string to the existing one. You are using the [E] flag in a proper way, but I do suspect your code could be simplified.
What's lacking is the complete definition of this function:
Rewrite index/<value of main_page>/<some var>/<value of some var>/<some other var>/<value of some other var>/etc. to index.php?main_page=somepage&vars'n'values...
since mod_rewrite requires precise regex and substitutions.
If I understood your static-URL to dynamic-URL mapping exactly, I might be able to suggest something.
To prevent a loop, all you need to do is to make sure that a static URL gets rewritten and that a dynamic one does not. If your static URL has a session ID appended, then maybe you can check for additional variables by looking for ampersands. There's almost always a way to do it, and simpler is best.
To model this out, define each valid variable in the query string by name, value type (i.e. numeric or alpha), valid range, such as a-z or 0-9 (just examples), whether that variable is always present or not, and then any constraints on the order in which those variables must be appended to the URL. Once that is organized into a nice simple chart or list, it's usually much easier to come up with code that implements the necessary rewrite.
Jim
I'm not exactly sure what you mean, because my code works, it's just the last part about removing the R flag that remains.
I can try to explain the code;
RewriteRule ^index/([^/]+)/?(.*) /index\.php\?main_page=$1 [E=test_cond:$2,E=main_page:$1,E=qstring:,C]
This takes care of the first part of the url. It will take what is in between the first 2 slashes (index/this_will_be_taken/).
RewriteRule index\.php(.*) /index\.php\?main_page=%{ENV:main_page}%{ENV:qstring}&%2=%3 [E=qstring:%{ENV:qstring}&%2=%3,E=test_cond:%4,N]
This one, combined with the additional conditionals that do the parsing, does the rest, except that there are 2 copies of it, with slightly different conditionals. I think they could be merged somehow, though, but when I try to remove one of them, long urls won't work. The N flag makes it start over, so it becomes a loop.
And finally,
RewriteRule .* - [R,E=test_cond:0,E=qstring:0,E=main_page:0]
redirects to the correct file with the correct query string, and sets the used envars to 0.
My only problem was that I didn't want to redirect, but rather display /index/products/etc instead of /index.php?mainpage=products&etc.
I did it this way because then I wouldn't have to name every valid variable (there's many). As I see it, this way is simpler. (the code is not simpler, of course, only the method)
Thanks for the tip on session ID, I haven't thought much of that yet. Looking for ampersands sounds like the way to go :)
One more thing; could you give a working example of the use of QSA?
And is there a way to unregister an envar, like "[E=var:,L]"? Will that work correctly, or will the value of var become ",L"? (no problem with the comma, right?)
I used [E=var:0], but if I am correct that will add the value 0 and not delete the variable.
Thanks again for your help :)
Anyway, I cleaned up the code and finally got it to work. Some of my errors were wrong use of the C flag, which stopped a loop from happening.
Here's the working code.
RewriteCond %{REQUEST_URI} /index/.* [NC]
RewriteCond %{ENV:test_cond} !. [NC]
RewriteRule ^index/([^/]+)/?(.*) /index\.php\?main_page=$1 [E=test_cond:$2,E=main_page:$1,E=qstring:] RewriteCond %{ENV:test_cond} . [NC]
RewriteCond %{ENV:test_cond} /?([^/]+)/([^/]+)/?(.*) [NC]
RewriteRule index\.php.* /index\.php\?main_page=%{ENV:main_page}%{ENV:qstring}&%1=%2 [E=qstring:%{ENV:qstring}&%1=%2,E=test_cond:%3,N] RewriteCond %{THE_REQUEST} /index/.* [NC]
RewriteCond %{REQUEST_URI} /index\.php.* [NC]
RewriteCond %{ENV:test_cond} !0 [NC]
RewriteCond %{ENV:test_cond} !/?[^/]+/[^/]+/?.* [NC]
# this will display static urls
# RewriteRule .* - [L,E=test_cond:0,E=qstring:0,E=main_page:0]
# this will display dynamic urls
RewriteRule (.*) /$1 [R,L,E=test_cond:0,E=qstring:0,E=main_page:0] Thanks for your help
-Eivind
I'll assume you have up to 25 parameters (12 name/value pairs) plus the main_page name in your static url.
We'll use this notation to demonstrate:
Requested URL = index/<main_page>/p1/v1/p2/v2/p3/v3/p4/v4/p5/v5/p6/v6/p7/v7/p8/v8/p9/v9/p10/v10/p11/v11/p12/v12
Where <main_page> is your main page as used in the code above, p1-p12 are the parameter names, and v1-v12 are the parameter values.
# Quit if query string is non-blank; That is, quit if a dynamic URL was requested (you
# could also do a "skip" here if you need to process the URL through additional rules)
RewriteCond %{QUERY_STRING} .
RewriteRule .* - [L]
#
# Otherwise, rewrite index/<main_page>/p1/v1/p2/v2/p3/v3/p4/v4/p5/v5/p6/v6/p7/v7/p8/v8/p9/v9/p10/v10/p11/v11/p12/v12
########### to index/<main_page>/p4/v4/p5/v5/p6/v6/p7/v7/p8/v8/p9/v9/p10/v10/p11/v11/p12/v12?p1=v1&p2=v2&p3=v3
RewriteRule ^index/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/(.+)$ /index/$1/$8?$2=$3&$4=$5&$6=$7
#
# Now rewrite index/<main_page>/p4/v4/p5/v5/p6/v6/p7/v7/p8/v8/p9/v9/p10/v10/p11/v11/p12/v12?p1=v1&p2=v2&p3=v3
####### to index/<main_page>/p7/v7/p8/v8/p9/v9/p10/v10/p11/v11/p12/v12?p1=v1&p2=v2&p3=v3&p4=v4&p5=v5&p6=v6
RewriteRule ^index/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/(.+)$ /index/$1/$8?$2=$3&$4=$5&$6=$7 [QSA]
#
# Now rewrite index/<main_page>/p7/v7/p8/v8/p9/v9/p10/v10/p11/v11/p12/v12?p1=v1&p2=v2&p3=v3&p4=v4&p5=v5&p6=v6
####### to index/<main_page>/p10/v10/p11/v11/p12/v12?p1=v1&p2=v2&p3=v3&p4=v4&p5=v5&p6=v6&p7=v7&p8=v8&p9=v9
RewriteRule ^index/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/(.+)$ /index/$1/$8?$2=$3&$4=$5&$6=$7 [QSA]
#
# Now rewrite index/<main_page>/p10/v10/p11/v11/p12/v12?p1=v1&p2=v2&p3=v3&p4=v4&p5=v5&p6=v6&p7=v7&p8=v8&p9=v9
####### to index/<main_page>/p12/v12?p1=v1&p2=v2&p3=v3&p4=v4&p5=v5&p6=v6&p7=v7&p8=v8&p9=v9&p10=v10&p11=v11
RewriteRule ^index/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/(.+)$ /index/$1/$6?$2=$3&$4=$5 [QSA]
#
# Now rewrite index/<main_page>/p12/v12?p1=v1&p2=v2&p3=v3&p4=v4&p5=v5&p6=v6&p7=v7&p8=v8&p9=v9&p10=v10&p11=v11
####### to index/<main_page>?p1=v1&p2=v2&p3=v3&p4=v4&p5=v5&p6=v6&p7=v7&p8=v8&p9=v9&p10=v10&p11=v11&p12=v12
RewriteRule ^index/([^/]+)/([^/]+)/(.+)$ /index/$1?$2=$3 [QSA]
#
# Now move <main_page> into query string
# Extract current query string
RewriteCond %{QUERY_STRING} (.+)
# and append it to the <main_page> query parameter
RewriteRule ^index/([^/]+)/? /index.php?main_page=$1?%1 [L]
#
# finale rule variant for external redirect
#RewriteRule ^index/([^/]+)/? [example.com...] [R,L]
This code is not tested, and is intended only as a demonstration. It shows an "in-line" approach, as opposed to a recursive method using user-defined server variables.
Each of the first three rules moves 3 name/value pairs from the URL to the query string, while preserving the <main_page> value and the existing query string (using [QSA]), and passing the "lefover" URL parameters on to the next rule.
The next two rule move two and one pair of name/value pairs respectively from the URL to the query string.
Note that you must have the last two rules in case the request comes in with a number of name/value pairs that is not a multiple of three. Rule number three increases the number of name/value pairs to 6. Rule number one increases this to up to 9 pairs total. Rule number two increases it to 12 name/value pairs, and then each additonal "copy" of rule number two will add the capability to process 3 more name/value pairs. As shown, this "stack" of rules will work for any number of parameter pairs between 1 and 12.
For each request, only those rules which match will be processed. For example, a request with four name/value pairs will invoke rule #1, rule #4, and rule #5. A request with five name/value pairs will invoke rule #1, rule #3, and rule #5 only, while a request with 12 name/value pairs would invoke all rules.
This code does not use the [E] flag or the [N] flag, handles 12 (or more) name/value parameter pairs, and is probably pretty close to what you need to create a more efficient rewrite.
Jim
Jim
It should have been an obvious thing, but far from everyone (me included) are good at making comprehensive comments, or any comments at all.
And many people, when first commenting, don't do it well, so it doesn't help a bit. So thanks for pointing out the importance of comments. Just because the code I am familiar with seems easy to read to me, doesn't mean it is immediately easy to read for people who haven't looked at it before.
I have learned something important. Thanks again :)
-Eivind