Forum Moderators: phranque
I came up with (for in httpd.conf):
RewriteRule ![A-Z] - [S=2]
RewriteCond %{REQUEST_URI} !^/(img¦css)/.*$
RewriteCond %{REQUEST_URI} ^/.*_
RewriteRule ^/(.*)_(.*) /$1-$2 [N,QSA]
rewritemap lowercase int:tolower
RewriteCond %{REQUEST_URI} !^/(img¦css)/.*$
RewriteCond $1 [A-Z]
RewriteRule ^/(.*)$ http://example.com/${lowercase:$1} [R=301,L]
Question: is this the best/fastest way to do this?
Rewriterule ^(img¦css)/ - [L]
RewriteRule ![A-Z] - [S=2]
RewriteCond %{REQUEST_URI} ^/.*_
RewriteRule ^/(.*)_(.*) /$1-$2 [N,QSA]
rewritemap lowercase int:tolower
RewriteCond %{REQUEST_URI} !^/(css¦img)/.*$
RewriteCond $1 [A-Z]
RewriteRule ^/(.*)$ http://example.com/${lowercase:$1} [R=301,L]
I think its getting better...
RewriteCond %{REQUEST_URI} ^/.*_ Similarly, the second RewriteCond in the third rule is redundant and can be removed by changing the follwoing rules pattern:
RewriteMap lowercase int:tolower
RewriteCond %{REQUEST_URI} !^/(css¦img)/.*$
RewriteRule ^/([^A-Z]*[A-Z].*)$ http://example.com/${lowercase:$1} [R=301,L]
Also, because of the patterns in the other rules, I assume that this code goes into httpd.conf or some other server configuration file, outside of any <Directory> container, and not in a .htaccess file. In that case, the RewriteRule's pattern must start with a slash, as shown in your other rules.
Jim
For example, you could replace the whole thing with:
RewriteMap lowercase int:tolower
#
RewriteCond $1 !^(img¦css)/
RewriteCond $1 [A-Z]
RewriteRule ^/(([^_]*)_(.*))$ /$2-$3 [E=ReplacedUnderscore:Yes,N]
#
RewriteCond %{ENV:ReplacedUnderscore} =Yes
RewriteRule ^/(.+)$ http://example.com/${lowercase:$1} [R=301,L]
But, that's just one of many methods that can work... :)
(Remember to replace that broken pipe character)
If you only have a known-maximum number of underscores, you could write a stack of rules. The first rule could replace three underscores if found, the second could replace two, and the last rule could replace one, for up to a total of six. Then do the lowercasing. This avoids having to do looping in the code, which can be very slow if the underscore-replacing rule isn't at or very near the top of the code.
If you use that approach, it's important that each rule replace one less underscore than the one that precedes it, or you will get into a situation where the rule-set fails when certain numbers of underscores are present.
You'd also want to skip the whole stack if the requested URL-path does not contain both underscores and uppercase characters, or if it is for /img/ or /css/ paths.
Alternatively, you could define a RewriteMap to call a PERL script that both replaces all underscores at once and converts the result to lowercase, greatly simplifying the rule, which would then only have to test the conditions of the first rule in this post.
I don't know which way is faster/better; You'll have to test to see what kind of performance impact each method has on your server.
Jim
The second rule only runs if the first rule has set the 'ReplacedUnderscore' flag.
You can play around with the order of the first rule's RewriteConds if you like. Put whichever RewriteCond is likely to fail first. I put the img/css RewriteCond first because most requests to a typical web site are for images.
Jim
Problem: I would like to redirect (301) my current URLs, which uses underscores and capital letters, to a new URL which uses hyphens and only lowercase. A couple of directories can not be included (img & css) in the entire process.
Correct your initial requirements specification, and start again...
You may dispense entirely with the "ReplacedUnderscore" variable logic if you wish to correct case and underscores separately, and simply replicate the RewriteConds from the first rule to the second as needed -- or use a "skip rule" as you did at the start.
Jim
Except when an URL does not have underscore(-s), the uppercase characters are not being replaced by lowercase.
So the problem is: replace underscore and/or uppercase in one redirect.
RewriteRule ![A-Z] - [S=1]
RewriteCond $1 [A-Z]
RewriteRule ^/(.+)$ http://example.com/${lowercase:$1} [R=301,L]
RewriteMap lowercase int:tolower
#
RewriteCond $1 !^(img¦css)/
RewriteCond $1 [A-Z]
RewriteRule ^/(([^_]*)_(.*))$ /$2-$3 [E=ReplacedUnderscore:Yes,N]
#
RewriteCond %{ENV:ReplacedUnderscore} =Yes
RewriteRule ^/(.+)$ http://example.com/${lowercase:$1} [R=301,L]
RewriteRule ![A-Z] - [S=1]
RewriteCond $1 !^(img¦css)/
RewriteCond $1 [A-Z]
RewriteRule ^/(.+)$ http://example.com/${lowercase:$1} [R=301,L]
It seems to me that what you're wanting --because you have not explicitly corrected your original requirements statement quoted above-- is simply:
RewriteMap lowercase int:tolower
#
# If uppercase characters and underscores, but not an /img/ or
# /css/ subdirectory request, replace underscores with hyphens
RewriteCond $1 !^(img¦css)/
RewriteCond $1 [A-Z]
RewriteRule ^/(([^_]*)_(.*))$ /$2-$3 [N]
#
# If uppercase characters but not /img/ or /css/ request, convert to lowercase
RewriteCond $1 !^(img¦css)/
RewriteRule ^/([^A_Z]*[A-Z].*)$ http://example.com/${lowercase:$1} [R=301,L]
The most important step of any coding project is to correctly and precisely define your requirements. Too often there is a rush to coding when requirements have not yet been fully specified. The result is almost always a lot of wasted time and effort, as this thread now illustrates. For a more-joyful life, spend the time needed to write down concise requirements, and then comment your code accurately and thoroughly.
Jim
A RewriteRule won't run if the pattern in the RewriteRule itself does not match, or if a condition in a RewriteCond does not match. In addition, RewriteConds are not processed at all if the RewriteRule pattern does not match (see Apache mod_rewrite documentation for details).
Disregarding all of the details, though, you should be able to establish whether the proposed code works or not simply by testing two sets of URLs: Test URLs which should be redirected, and test other URLs which should not be redirected. If operation is correct for both sets of URLs, and the URL-sets are sufficiently representative of all of the URLs used by your site, then the code can be shown to "do what you want."
I'm sorry, but I don't see or understand the 'disconnect' here that is blocking understanding. This is probably because I've been programming in one form or another for 40+ years, and I just "think in terms of logic and code" naturally.
Jim
So sorry for the n00b questions but it was just to be certain. I already tested the script and its running perfectly.
So thank you for your help and your fatherly lectures.