SEO Friendly URLs in Yii2

This tutorial provides some insights into Apache configuration and Yii2 configuration in order to achieve user friendly and search engine friendly URLs for your web app.

Basic Template

Yii2 official documentation has a page about Shared Hosting configuration that provides an example of how you can configure Apache for SEO friendly URLs using an .htaccess file.

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

After that you need to configure urlManager in your config/web.php file.

$config = [
    //...
    'components' => [
        'urlManager' => [
            'class' => 'yii\web\UrlManager',
            'showScriptName' => false,
            'enablePrettyUrl' => true,
        ],
        //...

If your application is located in the root folder the routes will look something like this: http://yourdomain.com/web/site/index, otherwise if your Yii2 app is in a subfolder they might look like this http://yourdomain.com/app/web/site/index.

Hide the web folder

You can get rid of the web part of the URL by moving all the files from the web folder to your applications root folder and changing the paths in index.php, but this method will mix the contents of the /assets/ folder and the /web/assets/ folder. This is why this solution is better suited for the advanced application template, unless you are willing to move all of your code along with the vendors folder into a separate folder like Yii 1.x did.

A better way to solve this is by adding an additional .htaccess file to the root of your app.

Options -Indexes

RewriteEngine on
RewriteRule ^(.*)$ web/$1 [L]

Note that this will only work if you have the aforementioned .htaccess file in the web directory. Otherwise the request will get caught in an internal redirect loop.

There are many websites that show similar configurations that are more complex. Since we are redirecting all requests to the web folder there is no need to deny access to files that start with a dot, JSON files and such. The Basic Template doesn’t have any of those files in the web folder. For the advanced template you can deny access to dotfiles by adding these lines.

<Files ~ "(^\.|\/\.)">
Order allow,deny
Deny from all
</Files>

Now we have to tell Yii that our base URL has changed. We do this by configuring baseUrl for urlManager and the request component in config/web.php.

$config = [
    //...

    'components' => [
        'urlManager' => [
            'class' => 'yii\web\UrlManager',
            'showScriptName' => false,
            'enablePrettyUrl' => true,
            'baseUrl' => '/',
        ],
        'request' => [
            'baseUrl' => '',
            //...
            ],
        //...

If your app is in a subfolder put the name of the subfolder into the baseUrl parameter ‘baseUrl’ => ‘/app’,.

Advanced Template

We can configure the advanced template similarly to the basic template. The .htaccess files in the web folders will be the same. But the .htaccess file for the root folder will be different to allow requests to the backend app.

Options -Indexes

RewriteEngine on
#if the request is not to the backend, route it to /frontend/web
RewriteCond %{REQUEST_URI} !^/backend
RewriteRule ^(.*)$ /frontend/web/$1 [L]

#otherwise route the request to the backend
RewriteRule ^backend/(.*)$ backend/web/$1 [L]

These rules don’t cause a redirect loop because the request is further rewritten by the rules in the .htaccess file of the web directory.

Don’t forget to configure the baseUrl for the request component and the urlManager.

Links from backend to frontend

The official documentation suggests that in order to make links from the backend app to the frontend app and from the frontend app to the backend app we have to configure an additional urlManagerBackend component for the frontend app.

'urlManager' => [
    'class' => 'yii\web\UrlManager',
    'baseUrl' => '',
    'enablePrettyUrl' => true,
    'showScriptName' => false,
],
'urlManagerBackend' => [
    'class' => 'yii\web\UrlManager',
    'baseUrl' => '/backend',
    'enablePrettyUrl' => true,
    'showScriptName' => false,
],

And an addition urlManagerFrontend for the backend app.

'urlManager' => [
    'class' => 'yii\web\UrlManager',
    'baseUrl' => '/backend',
    'enablePrettyUrl' => true,
    'showScriptName' => false,
],
'urlManagerFrontend' => [
    'class' => 'yii\web\UrlManager',
    'baseUrl' => '',
    'enablePrettyUrl' => true,
    'showScriptName' => false,
],

Then you can use them like so: $frontendUrl = \Yii::$app->urlManagerFrontend->createUrl([‘site/index’]);

This is great when you don’t have any URL rules, otherwise not only will you have to duplicate all the rules, but you will also have to change them in two places. To avoid this you can store the configuration for urlManagers in separate files and load the with the require function.

'urlManager' => require('urlmanager.php'),
'urlManagerBackend' => require('../../backend/config/url-manager.php'),

For the backend:

'urlManager' => require('urlmanager.php'),
'urlManagerFrontend' => require('../../frontend/config/url-manager.php'),

And this is how you can keep the configuration inside your url-manager.php files.

<?php
return [
    'class' => 'yii\web\UrlManager',
    'showScriptName' => false,
    'enablePrettyUrl' => true,
];

Trailing slash

If you’ve ever used nibbler you might have gotten a notification about a trailing slash problem.

this-website-returns-page-content-with-or-without-a-trailing-slash

You can fix this by adding these two lines to your /web/.htaccess file(s) right after RewriteEngine on:

RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]

Configuring Apache Directly

.htaccess files are convenient because you can bundle them together with your application and because you don’t have to  restart Apache after changing something inside an .htaccess file in order for the changes to take effect. But you also have to pay with performance because Apache has to scan the .htaccess files on every request. To avoid that you can configure Apache from httpd.conf.

Here’s an example configuration I used for a VirtualHost on a windows machine.

<VirtualHost *:80>
    ServerAdmin webmaster@mytestapp
    DocumentRoot "C:\wamp\www\mytestapp\frontend\web"
    ServerName mytestapp
    ErrorLog "logs/mytestapp-error.log"
    CustomLog "logs/mytestapp-access.log" common

    Alias /admin "C:\wamp\www\mytestapp\backend\web"

    <Directory "C:\wamp\www\mytestapp\frontend\web">
        RewriteEngine on
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule . index.php
        AllowOverride None
        Order allow,deny
        Allow from all
    </Directory>
    <Directory "C:\wamp\www\mytestapp\backend\web">
        RewriteEngine on
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule . index.php
        AllowOverride None
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

Note that in this configuration the backend app is available through the /admin route.

The Rules

Now that we’ve got the configuration out of the way we can start improving our URLs.

'urlManager' => [
    'class' => 'yii\web\UrlManager',
    'baseUrl' => '/',
    'enablePrettyUrl' => true,
    'showScriptName' => false,
    'enableStrictParsing' => true,
    'rules' => [
        '/' => 'site/index',
        'about' => 'site/about',
        'contact' => 'site/contact',
        'login' => 'site/login',
        'logout' => 'site/logout',
        'captcha' => 'site/captcha',
        'signup' => 'site/signup',
        'request-password-reset' => 'site/request-password-reset',
        'reset-password' => 'site/reset-password',
    ],
],

‘enableStrictParsing’ => true, tells Yii2 to allow only the routes that are listed in the rules. It’s up to you to decide whether you want it or not.

A tutorial for advanced routing with urlManager is coming soon.

Published by

Alexander

Your friendly, neighborhood, full stack, pseudorandom text generator

8 thoughts on “SEO Friendly URLs in Yii2”

  1. i am trying to my forntend/home to home but when i did this only index.php file is calling for all menu what is the reason the

  2. Hi my friend, for me not work, I don’t know.

    I configure the urlManager component on web.php
    I create a .htaccess on folder web… i have the mod rewrite instaled on apache, and the page not show me nothing

    1. Hi Yoedusvany,

      That’s too bad. I might have encountered the same problem a long time ago, but I don’t remember what was the cause and how I fixed it. I haven’t used Yii in a long time.
      Also it’s difficult, sometimes even impossible to help with a problem without any data.

      Best regards,
      Alex

  3. hello , place a .htaccess file with this code Options -Indexes

    RewriteEngine on
    RewriteRule ^(.*)$ web/$1 [L] , when entering the basic directory works but if I enter such a basic/site/index’ I routed basic/web I missing?

Leave a Reply