October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk9 min

How to Secure the WordPress Uploads Directory Without Breaking Media

Block PHP execution in WordPress uploads while keeping images, PDFs and other media available. Covers Nginx, Apache/LiteSpeed, Multisite, testing and permissions.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Block script execution in /wp-content/uploads at the web-server layer while leaving the directory readable for images, documents and other media. On Nginx, add a location rule to the server configuration; on Apache or LiteSpeed, use a .htaccess or virtual-host rule. Then test that a harmless PHP file is denied and that normal media still loads.

What the uploads directory must do—and what it must never do

WordPress normally stores uploaded media below /wp-content/uploads, often in year-and-month subdirectories. Browsers need to request files from this location, so making the entire directory inaccessible will break image previews, downloads, PDF links and other front-end content. WordPress also needs the PHP process to write new uploads there.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The dangerous capability is different: an attacker who places a PHP file in a web-accessible directory may try to call it over HTTP and have the server execute it. The control you want is therefore selective: allow ordinary media to be served, but make PHP files in the uploads tree non-executable. A denied request should return an error such as 403 or 404; it must not produce PHP output.

Choose the rule for your web server

Server Where the rule belongs Can ordinary media remain public? Multisite consideration Access required
Nginx The relevant server configuration, usually inside or alongside the WordPress server block Yes. The rule targets PHP requests only Cover both uploads and the legacy files path when your network uses it Permission to edit server configuration and reload Nginx
Apache or LiteSpeed An uploads-directory .htaccess, or the applicable virtual-host configuration Yes. A FilesMatch rule can deny PHP while leaving other extensions alone Check the actual upload and rewrite paths generated for the network AllowOverride access, or virtual-host access if .htaccess is disabled
IIS The site’s IIS configuration, commonly a web.config policy Depends on the handler and request-filtering configuration Use the network’s real upload paths IIS configuration access or host assistance

Nginx does not read directory-level .htaccess files. WordPress’s Advanced Administration Handbook states: “With Nginx there is no directory-level configuration file like Apache’s .htaccess or IIS’s web.config files.” Adding an Apache snippet to an Nginx site therefore has no effect.

Before you edit anything

1. Identify the real document root

Do not assume the site is installed directly under the first directory you see in a file manager. Confirm the virtual host’s document root, the WordPress installation directory and the absolute path that maps to /wp-content/uploads. A reverse proxy, symlink, container volume or separate media mount can change the mapping.

2. Confirm how uploads are stored

Standard installations use wp-content/uploads. A multisite network can use a files path and special rewrite behavior, and a plugin or host may store media outside the default directory. Inspect an actual image URL and the filesystem location behind it before applying a blanket rule.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

3. Establish administrative access and a rollback

  • For Nginx, obtain permission to edit the active server block and reload the service.
  • For Apache or LiteSpeed, determine whether directory overrides are enabled. If they are not, place the rule in the virtual-host configuration or ask the host to deploy it.
  • Back up the configuration file and record the original version. A syntax error can make the site unavailable until the server is reloaded with a corrected file.
  • Use a staging site when possible, but repeat the final check on production because hosts often use different handlers or caching layers.

Nginx: deny PHP requests in uploads

Recommended location rule

Add this rule to the Nginx configuration that serves the WordPress site:

location ~* /(?:uploads|files)/.*.php$ {
    deny all;
}

The case-insensitive expression matches a request containing an uploads or files directory followed by a PHP filename. Including files covers the path used by some WordPress Multisite configurations; if your network has a different custom path, add a specifically reviewed location for that path rather than guessing.

Deploy it safely

  1. Find the active Nginx server block for the domain. Put the location at server level, not inside a PHP-FPM-only location where it may be shadowed by another match.
  2. Save the file and run the host’s configuration test command, commonly nginx -t, before reloading. If your provider does not expose shell access, use its configuration validation control.
  3. Reload Nginx using the host’s documented method. A reload applies the new configuration without intentionally stopping existing connections.
  4. Create the harmless test file described in the verification section, request it over HTTPS and record the response.
  5. Request representative JPG, PNG, PDF and other files from the same uploads tree. Remove the test file immediately after the checks.

Common Nginx mistakes

  • Putting the rule in .htaccess: Nginx ignores that file, so PHP remains executable.
  • Using a regex that matches only the root directory: Uploads are normally nested in year/month folders. The expression must cover descendants.
  • Editing an inactive server block: A staging, IPv6 or alternate virtual host may be serving the request you tested. Confirm the active configuration and hostname.
  • Forgetting a custom or Multisite path: Test URLs generated by every site in the network, not just the main site.

Apache or LiteSpeed: use a directory rule

Uploads-directory .htaccess

When the host permits per-directory overrides, place the following in the uploads directory’s .htaccess file:

<FilesMatch ".php$">
    Require all denied
</FilesMatch>

This Apache 2.4-style directive denies requests for files whose names end in .php while leaving images, documents and other extensions available. LiteSpeed generally honors Apache-compatible directives, but confirm the exact supported syntax with the host.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Putting the rule in the main WordPress .htaccess

If you administer the site’s root .htaccess instead, keep custom directives outside WordPress’s generated # BEGIN WordPress and # END WordPress block. WordPress can regenerate that section when permalink settings change, potentially removing edits placed inside it. A separate uploads-directory file is easier to audit and less likely to be overwritten.

When the rule appears to do nothing

  • Overrides are disabled: The server may ignore .htaccess entirely. Ask the host to enable the required override class or install the rule in the virtual host.
  • The request is handled by a different vhost: Verify the domain, document root and HTTPS virtual host you actually tested.
  • PHP is served through an unusual handler: A host-specific handler or proxy may need a server-level deny rule rather than FilesMatch.
  • A rewrite changes the path: Multisite rewrites can map a friendly URL to another filesystem location. Test the real URL and inspect the resulting server logs with your host.

Verify the protection with a controlled test

A configuration that looks correct is not proof that execution is blocked. Use a disposable file containing plain text only; do not place credentials, shell commands or exploit code in it.

  1. Identify the exact uploads directory used by the site.
  2. Create security-test.php in that directory with a single harmless line such as upload-directory-test. It must not contain a PHP opening tag or executable code.
  3. Construct the corresponding HTTPS URL, including any year/month subdirectory, and request it in a private browser window or with an HTTP client.
  4. Confirm the server returns a denial response, normally HTTP 403 or 404. A page showing the plain text file is not sufficient evidence of safety if the file was not parsed; your host should confirm that PHP requests are rejected before reaching the PHP handler.
  5. Request ordinary files from the same directory: at minimum a JPG or PNG, a PDF if the site uses documents, and one representative media type used by your editors. They must continue to load or download normally.
  6. Delete security-test.php and verify that it no longer responds. Leaving a test file in a public directory creates unnecessary clutter and can confuse later audits.
  7. Repeat the check after changes to PHP, the web server, the hosting stack, a reverse proxy or a caching layer. Configuration reloads and migrations can silently replace local rules.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Permissions and ownership: prevent uploads from becoming an attack surface

Execution blocking is the highest-value control for this directory, but it does not make unsafe filesystem permissions acceptable. Avoid world-writable 777 permissions. WordPress hardening guidance recommends least privilege because every additional writer increases the chance that an unwanted file can be placed there.

  • Give the web process only the write access WordPress actually needs. Keep core files, themes and plugins non-writable by the web process where the operating model permits.
  • Ensure directory ownership and group membership match the host’s documented PHP and web-server users. Incorrect ownership often leads administrators to grant broad permissions as a quick fix.
  • Inspect uploads for unexpected executable extensions, double extensions and recently created files. A deny rule stops PHP execution, but unexplained files can reveal a compromised account or plugin.
  • Keep WordPress core, themes and plugins updated, and remove extensions that are no longer used. Vulnerable code elsewhere can still provide a route to write files or alter configuration.
  • Consider setting DISALLOW_FILE_EDIT in wp-config.php to disable the dashboard’s built-in theme and plugin editor when your maintenance process does not require it.
  • Run the site and web server as non-privileged users where the hosting model allows it. Separating service accounts limits the damage from a compromised process.
  • Use a security plugin, reverse-proxy web application firewall or hosting-level firewall as an additional layer. These controls supplement, rather than replace, the uploads execution rule.

Multisite and custom-storage checks

Do not copy a single-site rule across a network without checking each URL pattern. WordPress documents separate rewrite and server-configuration models for Multisite, and older network setups may expose a files path that is not present on a single site. The Nginx example above includes both names because a network can need coverage for both.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Custom media plugins may store files on another local path or on object storage. A rule under wp-content/uploads cannot protect a different directory. Locate the physical storage and the public URL mapping, then apply an equivalent execution-denial policy at the server or storage layer. If media is delivered from a separate hostname, test that hostname independently.

When a network has several site administrators, document the approved upload locations and prohibit ad-hoc writable directories. A new plugin that creates its own public folder should trigger the same review: can it write there, can the web server execute anything there, and do legitimate assets still load?

Troubleshooting by symptom

A PHP test file still executes

  • Confirm the request reaches the server whose configuration you edited; DNS, a CDN or a reverse proxy may point elsewhere.
  • Check that the rule covers the complete URL path, including nested year/month directories and any Multisite files segment.
  • On Nginx, validate and reload the active configuration. On Apache, verify that .htaccess overrides are enabled or move the rule to the virtual host.
  • Inspect server logs with the host to determine whether the request was handled by PHP-FPM, another handler or a different location block.

Images or PDFs return 403 after the change

  • Check that the rule matches only PHP filenames. A broad deny on the whole directory will block legitimate media.
  • Look for a conflicting parent-directory rule, hotlink policy, access-control list or WAF rule.
  • Test a direct URL without a browser cache and compare the response from the origin server if a proxy is involved.
  • Restore the last known-good configuration if the site is impaired, then narrow the match and retest with one file at a time.

WordPress cannot upload new media

  • Check directory ownership and write permissions for the PHP process; execution denial should not remove write access.
  • Review disk quotas, inode limits and temporary-directory settings.
  • Confirm that a custom storage plugin still points to an existing path and that its URL mapping was not accidentally included in an access-deny rule.

The rule disappears later

  • Look for a WordPress permalink update that regenerated the main .htaccess block.
  • Check host provisioning, container rebuilds, staging-to-production deployments and server-image updates.
  • Store the rule in configuration management or the host’s documented persistent customization mechanism, and include the functional test in post-change checks.

Operational checklist

  • Identify the real document root, upload path and public URL.
  • Classify the server as Nginx, Apache/LiteSpeed or IIS.
  • Check for Multisite, custom storage and separate media domains.
  • Deploy a server-layer rule that denies PHP execution only in the relevant upload locations.
  • Validate syntax before reloading the server.
  • Request a harmless PHP test file and confirm a 403 or 404.
  • Load representative images, documents and other required media.
  • Delete the test file and inspect the directory for unexpected executables.
  • Review least-privilege permissions, ownership, updates and dashboard file editing.
  • Repeat the functional test after hosting, PHP, web-server, proxy or caching changes.

The practical security boundary

The uploads directory should be publicly readable for the media your site publishes and writable only by the processes that need to upload files. It should not be a place where the web server executes PHP. Enforcing that boundary in the server configuration, then proving it with a denied test request and successful media requests, protects the site without sacrificing its publishing workflow.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.