Stop Mixing Code and User Uploads! The Ultimate Guide to a Secure and Scalable PHP MVC Project Structure

Published: 2026-01-13
Author: DP
Views: 11
Category: PHP
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