Pragmatism in the real world

Running Slim 4 in a subdirectory

If you want to run Slim 4 in a subdirectory of your website, then you have a few things you need to do. Let’s consider the situation:

  • Your main website is in the directory /var/www/html and is accessed at https://example.com/.
  • You want your new Slim 4 app to be in the directory /var/www/html/myapp and to be accessed at https://example.com/myapp.
  • Your Slim 4 index.php file is stored in /var/www/html/myapp.

There are two things you need to sort out: the web server and the Slim app. Let’s start with the web server.

The web server (Apache)

I’m going to cover Apache here as that’s the one I know. Somewhere in your virtual host definition file (a .conf file within /etc/apache2/sites-available on Ubuntu) you have DocumentRoot /var/www/html. You should also have a <Directory "/var/www/html"> section. Ensure that there is AllowOverride All within that Directory section so that Apache will read .htaccess files.

Create a .htaccess file in /var/www/html/myapp with the following contents:

RewriteEngine On
RewriteBase /myapp
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]

The key thing here is the RewriteBase directive which must be set to the URL segments where your index.php is. In our case, our Slim 4 app’s index.php is at https://example.com/myapp/index.php, so the RewriteBase is /myapp/. If our index.php was at https://example.com/foo/bar/index.php, then we would set RewriteBase /foo/bar/.

Now that we’ve told Apache where to rewrite from, we need to tell Slim 4 too.

Slim 4 config

In our Slim 4 application, we need to set the base path so that the router can match the URL from the browser with the path set in the route registration methods ($app->get(), $app->post()). This is done with the setBasePath() method.

In your index.php, add:

$app->setBasePath('/myapp');

Note that you do not include the trailing / as that’s the first character in the route registration method’s path (.e.g $app->get('/contact', ...)).

Everything will now work, but if you don’t want to hard code the base path in setBasePath(), then you can use this function:

$app->setBasePath((function () {
    $scriptDir = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));
    $uri = (string) parse_url('http://a' . $_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH);
    if (stripos($uri, $_SERVER['SCRIPT_NAME']) === 0) {
        return $_SERVER['SCRIPT_NAME'];
    }
    if ($scriptDir !== '/' && stripos($uri, $scriptDir) === 0) {
        return $scriptDir;
    }
    return '';
})());

In this case, we define an anonymous function and call it. The code in this anonymous function is based on how Slim 3 determined the base path. It works well when it works, but if you have a more esoteric requirement, then it won’t work. With Slim 3, it was complicated to handle those situations, which is why in Slim 4 it’s an explicit setBasePath() call where you have full control.

All done

There you have it. This is another case where Slim 4 has removed some magic as that magic didn’t work in all situations. The same solution can be used for any web server and will allow you to run your Slim 4 application in subdirectory should you need to.

Update Slim’s website now has documentation on this too.

12 thoughts on “Running Slim 4 in a subdirectory

  1. This works great if index.php is located at '/var/www/html/myapp/index.php', but I cannot get the directory index to work if I want to deploy index.php in a 'public' sub directory, thus in '/var/www/html/myapp/public/index.php'.

    I have set 'RewriteBase' to '/myapp/public' and all the defined routes are working fine, except the '/' route (the index route).

    It would be helpful if you could also document a way to solve this problem.

    1. Just remove this line, it will start working,
      RewriteCond %{REQUEST_FILENAME} !-d

  2. This will not work if you are not going to add .htaccess to /var/www/html, I am experiencing this right now, but the problem with adding an .htaccess to the root directory is it will make the current website unusable, do you happen to have a remedy for this?

  3. I've got a demo app at /var/www/html/slim/t1.
    It seems to work with this standard root .htaccess:
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [QSA,L]

    And this one in /slim/t1/public:
    RewriteEngine on
    RewriteBase /slim/t1
    RewriteRule ^$ public/ [L]
    RewriteRule (.*) public/$1 [L]

    Just starting! Will need to check other routes. Thanks!

Comments are closed.