I am attempting to take

example.com/home?lang=fr&foo=bar

and redirect to

example.com/fr/home?foo=bar

I attempted

    RewriteCond %{QUERY_STRING} lang=([a-z]{2})&?
    RewriteRule ^(.*) /%1/$1 [R=307]

however i obtain a redirect loop. The reason being the lang=fr isn't taken from the query string after 'fr' is gone to live in the leading from the request string. This leads to:

example.com/fr/fr/fr/fr/fr/fr/fr/fr/fr/....

I'm able to use

    RewriteRule ^(.*) /%1/$1? [R=307]

but which removes the foo=bar in the query string which I wish to keep intact

I am unable to discover a way of getting rid of only one variable in the query string. I am sure there's an easy solution but google has not assisted. Thanks

This can be a variation of the technique in the apache wiki which should have the desired effect:

RewriteCond %{QUERY_STRING} ^(.*)lang=([a-z]{2})&?(.*)$
RewriteRule (.*) /%2/$1?%1%3 [R=307]

In complement to @whoisgregg answer (+1), the industry nice adaptation in the apache wiki. Rules you will find not complelty nice (for instance the access control by query string in this article would'nt resist on the little urlencoding).

There exists a working solution for that test situation, however, many extended tests may fail:

RewriteCond %{QUERY_STRING} ^(.*)lang=([a-z]{2})&?(.*)$
RewriteRule (.*) /%2/$1?%1%3 [R=307]

foo.php?foo=bar&novlang=fr&toto=titi => /fr/foo.php?foo=bar&novtoto=titi

Here the lang= part is detected but when any parameter finishes with lang you will have a problem.

So, we want some fix, I have been focusing on that subject now and here is a better version I believe:

RewriteCond %{QUERY_STRING} (.*)(^|&|%26|%20)lang(=|%3D)([^&]+)(.*)$
RewriteRule (.*) /%4/$1?%1%5 [R=307]

foo.php?foo=bar&novlang=fr&toto=titi => not matched (normal, no lang=)
foo.php?foo=bar&lang=fr&toto=titi&b=a => fr/foo.php?foo=bar&toto=titi&a=b

The only issue pending here's that lang might be partly or completly url encoded and wouldn't be detected, but that is an uncommon situation. I alos removed the charge of getting only 2 chars around the lang value. I believe ([^&]+) is much better, this means all figures until I match an &.