1
sunsnapper
Apache mod_rewrite RewriteRule Success

I have had some good luck creating user-friendly URLs using Apache mod_rewrite. Hopefully, people will find this information helpful.

At the bottom of this post are some links to resources that give additional details about how and why you might want to do this... to make your URLs easier for users to remember, bookmark, and email to friends.

In this example, I added these rules to my .htaccess file in my web root. If you additionally have access to your server root you might want to add similar statements to the httpd.conf file instead (though you will need to refer to the documentation links to double check for any required syntax modifications).

(If your FTP client does not show hidden files like .htaccess check to see if the FTP client has a preference to "show hidden files" or a preference to modify your LIST command to LIST -al instead.)

Whatever you do, make a backup copy of the files you intend to edit. Adding incorrect entries to these files can result in server configuration errors that will prevent your server from delivering pages. If you are editing your theme.html file to take advantage of these new links, you'll enjoy the peace of mind of backing that up to. Seriously... back up those files before you start!

Each entry should appear on a single line... do not allow the entries to wrap.

You'll notice that each RewriteRule begins with a pattern it is trying to match. This is identified with a beginning ^ and ending $.

At the end of each RewriteRule line in this example, you will see [L]. This tells the server that if it finds the match to stop looking for additional matches.

(There are other things you can refer to the documentation to learn more about).

I added the following lines to the bottom of my .htaccess file and they are working wonderfully:
RewriteEngine on
RewriteRule ^signout/$ /user.php?op=logout [L]
RewriteRule ^signout(.*)$ $1 [L]
RewriteRule ^myaccount/$ /user.php [L]
RewriteRule ^myaccount(.*)$ $1 [L]
RewriteRule ^contact(.*)$ /modules/contact$1 [L]
RewriteRule ^products/$ /modules/wfchannel/index.php?pagenum=2 [L]
RewriteRule ^discuss(.*)$ /modules/newbb$1 [L]
RewriteRule ^about/$ /modules/wfchannel/index.php?pagenum=1 [L]
RewriteRule ^about$ /modules/wfchannel/index.php?pagenum=1 [L]


Now to explain a few of these entries...

Line 1: RewriteEngine on
This makes the server aware it needs to look for pattern matches.

Line 2: RewriteRule ^signout/$ /user.php?op=logout [L]
When the server gets a request for signout/ it returns the result of /user.php?op=logout
The effect is:
http://www.domain.com/signout/ results as
http://www.domain.com/user.php?op=logout

Line 3: RewriteRule ^signout(.*)$ $1 [L]
When the server matches signout the characters after signout are passed to $1
This is important in this position to make sure that relative paths do not try to operate from the imaginary /signout/ directory.
The effect is:
http://www.domain.com/signout /foofoo results as
http://www.domain.com/foofoo

I will skip a couple lines since they use the same technique.

Line 6: RewriteRule ^contact(.*)$ /modules/contact$1 [L]
When the server matches contact the characters after contact are passed $1 appended to /modules/contact
The effect is:
http://www.domain.com/contact /foofoo results in
http://www.domain.com/modules/contact/foofoo

In these examples, note that when I want the root of a subdirectory to return a single specific item (line 2), I include the explicit command. I place this command above the "catch-all" directory passing (line 3) that occurs when I include (.*) in the rule.

Those are the basics. I hope you have found this helpful. Granted, this is not something everyone wants to take the time to do. But, if you get excited about controlling the URLs your visitors see and use, having the flexibility is wonderful.

Note that I am doing this with XOOPS 2.0.5. To get this to work, currently you need to use Onokazu's perfect one line hack as discussed in this thread.



Here are links to additional resources:
URLS! URLS! URLS!: Why and how to use mod_rewrite
http://www.alistapart.com/articles/urls/

Examples of using mod_rewrite
http://www.engelschall.com/pw/apache/rewriteguide/

Apache mod_rewrite manual
http://httpd.apache.org/docs/mod/mod_rewrite.html

This article about handling URLs with PHP might also be of interest:
http://www.alistapart.com/articles/succeed/

2
Herko
Re: Apache mod_rewrite RewriteRule Success
  • 2003/11/6 8:47

  • Herko

  • XOOPS is my life!

  • Posts: 4238

  • Since: 2002/2/4 1


Great work sunsnapper! I think you've made many people happy with this post I'll add it to the wiki and future documentation as well

Herko

3
sunsnapper
Re: Apache mod_rewrite RewriteRule Success

Thanks Herko. I'm glad you found it useful.

4
gstarrett
Re: Apache mod_rewrite RewriteRule Success
  • 2003/11/6 20:54

  • gstarrett

  • Friend of XOOPS

  • Posts: 174

  • Since: 2002/3/12


Sometimes it just takes a helping hand to get people in view of a new powerful technology... this definately qualifies. With the pervasiveness of Apache and power of htaccess files in general, this is a Very Good Thing, and your post will certainly help get (me anyway!) started there.

Thanks Sunsnapper!

5
Draven
Re: Apache mod_rewrite RewriteRule Success
  • 2003/11/6 22:50

  • Draven

  • Module Developer

  • Posts: 337

  • Since: 2003/5/28


I've looked into doing this myself but ran into one major problem, the only way to hide the ?var=value portion is to declare the full url in the mod_rewrite. This isn't feasible when talking about large dynamic pages like forums and WF_section articles.

While you can do it for specific pages like your example

RewriteRule ^products/$ /modules/wfchannel/index.php?page=3 [L]

what I believe would be optimal is the ability to pass var value pairs through folder like representation. So instead of mapping every single URL that contains var value pairs you do so something like this:

http://yourdomain.com/articles/articleid/3

and have mod rewrite conver that to

http://yourdomain.com/modules/wfsection/article.php?articleid=43

Basically it would match the article portion first, then anything after that would be broken in to var=value, in this case ?articleid=3. Another option is to use Apache's look back feature (Info on that found here http://www.sitepoint.com/article/485/1). But this requires some php code to run to further break down the URL and having the .php portion still messes up some search engine spiders. We could use the Force Type Derective but that would require renaming all major files from "filename.php" to "filename" which apache would then run as a php file because of the forced type.

Ultimately, your above way works well for small sites and for people that don't mind still using the ?var=value way of passing variables, but for true search engine friendliness you need to eliminate the ?var-value portion of the URL all togther. As far as I know googlebot is the only spider able to traverse dynamic pages. So if you want in to other search engines beyond your homepage, you need to eliminate these variables from the URL.

6
sunsnapper
Re: Apache mod_rewrite RewriteRule Success

Draven, if I understand your example correctly, I think mod_rewrite will let you accomplish this in a way that only requires a single rule. Before this gets moved into my "forgotten knowledge file" and while I still have this fresh in my mind... I think the rule would be:

[size=xx-small][font=Courier]
RewriteRule ^articles/articleid/(.*)$ /modules/wfsection/article.php?articleid=$[L]
[/
font][/size]


I think that looks correct to me. I'll give it a try and update this post tomorrow with confirmation.

I think the challenge then becomes having the module automatically fill its blocks with the new style URL.


Side Note: If modules referenced articles with a unique Title instead of an ID, one could use a similar rule to make this
http://domain.com/modules/wfsections/ ... ticletitleid=greatestnews
look like
http://domain.com/articles/greatestnews.html
which could be nice.
Without changing from IDs to Titles, about the best one could do (with the example above) would be to create a rule that mapped a URL like
http://domain.com/articles/3.html


7
Draven
Re: Apache mod_rewrite RewriteRule Success
  • 2003/11/7 18:38

  • Draven

  • Module Developer

  • Posts: 337

  • Since: 2003/5/28


Your example above still requires the key value pairs being passed through the URL in a normal fasion (?this=that). What I'm talking about is writing a url as

http://www.mysite.com/library/articles/articleid/31/page/2


While the above isn't a shorter url it does do one very important thing, it romves the "?" from the URL which is what trips up most search spiders.

Now if there was a way to have mod_rewrite - Keep in mind I'm not all that familiar with this function of apache so I may be missing something - rewrite the url to something like

/modules/wfsection/articles.php?articleid=31&page=2


that would be great. Now this could be done I suppose in this type of fashion:

1. match the first directory against some list of modules where we know /library/ is equal to /modules/wfsections/

2. Then match the next directory in the URL to a php file, in this case /articles/ is equal to /articles.php.

3. Anthing after the page match is a var=value par. simple php loop can determine that.

So the URL is broken up like this

http://[domain name]/[module alias]/[page name]/[var/value]

Once this works for one module it would work for all. The only thing taht might trip it up a bit is sub directories of the main, but if you mape the sub direcotries to the [module alias] it won't matter. The nice things is the [module alias] could be set in the Xoops_version file. Mean while never actually changing the modules absolute path.

Hope this makes sense. It does in my brain, but getting it our is the hard part.

-----

As for the problem of writing the initial url, this is a bit of work since there would need to be a common makeurl() function used in all modules and the core. The nice thing with that is you could have error checking for broken links easily incorporated into such a function.

Something like this:

Quote:

function makeUrl($mod_path,$mod_alias, $page_name, $var_array){

// error check, does this file exist?
if(!file_exists($mod_path.$page_name){
$error = 'return an error, page doesn't exist';
return $error;
}

// assemble var value paring
$count = count($var_array);
if(is_array($var_array)){
for($i=0;$i!=$count;$i++){
var_val .= $var_array[$i]['var'].'/'.$var_array[$i]['value'];
($i < $count)? var_val .= '/':'';
}
}

// strip the ".php" off the page name
$page = substr($page,0,strpos(".php",$page));

// assemble new url
$url = $mod_alias.$page."/".var_val;

return $url;


Don't use the above, it's not tested but you get the idea.

8
sunsnapper
Re: Apache mod_rewrite RewriteRule Success

UPDATE: I am happy to report, the scripts in this example "work" (i.e., they shouldn't crash the server) however, I need to investigate why it's not working with a page number var included.


Now I see where you are going... I think this will work. I'll go test it now.

[size=x-small][font=Courier]
RewriteRule ^library/([^/]+)/([^/]+)/([^/]+) /modules/wfsection/$1.php?$2=$3&page=$[L]
[/
font][/size]


should map
library/articles/articleid/31/2
to
/modules/wfsection/articles.php?articleid=31&page=2

The question arises of how to do the map if a page number is not included in the URL. There may be other ways, but, one way would be to have a second RewriteRule for that scenario that comes AFTER the first rule. Another would be to always include the page number in the URL (though this is risky, since you can't count on the user doing that if they are guessing at a URL by hand.) Also, it may be best to include another catch-all line AFTER that to keep other module functions working from underneath the library directory.

So, together, the rules would appear as:
[size=x-small][font=Courier]
RewriteRule ^library/([^/]+)/([^/]+)/([^/]+) /modules/wfsection/$1.php?$2=$3&page=$[L]
RewriteRule ^library/([^/]+)/([^/]+) /modules/wfsection/$1.php?$2=$[L]
RewriteRule ^library(.*)$ $[L]
[/
font][/size]

9
Draven
Re: Apache mod_rewrite RewriteRule Success
  • 2003/11/8 21:16

  • Draven

  • Module Developer

  • Posts: 337

  • Since: 2003/5/28


What if you just ran a buch of different scenarios, with a max of say 5 (I don't think any modules pass more than 5 var/values pairs.)

So wether your URL was:
/modules/wfsection/articles.php?articleid=31&page=2&var=value&var=value&var=value
/modules/wfsection/articles.php?articleid=31&page=2&var=value&var=value
/modules/wfsection/articles.php?articleid=31&page=2&var=value
/modules/wfsection/articles.php?articleid=31&page=2
/modules/wfsection/articles.php?articleid=31

You'd have:
RewriteRule ^library/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+) /modules/wfsection/$1.php?$2=$3&$4=$5&$6=$7&$8=$9&$10=$11 [L]
RewriteRule ^library/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+) /modules/wfsection/$1.php?$2=$3&$4=$5&$6=$7&$8=$[L]
RewriteRule ^library/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+) /modules/wfsection/$1.php?$2=$3&$4=$5&$6=$[L]
RewriteRule ^library/([^/]+)/([^/]+)/([^/]+)/([^/]+) /modules/wfsection/$1.php?$2=$3&$4=$[L]
RewriteRule ^library/([^/]+)/([^/]+) /modules/wfsection/$1.php?$2=$[L]
RewriteRule ^library(.*)$ $[L]


Now this could get quite confusing for a user to try and manage, so what if the core was to be resposible for writing, and rewriting, the htaccess file everytime a module is installed or uninstalled? Then you could allow the module developers to write the rewrite functions needed ONLY for their modules? The would know exactly how many var/value pairs are used in their modules. If only 2 then they only need two.

Hmmm, this is getting very interesting. In fact the core could then take the default module rewrites (Provide in some module config file) and then alter them on the fly to include the custom DIR names set in the Xoops_version file by a user. This way the user could set custom folder names (my "/library/" part of the example) in the xoops_version file.

10
Draven
Re: Apache mod_rewrite RewriteRule Success
  • 2003/11/8 21:34

  • Draven

  • Module Developer

  • Posts: 337

  • Since: 2003/5/28


Just had an idea. What if we just matched everything up to the page name, then returned everything else after that as one var?

So if we had:

http://mysite.com/library/articles/artilceid/3/page/4/var/value

and matched it so the server just turned it to:

http://mysite.com/modules/wfsections/ ... ilceid/3/page/4/var/value

Then let a small php function break up the var/val from $var_val pairs after the server returns it? Something like

Quote:

$vararray = explode("/",$var_val);

// Our array would look like array("artilceid","3","page","4","var","value");

//then a for loop like this would release everything to the name sapce

for($i=0, $i != count($vararray);$i++){
$$vararray[$i] = $vararray[$i++];
}



even if $page had no value it would still assign $page as "". I think. My heads kind of spinning at the moment.

Login

Username:
Password:

Lost Password? Register now!

Who's Online

73 user(s) are online (48 user(s) are browsing Support Forums)


Members: 0


Guests: 73


more...

Donat-O-Meter

Stats
Goal: $100.00
Due Date: May 31
Gross Amount: $0.00
Net Balance: $0.00
Left to go: $100.00
Make donations with PayPal!

Latest GitHub Commits