Back to blog

WordPress hardening: how to change and protect the uploads directory

3/8/2026 · 3 min · Cybersecurity

Share

🛡️ WordPress hardening: how to change and protect the uploads directory

The default WordPress uploads path (/wp-content/uploads/) is one of the first targets in automated attacks. Recon bots scan this predictable location for forgotten configs, plugin backups, and script execution vectors (RCE).

In this guide, I document the practical workflow I used to move the directory to a custom location and apply additional server-layer hardening (Apache/Nginx) so malicious code cannot be executed even if a bad file is uploaded.

The goal is straightforward: reduce attacker predictability, preserve integrity, and keep service availability under pressure.

---

1. Infrastructure planning and preparation

Before touching code, prepare filesystem structure correctly.

📁 Create the new directory

Connect through SSH or FTP and create the target folder. From a security standpoint, avoid names containing "uploads" or "media".

mkdir -p /public_html/storage_core/data/

🔑 Permission model (Linux)

The web service user (www-data, apache, or cPanel account user) must have write permission.

chmod 755 /public_html/storage_core/data/

If needed, fix ownership:

chown -R accountuser:accountuser /public_html/storage_core/data/

---

2. WordPress core configuration

Update uploads behavior in wp-config.php.

  1. Open wp-config.php in site root.
  2. Insert this line before:

/ That's all, stop editing! Happy publishing. /

// Define new uploads path relative to WordPress root
define('UPLOADS', 'storage_core/data');
Technical note: UPLOADS must be relative to ABSPATH, without leading slash.

After saving, perform a test upload in Media Library to validate write behavior.

---

3. Data migration and database integrity

🚚 Move existing files

For sites with existing media, migrate old files:

rsync -avh --progress /public_html/wp-content/uploads/ /public_html/storage_core/data/

🗄️ Search and replace (URL integrity fix)

This is the most missed step in real migrations: old media URLs remain stored in database fields (wp_posts, wp_postmeta).

WP-CLI approach:

wp search-replace 'wp-content/uploads/' 'storage_core/data/' --all-tables --precise --report-changed-only

Manual SQL fallback:

UPDATE wp_posts
SET post_content = REPLACE(post_content, 'wp-content/uploads/', 'storage_core/data/');

UPDATE wp_postmeta
SET meta_value = REPLACE(meta_value, 'wp-content/uploads/', 'storage_core/data/');
Production practice: always take a full backup before any global replace.

---

4. Server hardening: block script execution

Moving path improves obscurity, but does not stop .php execution attempts.

Apache (.htaccess inside new directory)

Create /public_html/storage_core/data/.htaccess:

# Block PHP execution in media directory
<Files *.php>
    deny from all
</Files>

Nginx (server block)

location /storage_core/data/ {
    location ~ \.php$ {
        deny all;
    }
}

In hybrid stacks (Nginx proxy + Apache backend), enforce blocking in both layers to avoid internal-route bypass.

---

5. Operational post-hardening validation

After migration and hardening rules, I run a strict validation cycle:

  1. New uploads from admin panel (/wp-admin/upload.php) write into new path.
  2. Existing media in old posts still renders correctly.
  3. Script execution attempt is blocked.

curl verification:

curl -I https://domain.tld/storage_core/data/test-image.webp
curl -I https://domain.tld/storage_core/data/probe.php

Expected result:

I also review error logs to confirm no silent breakage on gallery/CDN plugins.

---

6. Common field failures and fixes

Failure A: new images fail to upload

Likely cause:

Fix:

  1. Revalidate chmod/chown.
  2. Check open_basedir in php.ini or FPM pool.

Failure B: old media links break

Likely cause:

Fix:

  1. Re-run search-replace with report.
  2. Purge app and CDN cache.

Failure C: direct origin IP still exposes uploads

Likely cause:

Fix:

  1. Enforce local Apache/Nginx blocking.
  2. If reverse proxy is used, restrict origin access to trusted proxy ranges.

---

🏁 Strategic conclusion

By changing the default directory and blocking script execution, you remove your WordPress from the common recon path used by generic botnets and reduce damage from automated upload payloads.

Final checklist:

This is the operational discipline that separates fragile deployments from professional, resilient infrastructure.

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.