yii2: UrlRule Problem with trailing slash (/)

Hi i have added a UrlManager rules like given below added to my config

  '<project:[-a-zA-Z]+>/<controller:\w+>' => '<controller>',
  '<project:[-a-zA-Z]+>/<controller:\w+>/<action:\w+>' => '<controller>/<action>',

If i then call a url like

/projectname/controllername/action            (works)
/projectname/controllername           (works pointing to the default action in the controller)

/projectname/controllername/              (with trailing slash doens't work)

But the Url with a trailing slash produces a 404 error on the SiteController.

This is because the UrlRule doesn’t match. if i change the UrlRule to the code below (\w* instead of \w+) the default controller action is called.

  '<project:[-a-zA-Z]+>/<controller:\w+>/<action:\w*>' => '<controller>/<action>',

I also tested the UrlRules in yii1 but there the UrlRules from the top are also working with a trailing / in the Url but not in yii2.

So I guess there is some kind of “bug”. I Don’t Know if the trailing slashes are removed in the Request in yii1 but not in yii2. But there is clearly some difference between the 2 frameworks.

Regards Horizons

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 1
  • Comments: 23 (14 by maintainers)

Commits related to this issue

Most upvoted comments

Could be done app-side like the following:

$config = [
    // ...
    'params' => require(__DIR__ . '/params.php'),

    // redirect to the page with the trailing slash
    'on beforeRequest' => function () {
        $app = Yii::$app;
        $pathInfo = $app->request->pathInfo;
        if (!empty($pathInfo) && substr($pathInfo, -1) !== '/') {
            $app->response->redirect('/' . rtrim($pathInfo) . '/', 301);
        }
    },
];

Add a route at last line '<url:.+>/' => 'site/redirect',

and add a action

public function actionRedirect($url = '')
{
    if (substr($url, -1) == '/') {
        return $this->redirect('/' . substr($url, 0, -1));
    } else {
        throw new NotFoundHttpException;
    }
}

@samdark could you please take a look at my pull request? I hope it will help to solve the issue. I use this approach on sites I develop.

Another .htaccess rewrite rule before checking directory.

    # see http://stackoverflow.com/a/11880879/2485734
    RewriteCond %{REQUEST_URI} !(/$|\.)
    RewriteRule (.*) %{REQUEST_URI}/ [R=301,L]

and the config for UrlManager, enabling suffix

return [
    'class' => 'yii\web\UrlManager',
    'enablePrettyUrl' => true,
    'showScriptName' => false,
    'suffix' => '/',
    'rules' => [
        '/' => 'site/index',
        'product/<slug>' => 'product/view',
        'product-category/<name>' => 'product-category/index',
        'search/<keyword>' => 'search/result',
    ],
];

This redirect rule works for me in htaccess:

RewriteCond %{THE_REQUEST} \s/+(.+?)/+[?\s]
RewriteRule /$ /%1 [R,L]

@gerpayt:

slash should be inside regular expression: '<url:.+/>' => 'site/redirect'

some improvements: 301 redirect, baseUrl, one redirect in case of many slashes

public function actionRedirect($url)
{
    if (substr($url, -1) == '/') {
        return $this->redirect(Yii::$app->getRequest()->getBaseUrl() . '/' . rtrim($url, '/'), 301);
    } else {
        throw new NotFoundHttpException;
    }
}

@samdark then it should be an option to choose between with/without ending slash. And option to allow both for people who don’t care about seo. And maybe 404 as default option (for BC)?