Also available in these languages:
DeutschEnglishEspañolFrançaisעִבְרִיתBahasa Indonesia日本語polskiPortuguêsRomâniaРусскийsvenska简体中文

URL Management

Complete URL management for a Web application involves two aspects. First, when a user request comes in terms of a URL, the application needs to parse it into understandable parameters. Second, the application needs to provide a way of creating URLs so that the created URLs can be understood by the application. For a Yii application, these are accomplished with the help of CUrlManager.

Creating URLs

Although URLs can be hardcoded in controller views, it is often more flexible to create them dynamically:

$url=$this->createUrl($route,$params);

where $this refers to the controller instance; $route specifies the route of the request; and $params is a list of GET parameters to be appended to the URL.

By default, URLs created by createUrl is in the so-called get format. For example, given $route='post/read' and $params=array('id'=>100), we would obtain the following URL:

/index.php?r=post/read&id=100

where parameters appear in the query string as a list of Name=Value concatenated with the ampersand characters, and the r parameter specifies the request route. This URL format is not very user-friendly because it requires several non-word characters.

We could make the above URL look cleaner and more self-explanatory by using the so-called path format which eliminates the query string and puts the GET parameters into the path info part of URL:

/index.php/post/read/id/100

To change the URL format, we should configure the urlManager application component so that createUrl can automatically switch to the new format and the application can properly understand the new URLs:

array(
    ......
    'components'=>array(
        ......
        'urlManager'=>array(
            'urlFormat'=>'path',
        ),
    ),
);

Note that we do not need to specify the class of the urlManager component because it is pre-declared as CUrlManager in CWebApplication.

Tip: The URL generated by the createUrl method is a relative one. In order to get an absolute URL, we can prefix it with Yii::app()->request->hostInfo, or call createAbsoluteUrl.

User-friendly URLs

When path is used as the URL format, we can specify some URL rules to make our URLs even more user-friendly. For example, we can generate a URL as short as /post/100, instead of the lengthy /index.php/post/read/id/100. URL rules are used by CUrlManager for both URL creation and parsing purposes.

To specify URL rules, we need to configure the rules property of the urlManager application component:

array(
    ......
    'components'=>array(
        ......
        'urlManager'=>array(
            'urlFormat'=>'path',
            'rules'=>array(
                'pattern1'=>'route1',
                'pattern2'=>'route2',
                'pattern3'=>'route3',
            ),
        ),
    ),
);

The rules are specified as an array of pattern-route pairs, each corresponding to a single rule. The pattern of a rule is a string used to match the path info part of URLs. And the route of a rule should refer to a valid controller route.

Besides the above pattern-route format, a rule may also be specified with customized options, like the following:

'pattern1'=>array('route1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)

In the above, the array contains a list of customized options. As of version 1.1.0, the following options are available:

  • urlSuffix: the URL suffix used specifically for this rule. Defaults to null, meaning using the value of CUrlManager::urlSuffix.

  • caseSensitive: whether this rule is case sensitive. Defaults to null, meaning using the value of CUrlManager::caseSensitive.

  • defaultParams: the default GET parameters (name=>value) that this rule provides. When this rule is used to parse the incoming request, the values declared in this property will be injected into $_GET.

  • matchValue: whether the GET parameter values should match the corresponding sub-patterns in the rule when creating a URL. Defaults to null, meaning using the value of CUrlManager::matchValue. If this property is false, it means a rule will be used for creating a URL if its route and parameter names match the given ones. If this property is set true, then the given parameter values must also match the corresponding parameter sub-patterns. Note that setting this property to true will degrade performance.

Using Named Parameters

A rule can be associated with a few GET parameters. These GET parameters appear in the rule's pattern as special tokens in the following format:

<ParamName:ParamPattern>

where ParamName specifies the name of a GET parameter, and the optional ParamPattern specifies the regular expression that should be used to match the value of the GET parameter. In case when ParamPattern is omitted, it means the parameter should match any characters except the slash /. When creating a URL, these parameter tokens will be replaced with the corresponding parameter values; when parsing a URL, the corresponding GET parameters will be populated with the parsed results.

Let's use some examples to explain how URL rules work. We assume that our rule set consists of three rules:

array(
    'posts'=>'post/list',
    'post/<id:\d+>'=>'post/read',
    'post/<year:\d{4}>/<title>'=>'post/read',
)
  • Calling $this->createUrl('post/list') generates /index.php/posts. The first rule is applied.

  • Calling $this->createUrl('post/read',array('id'=>100)) generates /index.php/post/100. The second rule is applied.

  • Calling $this->createUrl('post/read',array('year'=>2008,'title'=>'a sample post')) generates /index.php/post/2008/a%20sample%20post. The third rule is applied.

  • Calling $this->createUrl('post/read') generates /index.php/post/read. None of the rules is applied.

In summary, when using createUrl to generate a URL, the route and the GET parameters passed to the method are used to decide which URL rule to be applied. If every parameter associated with a rule can be found in the GET parameters passed to createUrl, and if the route of the rule also matches the route parameter, the rule will be used to generate the URL.

If the GET parameters passed to createUrl are more than those required by a rule, the additional parameters will appear in the query string. For example, if we call $this->createUrl('post/read',array('id'=>100,'year'=>2008)), we would obtain /index.php/post/100?year=2008. In order to make these additional parameters appear in the path info part, we should append /* to the rule. Therefore, with the rule post/<id:\d+>/*, we can obtain the URL as /index.php/post/100/year/2008.

As we mentioned, the other purpose of URL rules is to parse the requesting URLs. Naturally, this is an inverse process of URL creation. For example, when a user requests for /index.php/post/100, the second rule in the above example will apply, which resolves in the route post/read and the GET parameter array('id'=>100) (accessible via $_GET).

Note: Using URL rules will degrade application performance. This is because when parsing the request URL, CUrlManager will attempt to match it with each rule until one can be applied. The more the number of rules, the more the performance impact. Therefore, a high-traffic Web application should minimize its use of URL rules.

Parameterizing Routes

Starting from version 1.0.5, we may reference named parameters in the route part of a rule. This allows a rule to be applied to multiple routes based on matching criteria. It may also help reduce the number of rules needed for an application, and thus improve the overall performance.

We use the following example rules to illustrate how to parameterize routes with named parameters:

array(
    '<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>' => '<_c>/<_a>',
    '<_c:(post|comment)>/<id:\d+>' => '<_c>/read',
    '<_c:(post|comment)>s' => '<_c>/list',
)

In the above, we use two named parameters in the route part of the rules: _c and _a. The former matches a controller ID to be either post or comment, while the latter matches an action ID to be create, update or delete. You may name the parameters differently as long as they do not conflict with GET parameters that may appear in URLs.

Using the aboving rules, the URL /index.php/post/123/create would be parsed as the route post/create with GET parameter id=123. And given the route comment/list and GET parameter page=2, we can create a URL /index.php/comments?page=2.

Parameterizing Hostnames

Starting from version 1.0.11, it is also possible to include hostname into the rules for parsing and creating URLs. One may extract part of the hostname to be a GET parameter. For example, the URL http://admin.example.com/en/profile may be parsed into GET parameters user=admin and lang=en. On the other hand, rules with hostname may also be used to create URLs with paratermized hostnames.

In order to use parameterized hostnames, simply declare URL rules with host info, e.g.:

array(
    'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile',
)

The above example says that the first segment in the hostname should be treated as user parameter while the first segment in the path info should be lang parameter. The rule corresponds to the user/profile route.

Note that CUrlManager::showScriptName will not take effect when a URL is being created using a rule with parameterized hostname.

Also note that the rule with parameterized hostname should NOT contain the sub-folder if the application is under a sub-folder of the Web root. For example, if the application is under http://www.example.com/sandbox/blog, then we should still use the same URL rule as described above without the sub-folder sandbox/blog.

Hiding index.php

There is one more thing that we can do to further clean our URLs, i.e., hiding the entry script index.php in the URL. This requires us to configure the Web server as well as the urlManager application component.

We first need to configure the Web server so that a URL without the entry script can still be handled by the entry script. For Apache HTTP server, this can be done by turning on the URL rewriting engine and specifying some rewriting rules. We can create the file /wwwroot/blog/.htaccess with the following content. Note that the same content can also be put in the Apache configuration file within the Directory element for /wwwroot/blog.

Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# otherwise forward it to index.php
RewriteRule . index.php

We then configure the showScriptName property of the urlManager component to be false.

Now if we call $this->createUrl('post/read',array('id'=>100)), we would obtain the URL /post/100. More importantly, this URL can be properly recognized by our Web application.

Faking URL Suffix

We may also add some suffix to our URLs. For example, we can have /post/100.html instead of /post/100. This makes it look more like a URL to a static Web page. To do so, simply configure the urlManager component by setting its urlSuffix property to the suffix you like.

$Id: topics.url.txt 2229 2010-06-25 19:57:54Z poppitypop@gmail.com $
If you find any typos or errors in the tutorial, please create a Yii ticket to report it. If it is a translation error, please create a Yiidoc ticket, instead. Thank you.

Total 24 comments:

#89
URL rules overhead
by jmolins at 2:05am on February 21, 2009.

Is this overhead a Yii thing or it happens in any framework that uses user-friendly urls?

#106
re: URL rules overhead
by mindeh at 3:59pm on March 8, 2009.

yup, any user-friendly URLs has to be parsed and this causes an overhead.

The "natural" way of making URLs is standard GET format and anything other than that (except POST of course) needs to parsed to be translated to application-understandable variables.

One can use Apache mod_rewrite, that would probably be faster but you'd lose convenience and flexibility of $this->createUrl().

On the other hand I think one ought to care about this overhead when site in question receives really high traffic.

Moreover, performance bottleneck would probably be else where anyway (say DB querying).

#117
Doesn't work inside httpd.conf
by xx666xx at 3:39pm on March 15, 2009.

Just so everyone knows, this particular piece of rewrite code won't work inside a block within your config file. It seems to require being in .htaccess. I'm not an expert in rewrites, just something I found out.

#305
hard-coding
by phuongle at 5:46pm on May 21, 2009.

Actually, you can eliminate overhead by hard-coding links in pages. I mean you don't use createUrl any more. So you don't need urlManager any more.

#309
.htaccess issue
by Bethrezen at 5:54am on May 22, 2009.

.htacccess sample file can cause errors on some apache systems. It's better to replace rewriterule with this:

RewriteRule ^.*$ /index.php [L]


#315
Hiding index.php in IIS7
by kennytean at 5:17am on May 25, 2009.

As i know, .htaccess is not applicable for IIS7. Are there any other way to hide the index.php?

#316
RE: Hiding index.php in IIS7
by kennytean at 5:30am on May 25, 2009.

I'd solved the issue.. If u got anything regarding this, msg me..

Cheers.

#386
zend example router
by Ismael at 6:27pm on June 15, 2009.

I think zend framework give a great default .htaccess configuration.

Take a look. I think it's better than showed in Yii guide.

http://framework.zend.com/manual/en/zend.controller.router.html

#452
Nothing I do gets rid of index.php
by jwallz at 7:05pm on July 7, 2009.

The whole point of a framework is to simplify. Why is index.php enabled by default?? This doesn't makes sense. Clean urls should be enabled by default with pattern matching to controller/action/param. I hate how we have to putz around with rewrite rules and htaccess files to turn this off. Nothing i seem to be doing with my .htaccess is getting rid of index.php. VERY frustrating.

#453
Nothing I do gets rid of index.php (Follow Up)
by jwallz at 7:26pm on July 7, 2009.

for others not familiar with Apache: In addition to making all of the changes listed on this page to enable clean urls, you also have to enable url rewriting in Apache by editing Apache's httpd.conf file. Instructions here => http://www.tildemark.com/software/servers/enable-htaccess-on-apache.html

#564
Get the Yii web application to run from WebRoot instead of WebRoot/webapp/
by ashishkulkarni at 8:46am on August 10, 2009.

Hi there,

How can I get a Yii Web App to run from say www.abc.com instead of www.abc.com/abc ?

Best Regards,

Ashish.

#656
eliminating overhead
by mindplay at 9:22pm on September 13, 2009.

Note that someone mentioned above that "you can eliminate overhead by hard-coding links in pages" - please note that, while it is true that this eliminates SOME of the overhead, routing incoming requests still causes considerable overhead, depending on the number of routes.

If you want this feature, technically, there is no way to eliminate the overhead of parsing and routing URLs.

Also note that the overhead of generating URLs with createUrl(), on the other hand, can be largely eliminated by implementing a good caching strategy.

#716
hiding index.php.
by webscriptz at 10:58pm on October 9, 2009.

It doesn't work, i've been looking around for a solution but i really need index.php or he wont guide me to my pages even when i have disabled the showScriptName property

Options +FollowSymLinks IndexIgnore / RewriteEngine on

if a directory or a file exists, use it directly

RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d

otherwise forward it to index.php

RewriteRule ^.*$ /index.php L

my apache2 runs on ubuntu linux and allowes symlinks etc

#734
getting it to work
by lgelfan at 10:29pm on October 14, 2009.

If you can't get this to work, I would suggest first make sure you can get mod_rewrite to work on it's own. A couple good resources for that are:

http://www.addedbytes.com/apache/url-rewriting-for-beginners/

http://httpd.apache.org/docs/2.0/misc/rewriteguide.html

and the Zend page mentioned above.

A few notes on the configuration:

  • don't use a leading slash in the rewrite rule if the directory you are working on is NOT your webroot. Either leave it off or use the full path to your webroot. Leaving it off will work in most cases:

RewriteRule ^.*$ index.php [L]

The rest of the config is the same as in the guide or you can also use the Zend one: http://framework.zend.com/manual/en/zend.controller.router.html (see the block marked "preferred")

Putting rewrite rules in your Apache config should work fine and is preferable to using .htaccess files when you can (for performance and security). Just put the rule in a Directory block -- NOTE that you may also need to define the RewriteBase path. (And remember to restart/reload Apache.)

The minimum Yii urlManager block you need in your config file (main.php) is:

'urlManager'=>array( 'urlFormat'=>'path', 'showScriptName'=>false, ),

Take note the showScriptName value is a literal not a string. Otherwise should work fine in most cases.

#787
Removing index.php and rewrite problems
by ptoly at 11:44am on November 4, 2009.

I managed to remove the index.php file by setting the root directory as the one with index.php and following above instructions. However when I try more sophistocated rewrite rules everything goes haywire.

For instance I'm trying to set up a standard redirect to the .php file:

RewriteRule ^index.html$ index.php r=301,nc

This gives me a page not found within the application, even though the rewrite log shows it is correctly sending stuff to index.php.

For some reason the index.php is creating an INTERNAL REDIRECT.

Anyone have any ideas how or why?

#893
Default site controller still points to index.php
by ore at 12:00am on December 11, 2009.

While i got this to work alright, i still have the default action pointing to index.php! E.g when I log in successfully(demo/demo) to the generic app that yiic creates the system redirects me to index.php and not index.html Why?

#1099
idea or not
by webscriptz at 2:35pm on February 12, 2010.

it's not that i'm again the $_GET methode but wouldn't it be wiser to have a function which does some checking before returning the param with $_GET?

like faulty characters etc?

#1129
Hiding index.php (this isn't clear in the documentation)
by intel352 at 2:35pm on February 18, 2010.

To hide index.php, you have to specify another parameter in your main configuration. This is mentioned in the docs above, but isn't presented with an example so can be easily glossed over.

To get your URLs using the path method (instead of GET method), and also remove index.php, you need to do the following:

  1. Add a .htaccess file to your document root, with the following contents:

    Options +FollowSymLinks IndexIgnore / RewriteEngine on

    if a directory or a file exists, use it directly

    RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d

    otherwise forward it to index.php

    RewriteRule . index.php

  2. Edit protected/config/main.php, to add the following inside your components array:

    // application components 'components'=>array( // ... 'urlManager'=>array( 'urlFormat'=>'path', 'showScriptName'=>false, ), // ... ),

And voila, visiting the default site's demo page will result in a url such as: example.com/site/page/view/about

#1130
Stupid markdown...
by intel352 at 2:36pm on February 18, 2010.

Bah, Markdown doesn't love me.

If anyone wants a properly formatted example, message me in the forum.

#1309
thanks to lgelfan comment #734
by pigpen at 3:45pm on March 22, 2010.

I had my Yii app in a subdirectory so your comment (#734) helped me out. I had to use your rewrite rule in my .htaccess file to get it working.

I used this:

RewriteRule ^.*$ index.php L

Thanks lgelfan.

#1694
ubuntu
by kitt at 7:44pm on July 16, 2010.

Don't forget replace "AllowOverride None" to "AllowOverride All" in virtual host conf

#1730
Not "AllowOverride All"
by xx666xx at 7:50pm on July 26, 2010.

That's too much. "AllowOverride FileInfo +Indexes" is all that is needed for this.

#1738
How do I write if my param has the '/'?
by zhangdi at 7:45am on July 28, 2010.

How do I write if my param has the '/'?

Like

<?php $this->createUrl('site/logout',array('forward')=>'/myspace')?>
#1781
Path format and relative URLs in the data.
by thinkingspace at 8:01pm on August 11, 2010.

Having 'urlFormat'=>'path' corrupts relative URLs in your data. For example, if in the blog example you put an image in your post with a source as follows:

src="./images/littlestone.jpg"

on a page with post in the URL, when the image URL should be:

http://localhost/blog/images/littlestone.jpg

the images URL becomes:

http://localhost/blog/index.php/post/images/littlestone.jpg

Absolute URLs are not affected. I can find no way to fix this.

Your Comment:

You may enter comment using Markdown syntax.

Please login with your forum account.
Note: you must have at least ONE forum post with your account.