WordPress hardening: how to change and protect the uploads directory
Back to blog

WordPress hardening: how to change and protect the uploads directory

6/7/2026 · 3 min · WordPress

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.

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.

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments