htaccess for clean URLs and redirecting

3 days ago 3
ARTICLE AD BOX

I am using .htacess to convert URLs with php variables to clean URLs.

To clarify, you don't "use .htaccess to convert URLs with php variables to clean URLs" (and that is not what you are doing here). It's the other way round. You use .htaccess to internally rewrite ("convert") clean URLs into URLs with "PHP variables" for PHP to then process. And, strictly speaking, these are "URL parameters", not "PHP variables" (the "PHP variables" come later when PHP converts/decodes the URL parameters to be easily accessible from PHP).

You can also (optionally) redirect URLs with URL parameters to "clean URLs". This is the bit you are stuck on.

RewriteRule ^productgroup/([0-9a-zA-Z]+)$ products/index.php?Cid=$1 [NC,L]

This rule is already complete. If you add an R (redirect) flag to this rule then you are changing it from an internal rewrite into an external redirect. And you are redirecting in the wrong direction (from the "clean URL" to the "URL params").

However, I would question whether you should be using the NC (nocase) flag here, as this potentially opens the site up to duplicate content issues. (ie. Why do you need to allow both productgroup and PRODUCTGROUP and every variation inbetween? These are technically different URLs and consequently are treated differently by search engines.) At the very least, you are unnecessarily rewritting URLs you are not interested in and forcing PHP to process them.

Also, do you need/want to allow uppercase letters (A-Z) in the 2nd path segment? (Do you have mixed case URLs?)

In order to externally redirect requests from products/index.php?Cid=<value> to productgroup/<value> you need to add an additional rule before the above rewrite.

For example, to redirect from /products/index.php?Cid=<value> to /productgroup/<value>:

RewriteCond %{QUERY_STRING} ^Cid=([0-9a-zA-Z]+)$ RewriteRule ^products/index\.php$ /productgroup/%1 [QSD,R=301,L]

The RewriteRule pattern only matches against the URL-path (not the query string), hence the need for an additional condition (RewriteCond directive) to match (and capture) the query string.

The %1 backreference contains the value of the Cid URL parameter as captured in the preceding condition (RewriteCond directive). But note the A-Z in the regex, as mentioned above (is this rquired?).

UPDATE: The QSD (Query String Discard) flag (Apache 2.4) is necessary to remove the original query string from the redirect response. This is preferable to appending an empty query string (a single ?), which is present in the redirect response (the browser strips the trailing ?).

You will also need to change your exitsing rule (the "internal rewrite") to use the END flag (Apache 2.4) instead of L in order to avoid a redirect loop. (Or, you can check the REDIRECT_STATUS environment variable in the above/redirect rule to make sure you are only checking direct requests and not internally rewritten requests by the later rewrite. This would be required if you are on Apache 2.2 for instance.) If you are on LiteSpeed (an Apache replacement) then it doesn't matter as L and END are effectively the same.

Note that this should be a 301 (permanent) redirect. However, test first with a 302 (temporary) redirect to avoid potential caching issues.

Your complete .htaccess would then become:

RewriteEngine On # Externally redirect "/products/index.php?Cid=<value>" to "/productgroup/<value>" RewriteCond %{QUERY_STRING} ^Cid=([0-9a-zA-Z]+)$ RewriteRule ^products/index\.php$ /productgroup/%1 [QSD,R=301,L] # Internally rewrite "/productgroup/<value>" to "/products/index.php?Cid=<value>" RewriteRule ^productgroup/([0-9a-zA-Z]+)$ products/index.php?Cid=$1 [END]

The RewriteBase directive is not required here.

This assumes you are internally linking to the "clean URLs" throughout your HTML.

Read Entire Article