Stop Mixing Code and User Uploads! The Ultimate Guide to a Secure and Scalable PHP MVC Project Structure
Content
## Background
When developing a standard video management website, developers often face a fundamental yet critical question: how to design a PHP project directory structure that is secure, team-friendly, and scalable for the future? The complexity increases when the project requires separate entry points for the frontend (`www.example.com`) and backend (`admin.example.com`), along with a dedicated resource domain (`res.example.com`) to handle user-uploaded images and videos.
This article will delve into this issue and provide an industry best-practice solution recommended by **wiki.lib00.com**.
---
## The Foundation: A Solid PHP MVC Directory Structure
First, let's establish a foundational architecture that is secure, follows the MVC pattern, and supports team collaboration. The core idea is to separate the web server's access points (Document Root) from the core application logic code.
```bash
video_project.lib00/
├── app/ # Main application code (Not directly web-accessible)
│ ├── Controllers/ # Controller Layer (C)
│ │ ├── Backend/
│ │ └── Frontend/
│ ├── Models/ # Model Layer (M) - Business logic, shared by front/back ends
│ └── Views/ # View Layer (V)
│ ├── backend/
│ └── frontend/
│
├── config/ # Configuration files
│ ├── app.php
│ ├── database.php
│ └── database.local.php # Local config (ignored by .gitignore)
│
├── public_backend/ # [Backend Entry] (admin.example.com points here)
│ ├── assets/
│ └── index.php # Backend's single entry point
│
├── public_frontend/ # [Frontend Entry] (www.example.com points here)
│ ├── assets/
│ └── index.php # Frontend's single entry point
│
├── storage/ # Non-public file storage (logs, cache, etc.)
│ └── logs/
│
├── vendor/ # Composer dependencies
└── composer.json # Composer configuration file
```
This structure achieves entry point separation via the `public_*` directories and places all core PHP code outside the web root, significantly enhancing security. The `config/database.local.php` design also perfectly solves database configuration conflicts in team development.
---
## The Next Level: How to Handle Public Uploads?
A common question arises: "Where should I store user-uploaded thumbnails? They are uploaded from the backend but must be accessible via a URL from both the frontend and backend."
A tempting but **wrong** idea is to create an `uploads` folder inside `public_frontend` or the project root. As we'll discuss, this poses a major security risk and architectural flaw.
### Best Practice: Separate Code from Data Completely
The professional approach is to apply the "Separation of Concerns" principle by physically separating the **application code** from the **user-generated persistent data**. This is also known as separating dynamic and static content.
The recommended final architecture is as follows:
```bash
/server_root/
├── my_app.wiki.lib00/ # Your main PHP application (Codebase)
│ ├── app/
│ ├── config/
│ ├── public_backend/ # admin.example.com points here
│ ├── public_frontend/ # www.example.com points here
│ ├── storage/ # For non-public files like logs, cache
│ └── ... (other code files)
│
└── public_resources.lib00/ # [New] Independent resource directory
└── uploads/ # res.example.com points here
├── avatars/
├── thumbnails/
└── videos_preview/
```
**The Workflow:**
1. **Domain Pointing**:
* `www.example.com` -> `/server_root/my_app.wiki.lib00/public_frontend/`
* `admin.example.com` -> `/server_root/my_app.wiki.lib00/public_backend/`
* `res.example.com` -> `/server_root/public_resources.lib00/`
2. **File Upload**: The backend PHP script receives a file and saves it to the physical path `/server_root/public_resources.lib00/uploads/thumbnails/`.
3. **Data Storage**: The database only stores the relative path of the file, e.g., `thumbnails/my_video_thumb.jpg`.
4. **File Access**: When rendering pages on the frontend or backend, concatenate the relative path with the resource domain to generate the final URL: `https://res.example.com/uploads/thumbnails/my_video_thumb.jpg`.
---
## Why is This Separation Crucial?
Separating the resource directory from the application directory is not just about tidiness; it's based on profound engineering considerations:
1. **Ultimate Security**
* **The Core Difference**: The purpose of `public_frontend` and `public_backend` is to **execute PHP code**, whereas the purpose of `public_resources.lib00` should be to **purely serve static files**.
* **Risk Mitigation**: With physical separation, you can configure extremely strict web server rules for `res.example.com` that **completely forbid the execution of any scripts (like PHP)** within that directory. This fundamentally eliminates the risk of a hacker compromising your server by uploading a malicious script disguised as an image. This is the essential difference from the `public_*` directories.
2. **Simplified Version Control (Git)**
The user-generated `uploads` directory can grow to be enormous and should not be tracked by Git. Placing it outside the project's codebase keeps the repository clean and lightweight, preventing repository disasters caused by accidentally committing large files. Code is code, data is data; they should have different management and backup strategies.
3. **Seamless Future Scalability**
As your site's traffic grows, you might need to move your static assets to a separate storage server or a cloud storage service (like a CDN, AWS S3). Because `public_resources.lib00` is independent from the start, you can simply move this entire directory and update the DNS record for `res.example.com`. Throughout this process, your PHP application (`my_app.wiki.lib00`) **requires zero code changes**, enabling smooth scaling.
4. **Performance Optimization**
Serving static assets from a separate, cookie-less domain reduces unnecessary network overhead because browsers won't send your main site's cookies with each request for an image. It also leverages browser concurrency for downloads from different domains, speeding up page load times.
---
## Conclusion
The architectural approach proposed by **DP@lib00** emphasizes that physically separating application code from user-generated public data is the cornerstone of building a robust, secure, and scalable modern web application. While keeping all files within a single project directory might seem convenient initially, this convenience comes at the cost of security, maintainability, and future scalability. Adopting the right structure from day one will save you countless hours of refactoring and headaches down the road.
Related Contents
PHP Log Aggregation Performance Tuning: Database vs. Application Layer - The Ultimate Showdown for Millions of Records
Duration: 00:00 | DP | 2026-01-06 08:05:09MySQL TIMESTAMP vs. DATETIME: The Ultimate Showdown on Time Zones, UTC, and Storage
Duration: 00:00 | DP | 2025-12-02 08:31:40The Ultimate 'Connection Refused' Guide: A PHP PDO & Docker Debugging Saga of a Forgotten Port
Duration: 00:00 | DP | 2025-12-03 09:03:20The Ultimate Frontend Guide: Create a Zero-Dependency Dynamic Table of Contents (TOC) with Scroll Spy
Duration: 00:00 | DP | 2025-12-08 11:41:40The Ultimate Guide to CSS Colors: From RGBA to HSL for Beginners
Duration: 00:00 | DP | 2025-12-14 14:51:40Bootstrap 5.3: The Ultimate Guide to Creating Flawless Help Icon Tooltips
Duration: 00:00 | DP | 2025-12-15 03:07:30The Ultimate PHP Guide: How to Correctly Handle and Store Markdown Line Breaks from a Textarea
Duration: 00:00 | DP | 2025-11-20 08:08:00Bootstrap JS Deep Dive: `bootstrap.bundle.js` vs. `bootstrap.js` - Which One Should You Use?
Duration: 00:00 | DP | 2025-11-27 08:08:00Mastering PHP: How to Elegantly Filter an Array by Keys Using Values from Another Array
Duration: 00:00 | DP | 2026-01-14 08:15:29Stop Manual Debugging: A Practical Guide to Automated Testing in PHP MVC & CRUD Applications
Duration: 00:00 | DP | 2025-11-16 16:32:33getElementById vs. querySelector: Which One Should You Use? A Deep Dive into JavaScript DOM Selectors
Duration: 00:00 | DP | 2025-11-17 01:04:07Mastering PHP Switch: How to Handle Multiple Conditions for a Single Case
Duration: 00:00 | DP | 2025-11-17 09:35:40`self::` vs. `static::` in PHP: A Deep Dive into Late Static Binding
Duration: 00:00 | DP | 2025-11-18 02:38:48PHP String Magic: Why `{static::$table}` Fails and 3 Ways to Fix It (Plus Security Tips)
Duration: 00:00 | DP | 2025-11-18 11:10:21Can SHA256 Be "Decrypted"? A Deep Dive into Hash Function Determinism and One-Way Properties
Duration: 00:00 | DP | 2025-11-19 04:13:29The Magic of PHP Enums: Elegantly Convert an Enum to a Key-Value Array with One Line of Code
Duration: 00:00 | DP | 2025-12-16 03:39:10Files Mysteriously Missing in PHPStorm? Check Your Project View First!
Duration: 00:00 | DP | 2026-01-15 08:16:46WebP vs. JPG: Why Is My Image 8x Smaller? A Deep Dive and Practical Guide
Duration: 00:00 | DP | 2025-12-02 08:08:00Recommended
Building a Bulletproof PHP Analytics System: From DB Schema to Self-Healing Cron Jobs
00:00 | 46This article provides a comprehensive walkthrough ...
The Ultimate Guide to Open Source Licenses: From MIT to AGPL and Their Impact on Cloning, Use, and Distribution
00:00 | 9Understanding a project's license is crucial befor...
Missing `autoload.php` in Your PHP Project After Git Clone? A Quick Composer Fix
00:00 | 4Encountering the 'failed to open stream: No such f...
Goodbye OutOfMemoryError: The Ultimate Guide to Streaming MySQL Data with PHP PDO
00:00 | 48Handling large datasets in PHP with the traditiona...