views:

804

answers:

2

I would like to map this:

`http://www.example.com/index.php?param1=value1&param2=value2&param3=value3` (etc. ad infinitum)

to

`http://www.example.com/index.php?param1=newvalue1&param2=value2&param3=value3` (etc.)

ie. just change the value of a single parameter in the querystring. I know what the old value is, so I am trying to match the exact text index.php?param1=value1 and replace it with index.php?param1=newvalue1. I cannot seem to find any examples on how to do this using mod_rewrite. Any assistance greatly appreciated.

+2  A: 

Try this rule:

RewriteCond %{QUERY_STRING} ^(([^&]*&)*)param1=value1(&.*)?$
RewriteRule ^index\.php$ /index.php?%1param1=newvalue1%3 [L,R=301]
Gumbo
That nearly does it! Just the additional parameters are not tagged onto the end (param2, param3, etc.)
Russ
@Russ: Little typo: use `%3` instead of `%4`.
Gumbo
That works a treat, many thanks. I will add a vote as soon as I have a high enough reputation (will not let me vote you up until I reach 15).
Russ
Actually, still a slight problem - although it works with the [R=301] in there, if I take that out (in an attempt to do a transparent redirect), it no longer works - the rule seems to be completely ignored.
Russ
Btw, I probably should add that I also want to preserve any POST values during this redirect. I understand redirecting to internal pages with mod_rewrite should preserve post data, but it is not working in this case - only GET data is present after the redirect.
Russ
@Russ: All POST variables should be preserved when doing an internal redirect (without `R` flag). But if you just want to change a specific GET value and don’t want to redirect, why don’t you just change it with PHP?
Gumbo
That's what I thought, but without the R flag, the whole thing is just ignored. As soon as I put the R flag back in, it works again but I lose the POST data. I cannot use PHP because this is all taking place within the context of a CMS over which I have no control.
Russ
@Ross: Do you use other rules that might conflict with this one?
Gumbo
This is the first rule after the RewriteEngine On directive, and as it ends with [L] the other rules that appear later should not affect it. After some experimentation, I have found that using the [P] flag makes it work - but I am not sure exactly what that means or whether it would work for everyone (I will have to get lots of other people to implement this rule).
Russ
@Ross: The `L` flag just ends the current processing. But it will also reinject the request and will cause processing to be repeated starting from the first rule (see http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewriteflags). The `P` flag is to use a proxy to request the rewritten URL and pass the response back to the client. That does require mod_proxy.
Gumbo
Ok, I understand that, but if it works when redirecting I still don't see why it doesn't work when rewriting internally, as both processes should use the same rules. The remaining rules are just the default ones the CMS uses for SEF purposes, but they should not affect the rewritten URL. Anyway the proxy flag seems to work, so unless you (or anyone else) has any further ideas, I guess I will stick with that. Thanks very much for all your help.
Russ
@Ross: Yes, they should. You could enabling logginf for mod_rewrite (see http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewriteloglevel) to see what rules are applied.
Gumbo
I tried that, but the log file, although created, is always empty (whether rewrites are successful or not).
Russ
Ok, I got the RewriteLog working (I had neglected to set RewriteLogLevel), and I think I found the cause - after processing the rule, control was passed to index.php, but the original URL was still available to index.php, so it was using that to serve up the original page instead of the new page. When using the redirect or proxy flag to re-fetch the page, the original URL was no longer available to index.php, so it served up the correct (new) page. That's what it looks like to me anyway. I have a (rather ugly) workaround for the few cases where [P] is disabled, so I should be ok. Thanks again!
Russ
@Russ: You could also try the `PT` flag.
Gumbo
Tried that, but it did not work. Only P seems to work.
Russ
A: 

this is kind of a brittle solution in that it depends on the order of the GET params but it works for your specific example, preserves any GET args after param1 and also preserves POST args:

RewriteCond %{QUERY_STRING} param1=value1(&.*)*$
RewriteRule ^/index\.php$ /index.php?param1=newvalue1%1 [L]

I have a little test php page that just does print_r($_GET) and print_r($_POST) and using curl from the command line with post args i see the following:

$ curl --data "post1=postval1&post2=postval2" "http://www.example.com/index.php?param1=value1&param2=value2&param3=value3"
_GET
Array
(
    [param1] => newvalue1
    [param2] => value2
    [param3] => value3
)

_POST
Array
(
    [post1] => postval1
    [post2] => postval2
)

if you wanted the rewrite condition to be more flexible you could add some conditional patterns like Gumbo did but it would be good to know exactly what conditions you need to handle (i.e. can param1 be in any position, can it be the only get arg, etc.)

edit for new requirements below

the following seems to work for replacing "value1" with "newvalue1" anywhere in the query string or the url (but not in post'ed keys/values):

RewriteCond %{QUERY_STRING} ^(.*)value1(.*)$
RewriteRule ^(.*)$ $1?%1newvalue1%2 [L]

RewriteRule ^(.*)value1(.*)$ $1newvalue1$2 [L]

%N is used for substituting values from the RewriteCond while $N is used for substituting values from the RewriteRule itself. just used two RewriteRules, one of them with the associated RewriteCond to handle the query string.

jnichols959
Russ