Search code examples
.htaccessmod-rewriteapache2

Apache2 ModRewrite rule to remove trailing character in a query param


This should be fairly straightforward but I'm having a lot of trouble.

Goal: If the "name" query param has a trailing colon, remove it:

https://example.com/dir/?foo=bar&name=hello:&baz=bat#123  ==>
https://example.com/dir/?foo=bar&name=hello&baz=bat#123

I've tried a BUNCH of rules and haven't found one that works. The closest I've come is:

# Grab "name" value w/out trailing colon
RewriteCond %{QUERY_STRING} (.*[?&]*)name=(.*):(.*)
# Duplicate original path and add query parameters
RewriteRule ^(.*)$ /$1?%1name=%2%3 [L,R=302]

This almost works, except it redirects to the top-level directory:

https://example.com/dir/?foo=bar&name=hello:&baz=bat#123  ==>
https://example.com/?foo=bar&name=hello&baz=bat#123

I want this rewrite to work for the top-level domain name or any number of subdirectories. How can I accomplish that? Thanks!


Solution

  • (.*[?&]*)name=(.*):(.*)
    

    This regex doesn't just match a trailing colon at the end of the name URL parameter value, it matches name= followed by : anywhere in the query string (including in the middle of or as part of other URL parameters). It also matches any URL parameter name that simply ends with name, eg. fooname=hello&bar=ba:z.

    The missing subdirectories would seem to suggest you are using this rule in a .htaccess file in the /dir subdirectory (as opposed to the root)? The RewriteRule pattern (which you are capturing) only matches the URL-path relative to the directory that contains the .htaccess file.

    Note that the fragment identifier (the #123 part) is not sent to the server - this is managed entirely by the browser.

    Depending on the URL parameter values you are expecting, you may need the NE (noescape) flag on the RewriteRule directive to prevent the resulting values being doubly URL-encoded in the redirect response. This is because the QUERY_STRING server variable is not %-decoded and the resulting substitution string is %-encoded by default before sending this back to the client.

    Try the following instead:

    # Grab "name" value w/out trailing colon
    RewriteCond %{QUERY_STRING} ^(.+&)?(name=[^&]*):(&.+)?$
    # Duplicate original path and add query parameters
    RewriteRule ^ %{REQUEST_URI}?%1%2%3 [NE,R=302,L]
    

    Needless to say, this needs to go near the top of the .htaccess file, before any existing (internal) rewrites.

    I included the name= part in the capturing subpattern. Any URL parameters before or after the name URL param are entirely optional. Only colons at the end of the name URL parameter value are removed.

    The REQUEST_URI server variable contains the root-relative URL-path (including the slash prefix), so this works to preserve the URL-path, regardless of where the .htaccess file is located.