Making Codeigniter clean URLs cleaner
October 7, 2008
Usually when working with codeigniter you come across the use case where you need to remove the extra “index” appearing in the URL for purposes ranging from shorter URLs to SEO.
For example,
Current URL style:
http://example.com/controller/method/category1/category2/page-name
Target URL style:
http://example.com/category1/category2/page-name.html
As you can see, the difference in the target URL is the absence of the controller name and the method name and the presence of the extension “.html”. Now, lets see how to achieve this and why the extra extension is required.
Below is my version of .htaccess.
RewriteEngine on
RewriteRule ^([a-z0-9_-]+)\.html$ index.php/page/$1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond $1 !^(index\.php|asset|robots\.txt)
RewriteRule ^(.*)$ index.php/$1 [L]
The line that is extra is 3 which redirects all the requests for “.html”; files to the controller named “page”.
Now, lets look at the page controller which will handle these requests.
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class Page extends Controller
{
function __construct()
{
parent::Controller();
}
function _remap()
{
$segment_count = $this->uri->total_segments();
$segments = $this->uri->segment_array();
if($segment_count == 0)
{
$this->data['title'] = 'Page Title';
$this->load->view('home', $this->data);
}
elseif($segment_count == 2) // Single page
{
$page_url = $segments[2];
// Handle the page request
}
elseif($segment_count == 3) // Page with category
{
$category = $segments[2];
$page_url = $segments[3];
// Handle the category + page request
}
}
}
In this way you can have pages with smaller and cleaner URLs.
Tips:
- You can use any extension you like. e.g; in case you have static HTML files to server, you can use the extension “.html” for static and “.htm” for non-static files.
- You can have any number of URL stubs before the “.html” extension and you can access all these stubs in the controller.
Cons:
- The URL needs an extra extension.
- Only one controller per extension.
Do let me know what you think about this or if there is any way to improve on this.
Old comments
Ruben Müller
Why don’t you use the routes.php?
http://codeigniter.com/user_guide/general/routing.html
Bob
I’m wondering why you would want the extension (.html, .htm, .php, whatever). If you’re looking for shorter, cleaner URLs, why not:
Current: http://example.com/controller/method/category1/category2/page-name
Target : http://example.com/controller/method/category1/category2
or to really chop it up:
Target : http://example.com/category1/category2
I suppose it makes a difference if your controller/method names are married to your site structure (i.e., http://example.com/products/view/cat1/cat2) or not (http://example.com/whichpage/displayproducts/cat1/cat2) but at least to the way I code, I’m not seeing the benefits of this method.
Cheers, Bob
Sukumar Yethadka
@Ruben Using routes is an option. But in order to get URLs like
http://example.com/category1/category2/page-name
without the controller name and the method name (“index” for example); the route would be (Please note that I haven’t tested this below route)
$route['(:any)'] = 'page/$1';
Then you would be able to use any other controller since all the requests are routed to “page” controller. Do let me know if there is a better routing method I missed.
@Bob The main reasons I didn’t want
Target : http://example.com/controller/method/category1/category2
is because my URLs would have looked like
http://example.com/page/index/category1/category2
AFAIK, google considers the URL segments to be more important the closer they are to the domain name.
Yes, Target : http://example.com/category1/category2
is quite possible and using routes at that. However, I think we wouldn’ t be able to use other controllers because of this (Unless we have some filter that checks for the existence of the controller and then the category). Using extensions helps to map a particular extension to a specific controller whithout affecting any other controllers.
One of the main use cases I can think of is for a small blog similar to wordpress.
Hope that makes sense.
Henry Weismann
How about:
Current Url: http://example.com/controller-method/category1/category2
OR
Current Url: http://example.com/subfolder-controller-method/category1/category2
is because my URLs would have looked like
Target Url: http://example.com/index.php/controller/method/category1/category2
OR
Target Url: http://example.com/index.php/controller/method/index/category1/category2
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond $1 !^(index\.php|asset|robots\.txt)
RewriteRule ^([^/.]+)-([^/.]+)-([^/.]+)/(.*)$ index.php/$1/$2/$3/$4 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond $1 !^(index\.php|asset|robots\.txt)
RewriteRule ^([^/.]+)-([^/.]+)/(.*)$ index.php/$1/$2/$3 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond $1 !^(index\.php|asset|robots\.txt)
RewriteRule ^(.*)$ index.php/$1 [L]
Using Routes:
$route['^([^/.]+)-([^/.]+)/(.*)'] = "$1/$2/$3";
$route['^([^/.]+)-([^/.]+)-([^/.]+)/(.*)'] = "$1/$2/$3/$4";
I have no idea if that will work as I just wrote it now. Feel free to test it.
Henry Weismann
Should read: Target Url: http://example.com/index.php/controller/method/category1/category2
OR
Target Url: http://example.com/index.php/subfolder/controller/method/index/category1/category2
Sukumar Yethadka
@Henry, Your code looks good, but my aim is to remove the controller and the method name from the URL. Target URL style: http://example.com/category1/category2/page-name Your method is useful to merge the forward slashes in the URL.
Useful code nevertheless, thanks!
Sean
One problem I notice about CI Urls is how they look inside of google on your website snippet. They all include a ../ inside the URL have you noticed this and do you have any idea how to remove that ../ it wastes space and is distracting. Thanks, Sean
Sukumar Yethadka
@Sean, I didn’t really get what you are referring to. You can check the usage of this code snippet on http://www.fourseasonsvineyards.com/ As you can see there are no “../” in the URLs anywhere. Do let me know if I am missing something.