Moving Craft’s Files Below the Webroot

We recommend that all of Craft’s PHP files live above your server’s webroot, but sometimes that may not be possible due to hosting restrictions.

Let’s look at what’s involved in restructuring the starter project’s files so that everything lives below your webroot.

These steps assume you are working directly on your server over FTP, and that your access is confined to the webroot.

Step 1: Create a new app/ folder #

Create a new folder within your webroot called app/.

Step 2: Protect the app/ folder #

If your web server is running Apache, and you’re allowed to create .htaccess files (via the AllowOverride directive), then create a new .htaccess file within your app/ folder, with this content:

deny from all

If you’re running Nginx or aren’t able to use .htaccess files, then you’ll need to ask your web hosting provider how to protect your app/ folder from public HTTP traffic.

If you have no means to prevent access to this new app/ folder, you should not proceed. In addition to Craft’s source files, your templates, caches, and configuration may be publicly accessible.

Step 3: Move the application files #

Move or upload the following folders and files (typically found at the root of new projects) into your new app/ folder:

  • config/
  • modules/
  • storage/
  • templates/
  • translations/ (if you have it)
  • vendor/
  • .env
  • .env.example
  • bootstrap.php (if you have it)
  • composer.json
  • composer.lock
  • craft
  • craft.bat

Then, move everything in the default web/ folder back up to the webroot. At this point, your webroot should look like this:

app/               (Folder created in step #1)
├── config/
├── modules/
├── storage/
├── templates/
├── translations/
├── vendor/
├── .env
├── .env.example
├── .htaccess      (Created in step #2)
├── bootstrap.php
├── composer.json
├── composer.lock
├── craft
└── craft.bat
web/               (You can delete this; should be empty.)
.htaccess          (Craft’s default)
...                (Other static files, uploads, etc.)

Keep in mind we’re only looking at what is relevant to your server—it’s up to you how your project is organized in version control or for local development.

Step 4: Update Entry Scripts #

Finally, we need to tell your index.php file where Craft has been moved to. This process may differ based on when you first initialized the project, but the principles are similar.

Projects using bootstrap.php #

Open index.php and find this line:

require dirname(__DIR__) . '/bootstrap.php';

Change it to this:

require __DIR__ . '/app/bootstrap.php';

Projects without bootstrap.php #

In index.php, find this line:

define('CRAFT_BASE_PATH', dirname(__DIR__));

Change it to this:

define('CRAFT_BASE_PATH', __DIR__ . '/app');

Once you’ve done that, Craft should be back up and running, having initialized from your new app/ folder.

SSH users: The craft executable doesn’t require any changes, as its position relative to the application files didn’t change—but don’t forget to move into your app/ directory before running Composer or Craft console commands!

Local Development #

If you use a local development environment like DDEV, you may need to update its configuration:

# .ddev/config.yml
name: my-project
type: php
docroot: '' # <- An empty string uses the project root

Similarly, if you’ve relocated the craft CLI executable, you’ll need to let DDEV know what directory to look in, by setting this variable in your .ddev/.env file:


Tracking your remote server’s webroot as a subdirectory in your project is effectively the same way the Craft starter project works—instead of moving the webroot up, you can also move Craft’s files down into the web/ folder.

Applies to Craft CMS 4 and Craft CMS 3.