You are viewing revision #5 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.
In this article, I will describe how to manage URLs for a Website that has both secure and nonsecure content.
Secure content are sent via the https
protocol using SSL (secure socket layer), while nonsecure content via the normal http
protocol. For simplicity of description, let's call the former https
content/page while the latter http
content/page. A serious Website often needs to serve some pages in https
and some in http
. For example, to prevent password sniffing, we would like to serve the login page using https
; and to save server processing power, we would like to serve insensitive content (e.g. home page) using http
.
A requirement is that when we are in an https
page we want to generate URLs to http
pages, and vice versa. For example, a Web site has a main menu shared by all pages, and the main menu contains links to both http
page (e.g. About page) and https
page (e.g. Login page). If we are now in an http
page, we can use relative URLs (e.g. /about) to link to other http
pages, but we have to use absolute URLs with https
protocol to link to the https
pages. And if we are in an https
page, the situation will be the other way around.
Another requirement is that if a secure page is requested via an http
request, we should redirect the browser to use https
protocol; and vice versa. The redirection usually should be 301 permanent redirection. It is possible to achieve this goal via the rewrite rules of Web servers. But the rewrite rules could become very complex if we want fine-grained control of secure and nonsecure content.
To achieve the above two requirements, we can extend [CUrlManager] as follows,
class UrlManager extends CUrlManager
{
/**
* @var string the host info used in non-SSL mode
*/
public $hostInfo = 'http://localhost';
/**
* @var string the host info used in SSL mode
*/
public $secureHostInfo = 'https://localhost';
/**
* @var array list of routes that should work only in SSL mode.
* Each array element can be either a URL route (e.g. 'site/create')
* or a controller ID (e.g. 'settings'). The latter means all actions
* of that controller should be secured.
*/
public $secureRoutes = array();
public function createUrl($route, $params = array(), $ampersand = '&')
{
$url = parent::createUrl($route, $params, $ampersand);
// If already an absolute URL, return it directly
if (strpos($url, 'http') === 0) {
return $url;
}
// Check if the current protocol matches the expected protocol of the route
// If not, prefix the generated URL with the correct host info.
$secureRoute = $this->isSecureRoute($route);
if (Yii::app()->request->isSecureConnection) {
return $secureRoute ? $url : $this->hostInfo . $url;
} else {
return $secureRoute ? $this->secureHostInfo . $url : $url;
}
}
public function parseUrl($request)
{
$route = parent::parseUrl($request);
// Perform a 301 redirection if the current protocol
// does not match the expected protocol
$secureRoute = $this->isSecureRoute($route);
$sslRequest = $request->isSecureConnection;
if ($secureRoute !== $sslRequest) {
$hostInfo = $secureRoute ? $this->secureHostInfo : $this->hostInfo;
if ((strpos($hostInfo, 'https') === 0) xor $sslRequest) {
$request->redirect($hostInfo . $request->url, true, 301);
}
}
return $route;
}
private $_secureMap;
/**
* @param string the URL route to be checked
* @return boolean if the give route should be serviced in SSL mode
*/
protected function isSecureRoute($route)
{
if ($this->_secureMap === null) {
foreach ($this->secureRoutes as $r) {
$this->_secureMap[strtolower($r)] = true;
}
}
$route = strtolower($route);
if (isset($this->_secureMap[$route])) {
return true;
} else {
return ($pos = strpos($route, '/')) !== false
&& isset($this->_secureMap[substr($route, 0, $pos)]);
}
}
}
Now in the application configuration, we specify UrlManager
as our URL manager instead of the default [CUrlManager]:
return array(
// ....
'components' => array(
'urlManager' => array(
'class' => 'UrlManager',
'urlFormat' => 'path',
'hostInfo' => 'http://example.com',
'secureHostInfo' => 'https://example.com',
'secureRoutes' => array(
'site/login', // site/login action
'site/signup', // site/signup action
'settings', // all actions of SettingsController
),
),
),
);
In the above code, we configure UrlManager
to secure the login, sign-up and settings pages. If you want to secure other pages, just add the corresponding routes to the secureRoutes
array shown above.
We can now use Yii::app()->createUrl()
as usual in places where we need to create URLs. Our UrlManager
will automatically determine if the generated URLs need to be prefixed with proper host info. The UrlManager
will also do the 301 redirection work if needed.
Nice article
Thanks for this article. This is what I was wondering how to do it in my project am working on.
Nice one
Thanks Qiang... very good article.
How to force all routes to https?
Thanks for the article. This is an essential wiki.
My customer wanted me to serve all pages via https protocol.
So using this wiki I added all of the sites controllers in
'secureRoutes'
array. (site route too)
Is this solution effective for making all routes secure?
Thanks.
Nice article
Thanks a lot
awesome trick
Thanks so much Qiang
Saving me tons of trouble working with Apache ReWrite.
Thanks
Many thanks Qiang ... perfect and simple solution
Spot On
Thanks vey much. This was exactly what I wanted. Worked like a charm. Cheers.
SSL needs
Hi,
I am new in yii . I don't know how to use the UrlManager class. Kindly explain brifly
hostInfo
can i set hostInfo and secureHostInfo to relative url (without domain name)? I wont to set one site to different domains.
Good explanation but VERY BAD realization in yiiframework.com
I like the explanation in this article but why the hell is the yiiframework.com login not realized using https? The form is submitted using plain text (http).
I know this is probably not the best place to tell this bug but I hope it can be fixed using this article ;-)
Bug with modules - and a fix
In case of a url which is www.example.com/module/controller/action/arg1/arg1val
adding "module/controller" to the $secureRoutes won't work as expected.
The fix is, in function isSecureRoute(), replace the line
return ($pos = strpos($route, '/')) !== false && isset($this->_secureMap[substr($route, 0, $pos)]);
with the lines
if (($pos = strpos($route, '/')) !== false && isset($this->_secureMap[substr($route, 0, $pos)])) return true; //match controler/* return (($pos = strpos($route, '/', $pos+1)) !== false && isset($this->_secureMap[substr($route, 0, $pos)])); //match module/controller/*
Brilliant
This article solved the https problems of my website. Really helpful!!!
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.