I have finally activated the Apache rewrite module to get rid of the ?q= prefix. This works very well.
However, there are many links out there that still have the ?q= prefix, and this is ugly and upsets both web statistics and search engines, because there are now 2 URLs for each page.
The obvious solution is to use the rewrite module to remove ?q= through an external rewrite, thus:
RewriteCond %{QUERY_STRING} ^q=([^&]*)&?(.*)$
RewriteRule ^ /%1?%2 [NS,R=permanent,L]
This rule removes the q=, but appends any other query string parameters correctly. It works perfectly, apart from the little fact that the whole rewrite module goes into an endless loop, because Drupal's required rule puts the ?q= back in and, for some inexplicable reason, perhaps a defect in the rewrite module or just very sloppy programming, loops back to the rule above, rather than being happy and dropping into Drupal.
I believe that, if I could prevent that loopback and enforce that the rules be processed one after the other only, that would probably solve the problem. I have experimented with using only one external rewrite near the end, but that is even more complex, because then I need another parameter to remember whether any rewrite has taken place and therefore an external rewrite is necessary. I never got that code to work reliably.
I have spent far too much time to investigate this and ended up with an ugly and convoluted workaround. I changed the rule for Drupal to append an extra, arbitrary parameter z=1, which then allows me to check for the loopback and avoid it:
# Remove "?q=", but only if not followed by "&z=":
RewriteCond %{QUERY_STRING} ^q=([^&]*)&?(.*)$
RewriteCond %{QUERY_STRING} !&z=1
RewriteRule ^ /%1?%2 [NS,R=permanent,L]
# Remove "z=...", but only if the query string
# does not begin with "q=". This rule must follow
# the remove "?q=" rule:
RewriteCond %{QUERY_STRING} !^q=
RewriteCond %{QUERY_STRING} ^([^&]*)&?z=
RewriteRule ^(.*)$ /$1?%1 [NS,R=permanent,L]
# Rewrite current-style URLs into the form '?q=command'.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ ?q=$1&z=1 [QSA,L]
# Note that the final URI that goes to Drupal gets a special
# query parameter z=1, whose only purpose it is to be used
# in the earlier rule that removes the q=... parameter.
# Without this stopper there would be an endless loop
# removing and reinserting the q=... parameter.
This does work, but it has at least one side effect, apart from its uglinesssome of the links in Drupal, for example the clickable page numbers at the bottom of lists, still have and show the ?z=1 parameter. I also dread the multiple external rewrites.
Is there anybody here who knows this idiotic rewrite module a bit better than me and can enlighten me with some better solution? Its documentation is a joke tooit doesn't define its function. It only explains some aspects of it superficially.
I append my complete rewrite code below, just in case anybody else has an interest in enforcing standardized URLs. At least it works.
Attention: You may have to adapt the RewriteBase to your server.
Hans-Georg
# 2007-10-05 Hans-Georg Michna - Added all following
# rewrite blocks, except the last one, which is modified:
RewriteBase /
# Rewrite undesirable URL styles
# Remove "?q=", but only if not followed by "&z=":
RewriteCond %{QUERY_STRING} ^q=([^&]*)&?(.*)$
RewriteCond %{QUERY_STRING} !&z=1
RewriteRule ^ /%1?%2 [NS,R=permanent,L]
# Remove "z=...", but only if the query string
# does not begin with "q=". This rule must follow
# the remove "?q=" rule:
RewriteCond %{QUERY_STRING} !^q=
RewriteCond %{QUERY_STRING} ^([^&]*)&?z=
RewriteRule ^(.*)$ /$1?%1 [NS,R=permanent,L]
# Remove index.php:
RewriteRule ^/*index\.php/*(.*)$ /$1 [NS,R=permanent,L]
# Remove "&...":
RewriteRule ^(.*)& /$1 [NS,R=permanent,L]
# Remove leading & from QUERY_STRING:
RewriteCond %{QUERY_STRING} ^&+(.*)$
RewriteRule ^(.*)$ /$1?%1 [NS,R=permanent,L]
# Remove superfluous multiple leading slashes:
RewriteCond %{REQUEST_URI} ^//+(.*)$
RewriteRule ^ /%1 [NS,R=permanent,L]
# Remove superfluous multiple inserted slashes:
RewriteCond %{REQUEST_URI} ^/*([^/]+)//+([^/].*)$
RewriteRule ^ /%1/%2 [NS,R=permanent,L]
# Add trailing slash to directories:
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*[^/])$ /$1/ [NS,R=permanent,L]
# Remove trailing slashes from non-directories:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+?)/+$ /$1 [NS,R=permanent,L]
# Remove node:
# RewriteRule ^node$ / [NS,R=permanent,L]
# Do not activate. This rule makes it appear as if
# the logon does not work, because after logging on
# Drupal redirects the user from / to /node
# to force the browser to reload.
# An extra rule to replace /node with / is required
# in Google Analytics.
# Rewrite current-style URLs into the form '?q=command'.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ ?q=$1&z=1 [QSA,L]
# Note that the final URI that goes to Drupal gets a special
# query parameter z=1, whose only purpose it is to be used
# in the earlier rule that removes the q=... parameter.
# Without this stopper there would be an endless loop
# removing and reinserting the q=... parameter.