Rewrite rule with QUERY_STRING don't work

Solution 1:

RewriteCond %{QUERY_STRING} p=(.*)
RewriteRule ^(.*)/p=$ $1?page=%{QUERY_STRING} [L,QSA]

There are numerous "errors" here:

  • The RewriteRule pattern, ie. ^(.*)/p=$, will never match because p= does not occur as part of the URL-path (it is part of the query string). The RewriteRule pattern matches against the URL-path only, which notably excludes the query string. So, the above will do nothing for the example URL stated.

  • You are using the QUERY_STRING server variable in place of the required URL parameter value in the substitution string. But QUERY_STRING is obviously the entire "query string", so would end up constructing a malformed query string of the form: page=p=<value>.

  • The use of the QSA flag results in the original query string on the request being merged with the query string you are using in the substitution string. So, this would result in &p=<value> being appended, which is not what you required. You are looking to replace the existing query string.

  • RewriteRule ^(.*)/p=$ $1?page= - You are excluding the trailing slash from the capturing group in the RewriteRule pattern, so this would effectively remove the trailing slash from the rewritten URL. This is different to your example.

  • You appear to require an internal rewrite (no external redirect) and you are "only" changing the name of a single URL parameter. This is something you would ordinarily resolve in your application, rather than a URL rewrite in Apache. (?)

Try the following instead:

RewriteEngine On

# Internally "rewrite" the request
RewriteCond %{QUERY_STRING} ^p=([^&]*)$
RewriteRule (.*/)$ $1?page=%1 [L]

This matches any URL-path that ends in a slash (as per your example). The %1 backreference refers to the captured group in the last matched CondPattern, ie. the value of the p URL parameter. It is assumed that p=<value> is the only URL parameter on the request.


UPDATE: it seems not to work... if i call localhost/list/?p=2 i simply get localhost/list/?p=2

Yes, the visible URL does not change, since this is an internal "rewrite" (as you appear to be requesting in your question). The URL is internally rewritten to list/?page=2. (There is also the point that list/?page=2 is not strictly a valid end-point - it requires further rewriting, perhaps by mod_dir to form a valid request. eg list/index.php?page=2?)

If you want the URL to visibly change then maybe you do require an external "redirect" after all? For this you need to include a slash prefix on the RewriteRule substitution string and add the R (redirect) flag. For example:

# Externally "redirect" the request
RewriteCond %{QUERY_STRING} ^p=([^&]*)$
RewriteRule (.*/)$ /$1?page=%1 [R=302,L]

if i modify the rule to get localhost/list/?p=2&?p=2 as below:

RewriteEngine On
RewriteCond %{QUERY_STRING} ^p=([^&]*)$
RewriteRule (.*/)$ $1&?page=%1 [L]

i get 'The requested URL /list/& was not found on this server.' as '?' cut the rewrite

If you request localhost/list/?p=2&?p=2 (but why?) then the above directive(s) won't do anything at all since the condition will not match.

Why did you add a & in the RewriteRule substitution string? (That does not make sense?) That would indeed result in a URL-path of the form /list/&, if the rule was processed. But as stated, it wouldn't be processed for the URL you've stated?