Composer Plugin for improving the security of composer-based Drupal projects by moving all PHP files out of docroot
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

Packagist Downloads Build Status

Drupal Paranoia

Composer plugin for improving the website security for composer-based Drupal websites by moving all PHP files out of docroot.

Why use this Plugin?

The critical security issue with Coder is a good example to consider moving PHP files outside of docroot:

More related links:


Except for Windows, this plugin should work on environments that have Composer support. Windows support issue.


Make sure you have a based drupal-composer/drupal-project project created.

Rename your current docroot directory to app.

mv web app

Update the composer.json of your root package with the following changes:

"extra": {
    "installer-paths": {
        "app/core": ["type:drupal-core"],
        "app/libraries/{$name}": ["type:drupal-library"],
        "app/modules/contrib/{$name}": ["type:drupal-module"],
        "app/profiles/contrib/{$name}": ["type:drupal-profile"],
        "app/themes/contrib/{$name}": ["type:drupal-theme"],
        "drush/contrib/{$name}": ["type:drupal-drush"]
    "drupal-app-dir": "app",
    "drupal-web-dir": "web",

Use composer require ... to install this Plugin on your project.

composer require drupal-composer/drupal-paranoia:~1

Done! The plugin and the new docroot are now installed.

Optional Configuration

Modify the asset file types

To extend the list of assets file types you can use the drupal-asset-files extra key:

"extra": {
    "drupal-asset-files": [

If you need to modify it you can use the post-drupal-set-asset-file-types event:

"scripts": {
    "post-drupal-set-asset-file-types": [

 * @file
 * Contains \DrupalProject\composer\ScriptHandler.

namespace DrupalProject\composer;

use DrupalComposer\DrupalParanoia\AssetFileTypesEvent;

class ScriptHandler {

  public static function setAssetFileTypes(AssetFileTypesEvent $event) {
    $asset_file_types = $event->getAssetFileTypes();
    // Do what you want with the asset file types.


Plugin events

This plugin fires the following named event during its execution process:

  • drupal-paranoia-post-command-run: Occurs after the command drupal:paranoia is executed.
Example of event subscriber

namespace MyVendor;

use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
use DrupalComposer\DrupalParanoia\PluginEvents as DrupalParanoiaPluginEvents;

class MyClass implements PluginInterface, EventSubscriberInterface
    protected $composer;
    protected $io;

    public function activate(Composer $composer, IOInterface $io)
        $this->composer = $composer;
        $this->io = $io;

    public static function getSubscribedEvents()
        return array(
            DrupalParanoiaPluginEvents::POST_COMMAND_RUN => 'postDrupalParanoiaCommand',

    public function postDrupalParanoiaCommand(CommandEvent $event) {
        // Add your custom action.

Folder structure

Your project now is basically structured on two folders.

  • app: Contains the files and folders of the full Drupal installation.
  • web: Contains only the symlinks of the assets files and the PHP stub files from the app folder.

Every time that you install or update a Drupal package via Composer, the web folder is automatically recreated.

If necessary, you can rebuild it manually, running the command

composer drupal:paranoia

This could be necessary when updating themes images, CSS and JS files.

Public files

This plugin assumes that the public files folder exists at app/sites/<site>/files and symlinks web/sites/<site>/files -> ../../../app/sites/<site>/files.


The document root configuration of your web server should point to the web path.