Apache friendly urls - apache

I've got a small CMS system written in PHP and running on Apache. The format of the URLs this CMS system uses/generates is:
/display.php?PageID=xxx where xxx is just some integer number. As you can see, those URLs are not very friendly, neither for users nor search engines.
I believe that using mod_rewrite (or something like that) and .htaccess files I should be able to configure Apache for URL-rewriting. I have searched for information about this before but I did not find any easy method to do this, it always involved messing with regular expressions, which I'm not very familiar with.
Since the website in question is really simple and small, just 5-10 different pages, I would really like to be able to just hard-code the configuration, without any special rules or regexps.
I'd just like to map a friendly URL to an actual URL, perhaps like this:
/about = /display.php?PageID=44
/products = /display.php?PageID=34
etc.
Is it possible to configure the mod_rewrite plugin in a basic way like this?
Could someone explain the easiest method to do this? Explain it to me as if I was a child :-)
Thanks in advance!

well putting something like
RewriteEngine on
RewriteRule ^about$ ./display.php?PageID=44
RewriteRule ^products$ ./display.php?PageID=34
in your .htaccess-file shouldn't be the big deal I think...

URL Rewriting for Beginners is my favorite intro article to this, it should cover what you're looking for. In fact, the first actual example where you write a .htaccess file is almost identical to what you want.

Another way is filter by a dynamic php file with a mapping for pages or a routing strategy like frameworks like drupal code igniter ....
and your URL will be like
my-pages/about.html -> display.php?PageID=44
my-pages/products.html -> display.php?PageID=34
and so on
Here a suggestion for .htaccess file and the filter the action with this strategy
--- .htaccess file ----
*RewriteEngine on
RewriteRule ^my-pages/(.).html$ MY-URL.php [QSA,L,E]
---------------- MY-URL.php ---------
<?php
$PREFIX = 'my-pages/'; //--- not used
$mapping=array(
'about' => 44,
'products' => 34
);
$pathinfo= pathinfo( $_SERVER['REQUEST_URI'] );
/* $pathinfo['dirname'] -> my-pages
$pathinfo['basename'] -> ???.html
$pathinfo['extension']-> .html
*/
$page = substr( $pathinfo['basename'] ,0,-5);
if( isset( $mapping[$page] ){
// ---- redirect or include
YUOR CODE HERE
}
else {
//--- error 404
YUOR CODE HERE
}
?>

Related

Make optional parameter with .htaccess

I have looked for the answer to my problem here, but the solution is always provided without explaining how to do it, that's the reason I can not do it properly.
I have this code:
RewriteRule ^dex/([^_]*)/([^_]*)/([^_]*)/([^_]*)/([^_]*)$ /dex.php?one=$1&two=$2&three=$3&four=$4&five=$5 [L]
This htaccess as it is makes it mandatory that all parameters are given. This URL works:
http://example.com/dex/one/two/three/four/five
I also want to make it work like this:
http://example.com/dex/one/two/three/four
Making the last (or some) parameters optional. I read something about QSA|qsappend here: http://httpd.apache.org/docs/current/en/rewrite/flags.html#flag_qsa but I can't understand it completely.
Any help? Thank you
To anyone having the same issue, this is the code used to fix it:
RewriteRule ^dex/([^/]*)/([^/]*)/([^/]*)/([^/]*)/?([^/]*)?$ /dex.php?one=$1&two=$2&three=$3&four=$4&five=$5 [L]
Changed ([^_]*) to ([^/]*) and added ? after what I wanted to make optional.
In this case: /? is making a end slash optional, and ([^/]*)? is making the last parameter optional. So it works when the URL is like this:
http://example.com/dex/one/two/three/four/
http://example.com/dex/one/two/three/four
http://example.com/dex/one/two/three/four/five
http://example.com/dex/one/two/three/four/five/
Hope this helps someone.
Rewrite rules use regular expressions. These regular expressions are always tiresome and difficult to maintain. The following, for example, is an answer to question about regex parsers on HTML, and the difficulty you may find with them:
RegEx match open tags except XHTML self-contained tags
Your htaccess file is an Apache configuration file. It should be used for simple configuration, and not for programming. Have it point to the code, and then let the code do the rest. For example, your .htaccess file:
RewriteRule ^(.*)$ index.php [QSA,NC,L]
And, if you're using PHP with index.php...
$objects = explode('/', ltrim($_SERVER[REDIRECT_URL], '/'));
print_r($objects); // list of items from URL
You may end up wanting to grab a different SERVER parameter, but you'll want to keep this complicated stuff in the code, not in configuration files.

Apache mod_rewrite path name as query parameters?

I want to use Apache's mod_rewrite in order to be able to take each folder of a path as a particular query parameter, for example consider the following:
Basic example
Url requested: http://domain.com/shoes/prada/image-1/
Page served: http://domain.com/?cid=shoes&bid=prada&pid=image-1
In this scenario, there are 3 sub-folders requested (/shoes/, /prada/ then image-1), so the first sub-folder is passed in the actual page served as cid, the second as bid and the third as pid.
Full example
However, I would also like it to serve a particular page depending on the number of sub-folders requested, e.g.
Url requested: http://domain.com/shoes/prada/
Page served: http://domain.com/shop.php?cid=shoes&bid=prada
So far all I've managed to find is regex based matching for mod_rewrite but my path's will vary a lot, which is why I would like to have conditions based on the number of folders accessed (please note, I'm not that good with regex - I reckon a wildcard character would help with this, but I wouldn't be sure where to start).
Any help on this would be greatly appreciated! This is pretty long winded, so if you need any more info for clarifying, please let me know!
With a little bit of work I was able to tweak some regex and get a working rule set for what I wanted:
RewriteEngine on
RewriteRule ^(.*)/(.*)/(.*)/(.)?$ product.php?tid=$1&sid=$2&eid=$3 [L]
RewriteRule ^(.*)/(.*)/(.)?$ brand.php?tid=$1&sid=$2 [L]
RewriteRule ^(.*)/(.)?$ shop.php?tid=$1 [L]
This is a bit different to the example, however it's what I intended for in the first place.
This allows for the rewriting of url's up to four folders deep, with the "name" of each folder being given as a parameter, and each additional level of depth rewriting the url to a separate resource for example:
http://x.com/shoes/prada/2011-high-heels/ -> http://x.com/product.php?tid=shoes&sid=prada&eid=2011-high-heels
Tested on http://martinmelin.se/rewrite-rule-tester/

Abusing Mod Rewrite for a clean file system, and pretty URLs

I have a few php files that do a few different jobs. I'd like to change the way my clients access these php files to make it more clean for the end user. The Mod_Rewrite system has shown that it can do some pretty powerful things when in the hands of the right server admin. So I was wondering how far can you abuse the Mod Rewrite rules for a cleaner file system, and pretty URLs. Considering that the PHP files themselves use query strings to get their data, I'd like to alias the way the query string is built based upon how the how deep into the fake files system we go.
Our website's URL is http://www.domain.tld/, but we shall call it domain.tld for short. I'd like to map a few different address to a few different query strings on a few different files. But I'd also like to to be expandable on a whim.
Or first set would be, anything going past domain.tld/series/ should be directed to the domain.tld/series.php script with any (fake) directory past series to become part of the query-string for series.php. The same should happen to anything directed in the direction of domain.tld/users/ that should be redirected to the domain.tld/users.php file.
So if we had a URLs like, domain.tld/series/Master/2010/ or domain.tld/series/Novice/Season 01/ they would still be redirected to the domain.tld/series.php script, but with the query-string of ?0=Master&1=2010 and ?0=Novice&1=Season 01. But should I want to get an overview of the Master series, I could go the the URL domain.tld/series/Master/ and produce the query-string of just ?0=Master. The idea being that the rewrite rule should allow for infinite expandability.
This is how I'm doing it, and it sure works infinitely:
RewriteRule ^((/?[^/]+)+)/?$ ?q=$1 [L]
The trick is that the whole path is passed on as a single parameter, q, to index.php. So for example domain.tld/series/Novice/Season 01/ becomes domain.tld/?q=series/Novice/Season 01. Then you can do:
<?php
$params = explode('/', $_GET['q']);
var_dump($params);
?>
to get the individual parts.
array(3) { 0 => 'series', 1 => 'Novice', 2 => 'Season 01' }
It is not possible to be completely dynamic in such a system and have, as you say 'infinite expandability. You would have to define a RewriteRule for every 'tier' you will allow in your URL, or alternatively match everything after the first 'tier' as a single variable and do the work with PHP.
Example 1
RewriteRule ^([^/]+)/?$ /$1.php
RewriteRule ^([^/]+)/([^/]+)/?$ /$1.php?0=$2
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/?$ /$1.php?0=$2&1=$3
Example 2
RewriteRule ^([^/]+)/(.*)/? /$1.php?qs=$2
Obviously these are only very simple examples and you'd probably have to use RewriteConds etc. to exempt certain files etc.

Using mod_rewrite to redirect 70+ URLs with variables

I am constructing a webcomic site, but the chapter order has changed significantly. I have manually written the conversions for myself in a form like this:
36 -> 26.1
37 -> 28
38 -> 28.1
39 -> 29
40 -> 30
41 -> 30.1
Basically, following this guide, I want to convert all urls like http://www.domain.com/view.php?chapter=38 to the newer kind like http://www.domain.com/c28.1.
I also want to ensure that those requesting a page number, like view.php?chapter=38&page=4 have their page numbers passed on to the redirect, like so: c28.1/p4.html.
It would seem pretty simple, except that I have 70 or so of these to implement, and am a little worried if this significantly hurts site performance (is 70 a lot..?) and can't get the comic and page variables to properly rewrite.
So my question is, how do I achieve this, what is my best solution? If performance is no problem, I would prefer to keep it in the .htaccess with my other mod_rewrite code, but if the only reasonable way to do it is write a .php script and call on it to do the redirect, I can do that - though I don't really know how to get PHP to do that.
Please keep in mind that the 70 or so mentioned are the only ones I will ever have, so I don't care to make an easily-accessible database. I just want the redirects to work with minimal fuss.
I will be so grateful for any response. Thanks in advance if anyone can help me figure this one out.
I think a lighter way (in terms of rows inside .htaccess file) to approach is, like you have mentioned, to use .htaccess to handle urls like:
RewriteRule ^c([0-9]+)$ view.php?chapter=$1
RewriteRule ^c([0-9]+).([0-9]+)$ view.php?chapter=$1&section=$2
RewriteRule ^c([0-9]+).([0-9]+)/p([0-9]+).html$ view.php?chapter=$1&section=$2&page=$3
And then map them to a php script that will handle the single contents.
I can suggest you that you should have also to map by rewriterule with a 301 redirect all the old urls in order to allow people that are coming from a search engine serp to be redirected to the right new url of the content.
Hope this helps.
I ended up consulting with the wonderful people of freenode #httpd and got a solution. For me, as I only want to catch requests for view.php, I found that it worked to simply write a view.php script that gathered things from an array. No mod_rewrite necessary. The script I am now using looks like:
<?
$chapters = array(
// old => new
1 => 2,
2 => 3,
3 => 4,
10 => 21
);
if (! isset($_GET['chapter']) || ! isset($chapters[$chapter = (int)$_GET['chapter']])) {
header('HTTP/1.1 404 Not Found');
echo 'Page Not Found';
exit;
}
$url = '/c' . $chapters[$chapter];
if (isset($_GET['page']))
$url .= '/p' . $_GET['page'] . '.html';
header('Location: ' . $url, 301);
?>
Thanks for your help, though, gh3.

mod_rewrite ecommerce URL design

I have problems with how I should structure my product listing pages, products pages, webpages.
It roughly translate into this:
/bags/nicebag.html = /product.php?product=nicebag&category=bags
/nicebag.html = /product.php?product=nice_bag
/bags = productlisting.php?&category=bags
Problem is that webpages will share same URL structure as no.2 in the list
/contact.html = page.php?page=contact
The reason why it is not listed in .htaccess separatly is that webpages can have different names. And even the same page can be in multiple languages.
The reason of no. 1 and 2 is not combined, is that sometimes I just want to reference only to the product since it can be in multiple categories.
What kind of structure do you suggest?
.htaccess
# Mod rewrite enabled.
Options +FollowSymLinks
RewriteEngine on
# ---- Rules ----
# product.php (Search for category & product name)
RewriteRule ^([A-Za-z0-9-_]+)/([A-Za-z0-9-_]+)\.html?$ product.php?prod_id=$2&cid=$1 [NC,L]
# productlisting.php (Search for category)
RewriteRule ^([A-Za-z0-9-_]+)?$ productlisting.php?&cid=$1 [NC,L]
I would use the path prefix /products/ to identify the products related URLs. So:
/products/bags/nicebag.html → /product.php?product=nicebag&category=bags
/products/nicebag.html → /product.php?product=nice_bag
/products/bags → /productlisting.php?&category=bags
With such a structure you could also rewrite /products/ to /productlisting.php that then shows all products.
# product listing
RewriteRule ^products/$ productlisting.php [L]
RewriteRule ^products/([A-Za-z0-9-_]+])$ productlisting.php?category=$1 [L]
# product details
RewriteRule ^products/([A-Za-z0-9-_]+)\.html$ product.php?prod_id=$1 [L]
RewriteRule ^products/([A-Za-z0-9-_]+)/([A-Za-z0-9-_]+)\.html$ product.php?prod_id=$2&cid=$1 [L]
# other pages
RewriteRule ^([A-Za-z0-9-_]+)\.html$ page.php?page=$1 [L]
use a different suffix for different types, e.g html for products and htm for pages or something like that
/bags/nicebag.html = /product.php?product=nicebag&category=bags
/nicebag.html = /product.php?product=nice_bag
/bags = productlisting.php?&category=bags
/contact.htm = page.php?page=contact
or
/contact/page.html = page.php?page=contact
As it will be messy and cumbersome to maintain your rewriting rules in the .htaccess file, I would only put one rule in there, rewriting to something like:
/dispatch.php?request=[request]
e.g.
RewriteRule ^(.*)$ dispatch.php?request=$1 [L,QSA]
In dispatch.php, you dissect the request into its elements (path, querystring, anchor, ...) and decide where to go from there. That way, you can use code for the decision making, which will give you a lot more flexibility than just maintaining a huge list of custom rewrite mappings.
For example, you can identify product and category elements in the path by querying against your database and base the dispatch logic on the results in a more generic way.
[Pseudocode]
if (isProduct($lastPathElement)) {
// Maybe verify that leading path elements are categories ...
// Other preparations/verifications ...
// refer execution to product.php
}
elseif (isCategory($lastPathElement)) {
// Other preparations/verifications ...
// refer execution to productlisting.php
}
// ... (Checks for other specific stuff)
else {
// Static page or 404
// refer execution to page.php
}
I was facing the very same issue a few weeks ago.
Ended up defining a different structure for the "static" pages.
www.examples.com/contact/
or
www.examples.com/info/contact.html
So it can be distinguished from the "dynamic" pages.
There's pretty much no way to distinguish between www.examples.com/nicebag.html and www.examples.com/contact.html without putting non-product webpage names in .htaccess or doing some preliminary processing in the receiving php script.
As I see, the options are:
rewrite all requests to page.php and for those that don't match any of the non-product pages, include the product script
write the non-product page names to .htaccess dynamically (messy and bug-prone)
rethink the URL structure for non-product pages. Perhaps just as little as www.example.com/page/contact.html might help
I'd go for the third one, anyway.
I would recommend flat structure:
domain.com/bags
domain.com/contact
domain.com/nice-bag