Yii2 Advanced Same Domain under Windows/Apache, Issues with Pretty URL 400 Bad Request in Backend application

We are an agency using Yii2 advanced for many of our clients, in most cases same domain. In some cases same-domain same-login-session on both front and backend.

For one of our applications, we have a REST API front-end and a UI driven back-end served under the same domain (only the backend has standard login). This has worked for a long time but the client recently decided to move all their resources to Windows 2019, where our issue started as we are trying to configure this.

Issue
When front-end (API) and back-end (UI Application) are served under the same domain, the pretty URL scheme does not work for the backend but works find for the front-end. A 400 Bad Request error is produced. The same application and configuration has worked in Linux for multiple years.

Works: https://domain/admin (if access rules allow this without login)

Anything with a slash does not work, example -

https://domain/admin/site/index (event if access rules allow without login)

If we assign each application (front and back) a separate domain, the pretty URL scheme works for both.

We tried a number of config variations without success. Any guidance on this is appreciated. I will provide details below, including the original config (inherited from the live Linux app) as well as some variations we tried.

Env:

  • Windows server 2019
  • Yii2 version 2.0.43.
  • Apache Lounge 2.4.54
  • PHP 7.4.30

Frontend Original Config:

from main-local.php

'components' => [
        'request' => [
            'cookieValidationKey' => '<frontend-specific-key>',
        ],
    ],

from main.php


    'basePath' => dirname(__DIR__),
    'components' => [
        'request' => [
            'csrfParam' => '_csrf-<frontend-specific-string>',
        ],
        'user' => [
            'identityClass' => 'common\models\User',
            'enableAutoLogin' => true,
            'identityCookie' => ['name' => '_identity-<frontend-specific-string>', 'httpOnly' => true],
        ],
        'session' => [
            'name' => 'advanced-<frontend-specific-string>',
        ],
        'urlManager' => [
            'class' => 'yii\web\UrlManager',
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
                //...
            ],
        ],
    ],

*** CSRF validation is kept enabled

Backend Original Config:

Backend main-local.php

'components' => [
        'request' => [
            'cookieValidationKey' => '<backend-specific-key>',
        ],
    ],

from main.php


    'basePath' => dirname(__DIR__),
    'components' => [
        'request' => [
            'csrfParam' => '_csrf-<backend-specific-string>',
        ],
        'user' => [
            'identityClass' => 'common\models\User',
            'enableAutoLogin' => true,
            'identityCookie' => ['name' => '_identity-<backend-specific-string>', 'httpOnly' => true],
        ],
        'session' => [
            'name' => 'advanced-<backend-specific-string>',
        ],
        'urlManager' => [
            'class' => 'yii\web\UrlManager',
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
                //...
            ],
        ],
    ],

Relevant Windows-Apache-Lounge vhost configs

    # Alias to the backend app as '/admin'
    Alias /admin "${SRVROOT}/backend/web"

    # prevent the directory redirect to the URL with a trailing slash
    RewriteRule ^/admin$ /admin/ [L,PT]

    <Directory ${SRVROOT}/backend/web>
        AllowOverride all
        <IfVersion < 2.4>
            Order Allow,Deny
            Allow from all
        </IfVersion>
        <IfVersion >= 2.4>
            Require all granted
        </IfVersion>
    </Directory>

.htaccess on both front and backend apps:

# Turn on rewrite engine
RewriteEngine on

##< FRIENDLY URL> ##
# 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
##</ FRIENDLY URL> ##

Changes / Things we tried and didn’t work

# 1. -----------------------------------------------
'request' => [
    ...
    'csrfCookie' => [
        'httpOnly' => true,
        'path' => '/admin',
    ],
],
'user' => [
    ...
    'identityCookie' => [
        ...
        'path' => '/admin',
    ],
],
'session' => [
    ...
    'cookieParams' => [
        'path' => '/admin',
    ],
],
# -----------------------------------------------
# 2. -----------------------------------------------
# Making the front-end and back-end share the same PHP session (as in same-login)
# 'components->session->name' is the same in frontend+backend (main.php)
# 'components->request->cookieValidationKey' is the same in frontend+backend+common (main-local.php) and common (codeception-local.php)
# -----------------------------------------------
# 3. -----------------------------------------------
# Adjusting urlManager in both apps
# Frontend: 'components->urlManager->baseUrl' = '/'
# Backend: 'components->urlManager->baseUrl' = '/admin'
# -----------------------------------------------
  1. IdentityCookie change to -
'identityCookie' => ['name' => '_identity-<string>', 'httpOnly' => true, 'sameSite' => PHP_VERSION_ID >= 70300 ? yii\web\Cookie::SAME_SITE_LAX : null],
  1. Also tried a various combinations of the above as well as tinkering with the path params such as adding an absolute URL using alias as so - Yii::getAlias(’@frontend_url’) and/or Yii::getAlias(’@backend_url’) (that includedd /admin within the alias).

Again, all is okay under CentOS and Ubunto Linux distro with Apache 2+ and PHP 7+. The issue occurs in Windows.

This should point to something peculiar with Apache htaccess thing.
My first attemp in that case would be removing all htaccess and try accessing with index.php (htaccess removed and showScriptName => true ) and see if it works. If it doesn’t I would work until it does then enable htaccess redirects and see if it also works.

Does not sound like a direct Yii problem to me but rather Apache thing. But I might be wrong!

Thank you for your insight. I agree with your overall take. It is not directly Yii related since it works fine in multiple boxes/env (Dev, Staging and Production) with different Linux distros, including one with Nginx (Dev). It is definitely specific to Apache/PHP on Windows. We’ll try the approach you mentioned with .htaccess files (and update this post) to see if we made any progress.

1 Like

So, trying this approach ( showScriptName => true) definitely confirms that the issue is related to URL rewrite, which is what we originally assumed. But we are not any closer to the exact cause/solution. With the value set to true ( showScriptName => true), it works with the script (index.php) name in the URL, for example -

http://domain/admin/index.php/site/index

Once it is set to false ( showScriptName => false), it is back to the 400 error -

400 Bad Request Your browser sent a request that this server could not understand.

… for all pretty URLs (without the script name - index.php), for example -

http://domain/admin/site/index

We’re assuming that for Apache Lounge and Windows server 2019 combo (with the proper Apache modules enabled),

  1. The .htaccess rules for Pretty URL is not being interpreted by the app.
  2. Or, the request is not being forward/passed on to the script (index.php) by the .htaccess rule properly

The .htaccess rules look simple -

# Turn on rewrite engine
RewriteEngine on

##< FRIENDLY URL> ##
# 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
##</ FRIENDLY URL> ##

If we comment out the last line, it throws a 404 error because index.php is not receiving the request anymore and the pretty URL does not represent a real dir structure. So, it seems the rules are not providing what the index.php script needs.

So, back to our original query, is there anything in the .htaccess file that can be changed and/or changes within the Yii 2 application config, so the request will be passed on and interpreted as expected?

Can/should we try a different way of writing the .htaccess rewrite rules? Any idea would be appreciated.

BTW - the Apache access log lists the request this way (one 200 OK and another 400), if this is helpful to anyone in any way. The 200 request shows index.php while the 400 does not.

::1 - - [23/Aug/2022:12:29:50 -0700] "GET /admin/ HTTP/1.1" 200 7632 "http://domain/admin/index.php/site/index" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0"
::1 - - [23/Aug/2022:12:29:58 -0700] "GET /admin/sync-queue/index HTTP/1.1" 400 226 "http://domain/admin/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0"

I was recently setting up a site with prettyURL’s and 404 issues too. Would ‘enableStrictParsing’ set to true? If it is, set it to false and the same with ‘showScriptName’

I just want to mention that removing FollowSymLinks from my Apache config gives me 403.
(rewrite isn’t allowed)

I don’t know if it would work or not, but checking the linefeed code of .htaccess (and Apache config file) might be worth doing.

I once had an issue with linux when a certain file (I forgot what it was) had CR/LF linefeed code. I guess the opposite case could be possible.

Thank you for all of your responses. So far, the 400 bad request error continues after trying all of this and related tweaks. For now, we’ll turn off Pretty URL on the backend app. If we find a solution, I will post it here.

2 Likes

Have you found any solution?