Article

Legacy PHP Project Structure Modernization

Separate the public document root from application code and adopt a practical public/src/config/storage/tests layout without forcing a framework.

Many inherited apps place index.php, configuration with database passwords, upload directories, and class files all inside the Apache or nginx document root. That layout worked when “FTP the folder” was the deploy model. It is a liability once scanners request /config/database.php or leftover backup files.

Modern structure is not one framework’s opinion forced onto every codebase. It is a practical separation: only the front controller and static assets are web-reachable; application code, config, and storage live beside or above the document root.

LEGACY — everything in docroot
/var/www/html/
  index.php
  admin/
  includes/
  config.php          ← credentials web-reachable if misconfigured
  uploads/
  templates/
MODERN — separated roots
/var/www/app/
  public/             ← document root
    index.php
    assets/
  src/
  config/             ← outside docroot
  templates/
  storage/
    logs/
    cache/
  tests/
  vendor/
  composer.json

Why document root separation matters

  • Reduces accidental exposure of config, composer files, and VCS metadata
  • Makes “only public/ is served” an infrastructure rule instead of an .htaccess hope
  • Clarifies where uploads and logs belong so they are not executable PHP

Migration without a rewrite

  1. Create public/ and move the front controller and assets there.
  2. Point the vhost document root at public/; keep a rollback vhost snippet.
  3. Move config above the docroot and update path constants once.
  4. Leave legacy class directories in place initially; introduce src/ for new code.
  5. Block execution in upload directories; store user files under storage/ when possible.

Pair with Composer, security modernization, and nginx + PHP-FPM or Apache FPM guides. Compare configs with Config Diff.

Related reading