On hiding the main Symfony front controller

Published on 2019-08-23 • Modified on 2019-08-23

In this post, we will see how to hide the front controller's file name of a Symfony application so it can't be accessed when typing it: "index.php". The less the users (or hackers of course) will know about the technical implementation of your website, the better it is. Let's go! 😎

It's the summer so this post will be very light. In fact, it's a test I have done on this blog. I wanted to have a specific controller for the production environment (like with Symfony3) and to hide the main controller so typing "index.php" will give a 404 error without using a .htaccess file nor redirections (when using Apache as the web server). It's not considered as a good practice, so use at your own risk!

» Published in "A week of Symfony 661" (26 August - 1 September 2019).

Use at your own risk

Configuration

  • PHP 7.2
  • Symfony 4.4
  • Apache 2.4

Introduction

The main front controller is the file that handles all http requests of a Symfony application. When using Symfony4, it's the index.php file and it's located in the "public" directory. For example when accessing the official Symfony website, you will notice that these URLs are available:

The goal here will be to prevent the users, bots or search indexes to access URLs through the index.php file.

Creation of a specific production front controller

Add a file in the "public" folder of your application and name it with a random string. (eg: ajJoNGJix4KRMM3xwhgLXG4GUgJHhyFk.php) Add the following code:

<?php declare(strict_types=1);

// public/ajJoNGJix4KRMM3xwhgLXG4GUgJHhyFk.php
// this is a fake name, not the one used by this website! 😁

use App\Kernel;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\HttpFoundation\Request;

require __DIR__.'/../vendor/autoload.php';

(new Dotenv())->load(__DIR__.'/../.env');
$kernel = new Kernel('prod', false);
$request = Request::createFromGlobals();
/** @noinspection PhpUnhandledExceptionInspection */
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

We have removed all dev and debug stuff to keep only the minimum code. (the performance difference with the default index.php isn't substantial) And we still have our index.php, I have renamed it to index_dev.php in this project but it's Ok to keep the original name.

Be careful that if you set TRUSTED_PROXIES or TRUSTED_HOSTS environment variables, you should keep the following lines:

if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? false) {
    Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST);
}

if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) {
    Request::setTrustedHosts(explode(',', $trustedHosts));
}

Hiding the main front controller name

Now, let's hide the main controller. The first thing is to exclude the index.php file from your deployment process. To deploy this blog I use a simple Makefile (you will find the full source here). I delete the file in the git-update target. Now, we have to tell our web server to use the new file, for example when using Apache:

# configuration/vhosts/prod/strangebuzz.com-le-ssl.conf
<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName www.strangebuzz.com
    ServerAlias strangebuzz.com
    DocumentRoot /var/www-protected/strangebuzz.com/public

    <Directory /var/www-protected/strangebuzz.com/public>
        AllowOverride All
        Require all granted
        # this is a fake one, again! 😁
        FallbackResource /ajJoNGJix4KRMM3xwhgLXG4GUgJHhyFk.php
        #FallbackResource /index.php
    </Directory>

    RedirectMatch 404 /\.git

    ErrorLog /var/log/apache2/strangebuzz.com_error.log
    CustomLog /var/log/apache2/strangebuzz.com_access.log combined

    RewriteEngine on
    RewriteCond %{SERVER_NAME} =strangebuzz.com
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
SSLCertificateFile /etc/letsencrypt/live/www.strangebuzz.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/www.strangebuzz.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

As you can see, we have modified the fallback resource to be our new production controller instead of index.php. Et voilà! Now, when accessing index.php, we get a 404 so there is only one URL associated to each resource. And the canonical URL will always be the version without the file inside.

Bonus:
1) There is a post in this blog where you could find the real main controller file name of this website, can you find it?
2) Why is it still indicated index.php?

Click here to see the answers!

1) It's here: The Symfony Request class interactive cheatsheet. The main front controller file name is returned by the getScriptName() function of the Request value object.
2) I am cheating, I simply put index.php manually instead of getting the result of the function! 😁

[
    'name' => 'getScriptName',
    'parameters' => '',
    'type' => 'string',
    //'result' => $r->getScriptName(),
    'result' => 'index.php', // hide real controller name
    'show' => !$refresh,
    'url' => 812
],

That's it! I hope you like it. Check out the links below to have additional information related to the post. As always, feedback, likes and retweets are welcome. (see the box below) See you! COil. 😊

 The Symfony doc  More on Stackoverflow


» Comments

Privacy-focused with Commento. (Comment system added on 2019-11-18: be the first! 🥇)

» Call to action

Did you like this post? You can help me back in several ways: (use the box above to comment or the Tweet on the right to contact me )

  • Report any error/typo.
  • Report something that could be improved.
  • Like and retweet!
  • Follow me on Twitter
  • Subscribe to the RSS feed.

Thank you for reading! And see you soon on Strangebuzz! 😉

COil