1. Enable pretty URLs
Sometimes users want to share your site URLs via social networks. For example, by default your about page URL looks like http://mkchowdhury.com/index.php?r=site%2Fabout. Let’s imagine this link on Facebook page. Do you want to click on it? Most of users have no idea what is index.php and what is %2. They trust such link less, so will click less on it. Thus web site owner would lose traffic.
URLs such as the following is better: http://mkchowdhury.com/about. Every user can understand that it is a clear way to get to about page.
Let’s enable pretty URLs for our Yii project.
1.1 Apache Web server configuration
If you’re using Apache you need an extra step. Inside your .htaccess file in your webroot directory or inside location section of your main Apache config add the following lines:
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
1.2 URL manager configuration
Configure urlManager component in your Yii config file:
‘components’ => [
//…
‘urlManager’ =>[
‘class’ => ‘yii\web\UrlManager’,
‘showScriptName’ => false, // Hide index.php
‘enablePrettyUrl’ => true, // Use pretty URLs
‘rules’ => [
],
],
//
],
Remove site parameter from URL
After previous steps you will get http://mkchowdhury.com/site/about link. site parameter tells nothing helpful to your users. So remove it by additional urlManager rule:
‘rules’ => [
‘<alias:\w+>’ => ‘site/<alias>’,
],
As a result your URL will looks like http://mkchowdhury.com/about.
2. Pagination pretty URLs
For example we can render our site content by GridView. If there are a lot of content rows we use pagination. And it is necessary to provide GET request for every pagination page. Thus search crawlers can index our content. We also need our URLs to be pretty. Let’s do it.
2.1. Initial state
For example we have a page with such URL http://mkchowdhury.com/ schools/ schoolTitle. Parameter schoolTitle is a title parameter for our request.
In the application config file we have:
‘components’ => [
//…
‘urlManager’ =>[
//….
‘rules’ => [
‘schools/<title:\w+>’ => ‘site/schools’,
],
],
//
],
We decided to add a GridView on the page ‘http://mkchowdhury.com/schools/schoolTitle’. Pagination pretty URLs.
When we click on pagination link our URL is transformed to ‘http://example. com/schools/schoo1Title?page=2’.
We want our pagination link looks like ‘http://mkchowdhury.com/schools/schoolTitle/2’.
Let’s add new urlManager rule higher than existed rule. Here it is:
‘components’ => [
//…
‘urlManager’ =>[
//….
‘rules’ => [
‘schools/<title:\w+>/<page:\d+>’ => ‘site/schools’, // new rule
‘schools/<title:\w+>’ => ‘site/schools’,
],
],
//
],
3. Adding SEO tags
Organic search is an excellent traffic source. In order to get it you have to make a lot of small steps to improve your project.
One of such steps is to provide different meta tags for different pages. It will improve your site organic search appearance and may result in better ranking.
Let’s review how to add SEO-related metadata to your pages.
3.1. Title
It is very simple to set title. Inside controller action:
\Yii::Sapp->view->title = ‘title set inside controller’;
Inside a view:
$this->title = ‘Title from view’;
Note: Setting $this->title in layout will override value which is set for concrete view so don’t do it.
It’s a good idea to have default title so inside layout you can have something like the following:
$this->title = $this->title ? $this->title : ‘default title’;
3.2. Description and Keywords
There are no dedicated view parameters for keywords or description. Since these are meta tags and you should set them by registerMetaTag() method.
Inside controller action:
\Yii::$app->view->registerMetaTag([
‘name’=>’description’,
‘content’=>’Descriptionsetinsidecontroller’,
]);
\Yii::$app->view->registerMetaTag([
‘name’=>’keywords’,
‘content’=>’Keywordssetinsidecontroller’,
]);
Inside a view:
$this->registerMetaTag([
‘name’=>’description’,
‘content’=>’Descriptionsetinsideview’,
]);
$this->registerMetaTag([
‘name’=>’keywords’,
‘content’=>’Keywordssetinsideview’,
]);
All registered meta tags will be rendered inside layout in place of $this->head() call.
Note that when the same tag is registered twice it’s rendered twice. For example, description meta tag that is registered both in layout and a view is rendered twice. Usually it’s not good for SEO. In order to avoid it you can specify key as the second argument of registerMetaTag:
$this->registerMetaTag([
‘name’=>’description’,
‘content’=>’Description1’,
],’description’);
$this->registerMetaTag([
‘name’=>’description’,
‘content’=>’Description2’,
],’description’);
In this case later second call will overwrite first call and description will be set to “Description 2”.
4. Canonical URLs
Because of many reasons the same or nearly the same page content often is accessible via multiple URLs. There are valid cases for it such as viewing an article within a category and not so valid ones. For end user it doesn’t really matter much but still it could be a problem because of search engines because either you might get wrong URLs preferred or, in the worst case, you might get penalized.
One way to solve it is to mark one of URLs as a primary or, as it called, canonical, one you may use <link rel=”canonical” tag in the page head.
Note: In the above we assume that pretty URLs are enabled. Let’s imagine we have two pages with similar or nearly similar content:
- http://mkchowdhury.com/item1
- http://mkchowdhury.com/item2
Our goal is to mark first one as canonical. Another one would be still accessible to end user. The process of adding SEO meta-tags is described in “adding SEO tags” recipe. Adding <link rel=”canonical” is very similar. In order to do it from controller action you may use the following code:
\Yii::$app->view->registerLinkTag([‘rel’=>’canonical’,’href’=> Url::to([‘item1’],true)]);
In order to achieve the same from inside the view do it as follows:
$this->registerLinkTag([‘rel’=>’canonical’,’href’=> Url::to([‘item1’],true)]);
Note: It is necessary to use absolute paths instead of relative ones.
As an alternative to Url :: to() you can use Url :: canonical() such as
$this->registerLinkTag([’rel’=>’canonical’,’href’=> Url::canonical()]);
The line above could be added to layout. Url :: canonical() generates the tag based on current controller route and action parameters ( the ones present in the method signature).
5. Using redirects
301
Let’s imagine we had a page http://mkchowdhury.com/item2 but then permanently moved content to http://mkchowdhury.com/item1. There is a good chance that some users (or search crawlers) have already saved http://mkchowdhury.com/item2 via bookmarks, database, web site article, etc. Because of that we can’t just remove http://mkchowdhury.com/item2. In this case use 301 redirect.
class MyController extends Controller
{
public function beforeAction($action)
{
if(in_array($action->id, [‘item2’])) {
Yii::$app->response->redirect(Url::to([‘item1’]), 301);
Yii::$app->end();
}
return parent::beforeAction($action);
}
For further convenience you can determine an array. So if you need to redirect another URL then add new key>value pair:
class MyController extends Controller
{
public function beforeAction($action)
{
$toRedir = [
‘item2’=>’item1’,
‘item3’=>’item1’,
];
if(isset($toRedir[$action->id])) {
Yii::$app->response->redirect(Url::to([$toRedir[$action->id]]),
301);
Yii::$app->end();
}
return parent::beforeAction($action);
}
6. Using slugs
Even when pretty URLs are enabled, these often aren’t looking too friendly:
http://mkchowdhury.com/post/42
Using Yii it doesn’t take much time to make URLs look like the following:
http://mkchowdhury.com/post/hello-world
6.1. Preparations
Set up database to use with Yii, create the following table:
post
===
id, title, content
Generate Post model and CRUD for it using Gii.
6.2. How to do it
Add slug field to post table that holds our posts. Then add sluggable behavior to the model:
<?php
use yii\behaviors\SluggableBehavior;
//…
class Post extends ActiveRecord
{
//…
public function behaviors()
{
return [
[
‘class’=> SluggableBehavior::className(),
‘attribute’=>’title’,
],
];
}
//…
}
Now when post is created slug in database will be automatically filled. We need to adjust controller. Add the following method:
protected function findModelBySlug($slug)
{
if(($model = Post::findOne([‘slug’=> $slug])) !== null) {
return $model;
}else{
throw new NotFoundHttpException();
}
}
Now adjust view action:
public function actionView($slug)
{
return $this->render(‘view’, [
‘model’=> $this->findModelBySlug($slug),
]);
}
Now in order to create a link to the post you have to pass slug into it like the following:
echo Url::to([‘post/view’,’slug’=> $post->slug]);
6.3. Handling title changes
There are be multiple strategies to deal with situation when title is changed. One of these is to include ID in the title and use it to find a post.
http://mkchowdhury.com/post/42/hello-world
7. Handling trailing slash in URLs
By default Yii handles URLs without trailing slash and gives out 404 for URLs with it. It is a good idea to choose either using or not using slash but handling both and doing 301 redirect from one variant to another.
For example,
/hello/world – 200
/hello/world/ – 301 redirect to /hello/world
7.1. Using UrlNormalizer
Since Yii 2.0.10 there’s UrlNormalizer class you can use to deal with slash and no-slash URLs in a very convenient way. Check out the “URL normalization section” in the official guide for details.
7.2. Redirecting via web server config
Besides PHP, there’s a way to achieve redirection using web server.
7.3. Redirecting via nginx
Redirecting to no-slash URLs.
location / {
rewrite ^(.*)/$ $1 permanent;
try_files $uri $uri/ /index.php?$args;
}
Redirecting to slash URLs.
location / {
rewrite ^(.*[^/])$ $1/ permanent;
try_files $uri $uri/ /index.php?$args;
}
7.4. Redirecting via Apache
Redirecting to no-slash URLs.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
Redirecting to slash URLs.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*[^/])$ /$1/ [L,R=301]