Giải pháp CMS WordPress dùng chung code cho nhiều website (multi-domain nhưng không dùng Multisite). Nội dung sẽ có:

  1. Vấn đề gặp phải
  2. Giải pháp với define('UPLOADS')
  3. Giải pháp dùng Symlink (liên kết mềm)
  4. Các rủi ro & cách xử lý
  5. Demo cấu hình Nginx / Apache để map thư mục upload riêng cho từng domain

1. Vấn đề khi dùng chung code WordPress cho nhiều domain

  • Bạn có 1 CMS WordPress duy nhất đặt ở /var/www/cms/.
  • Các website site1.com, site2.com, site3.com cùng trỏ về thư mục code này.
  • Nếu giữ mặc định, tất cả media sẽ được upload vào chung thư mục wp-content/uploads/.
    ➡ Nhược điểm: hình ảnh/video của các website bị trộn lẫn.

2. Giải pháp 1 – Dùng define('UPLOADS', ...)

Trong wp-config.php có thể thêm dòng sau:

define('UPLOADS', 'uploads/' . $_SERVER['HTTP_HOST']);

📌 Ý nghĩa:

  • Khi truy cập site1.com, file sẽ lưu ở:
    /var/www/cms/uploads/site1.com/
  • Khi truy cập site2.com, file sẽ lưu ở:
    /var/www/cms/uploads/site2.com/

👍 Ưu điểm:

  • Cách này dễ triển khai, chỉ cần 1 dòng config.
  • Tách biệt file upload của từng website.

👎 Nhược điểm:

  • Một số plugin có thể hardcode đường dẫn upload → dễ bị lỗi.
  • Backup / migrate cần chú ý mapping domain → folder.
  • Nếu đổi domain (ví dụ abc.comxyz.com) thì media path sẽ lệch.

3. Giải pháp 2 – Dùng Symlink

Bạn có thể tạo thư mục riêng cho từng site ở ngoài code, ví dụ:

/var/www/uploads/site1.com/
/var/www/uploads/site2.com/
/var/www/uploads/site3.com/

Sau đó trong wp-content/ của CMS, tạo symlink:

cd /var/www/cms/wp-content
ln -s /var/www/uploads/site1.com uploads/site1.com
ln -s /var/www/uploads/site2.com uploads/site2.com
ln -s /var/www/uploads/site3.com uploads/site3.com

📌 Với symlink:

  • Code vẫn trỏ tới wp-content/uploads/site1.com/...
  • Nhưng thực tế file nằm ở /var/www/uploads/site1.com/.
  • Có thể mount riêng theo ổ đĩa, dễ backup từng site.

👍 Ưu điểm:

  • Linh hoạt, có thể mount trên ổ cứng khác.
  • Không phụ thuộc vào $_SERVER['HTTP_HOST'].
  • Dễ migrate site riêng lẻ.

👎 Nhược điểm:

  • Cần quản lý symlink thủ công (hoặc viết script auto).
  • Nếu cấu hình sai permission, web có thể không ghi được file.

4. Rủi ro & Lưu ý bảo mật

  • Quyền ghi file:
    Nên để owner là www-data:www-data (nếu dùng Nginx/Apache chạy dưới user đó).
  • Bảo mật thư mục uploads:
    Dù không public link symlink, nhưng nếu web cho phép upload .php có thể bị khai thác.
    → Giải pháp: chặn thực thi .php trong thư mục uploads/.

Ví dụ với Nginx:

location ~* /uploads/.*\.php$ {
    deny all;
}

Ví dụ với Apache (.htaccess trong uploads):

<FilesMatch "\.php$">
    Deny from all
</FilesMatch>

5. Demo cấu hình Nginx / Apache

🔹 Nginx (multi-domain, mỗi domain có thư mục uploads riêng)

server {
    server_name site1.com;
    root /var/www/cms;

    location / {
        index index.php;
        try_files $uri $uri/ /index.php?$args;
    }

    # Media riêng cho site1
    location /uploads/ {
        alias /var/www/uploads/site1.com/;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

server {
    server_name site2.com;
    root /var/www/cms;

    location / {
        index index.php;
        try_files $uri $uri/ /index.php?$args;
    }

    location /uploads/ {
        alias /var/www/uploads/site2.com/;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

🔹 Apache (dùng VirtualHost)

<VirtualHost *:80>
    ServerName site1.com
    DocumentRoot /var/www/cms

    <Directory /var/www/cms>
        AllowOverride All
        Require all granted
    </Directory>

    # Alias cho uploads
    Alias /uploads/ /var/www/uploads/site1.com/

    <Directory /var/www/uploads/site1.com>
        Options -Indexes
        AllowOverride None
        Require all granted
    </Directory>
</VirtualHost>

<VirtualHost *:80>
    ServerName site2.com
    DocumentRoot /var/www/cms

    <Directory /var/www/cms>
        AllowOverride All
        Require all granted
    </Directory>

    Alias /uploads/ /var/www/uploads/site2.com/

    <Directory /var/www/uploads/site2.com>
        Options -Indexes
        AllowOverride None
        Require all granted
    </Directory>
</VirtualHost>

👉 Kết luận:

  • Nếu bạn muốn nhanh gọn → dùng define('UPLOADS').
  • Nếu bạn muốn chuyên nghiệp, backup dễ, an toàn hơn → dùng Symlink hoặc Alias trong Nginx/Apache.