Search code examples
apache.htaccesshttp-redirectmod-rewriteurl-rewriting

Using mod_rewrite to redirect based on query string parameter


I am trying to get something working with mod_rewrite, but am not entirely sure it's even possible.

Say I have some request like:

https://www.example.com?h=somerandomvalue&u=www.another.example/content

I'd like to take the value of the u parameter and rewrite the request using this value, so from the above example request, I'd like to rewrite it to be

https://www.another.example/content

So basically, request:

https://www.example.com?h=somerandomvalue&u=www.another.example/content

Redirects to:

www.another.example/content

Is this possible? I've seen other examples where people are capturing and using parameters as part of a rewritten path but I have not seen anything in the way of redirecting to an entirely new domain using the provided parameter.

Any help or guidance here is very much appreciated!


Solution

  • I've seen other examples where people are capturing and using parameters as part of a rewritten path

    Yes, this is possible. The basic principle is the same:

    RewriteEngine On
    
    RewriteCond %{QUERY_STRING} (?:^|&)u=([\w./-]{6,})(?:&|$)
    RewriteRule ^$ https://%1 [QSD,R=302,L]
    

    %1 is a backreference to the capturing group in the preceding CondPattern. ie. the value of the u URL parameter.

    ([\w./-]{6,}) is just a very rudimentary check to match a semi-valid URL. This can be improved.

    The (?:^|&) prefix on the regex ensures we only match the u URL parameter and not any URL parameter that simply ends in u.

    This only redirects requests for the root, as in your example. A malicious user could turn your site into a relay to forward traffic.

    However, without further validation of the URL being redirected to this is potentially open to abuse and should not be used.

    UPDATE#1:

    For example, you could limit the hostnames that can be redirected to with a series of RewriteCond directives:

    RewriteCond %{QUERY_STRING} (?:^|&)u=([\w./-]{6,})(?:&|$)
    RewriteCond %1 ^(www\.another\.example/.*) [OR]
    RewriteCond %1 ^(www\.two\.example/.*) [OR]
    RewriteCond %1 ^(www\.three\.example/.*) [OR]
    RewriteCond %1 ^(www\.four\.example/specific/path/to/file.html)
    RewriteRule ^$ https://%1 [QSD,R=302,L]
    

    The %1 backreference (which contains the captured group from the last matched CondPattern) matches the complete URL that is sent in the u URL parameter. This relies on the fact that RewriteCond directives finish "early" when the condition is satisfied.

    If you have many specific URLs then you could create a RewriteMap in the server config and perform a lookup to check the valid URLs.

    Alternatively, rewrite the request to a server-side script (eg. PHP) and manage the checks and redirection in the script instead.


    UPDATE#2:

    Does this example also work if the u parameter is an encoded URL?

    Not currently. The QUERY_STRING server variable is not %-decoded. To allow a URL encoded (%-encoded) parameter value then you will need to include % in the regex character class that matches the URL and include the NE (noescape) flag on the RewriteRule directive to prevent any %-encoded characters being doubly encoded in the redirect response.

    For example:

    RewriteCond %{QUERY_STRING} (?:^|&)u=([\w%./-]{6,})(?:&|$)
    :
    RewriteRule ^$ https://%1 [NE,QSD,R=302,L]