2026年11月14日

2026年11月14日

Apache Upload Directory Setup: Permissions, No PHP, WordPress UPLOADS

This article is a localized edition of the Japanese original. Read the Japanese original

When you want to "set where Apache stores uploaded files," the first thing to know is that Apache itself does not save the files it receives. Apache passes the request to an application such as PHP, and the application decides where the file goes. What you do on the Apache side is prepare permissions that allow writing to the destination directory and configure that directory to be served safely.

Using WordPress as the example, this article walks through choosing the destination, permissions, Apache configuration and size limits, in that order (based on official Apache 2.4 and WordPress information as of November 2026).

How an upload flows

  1. The browser sends the file
  2. Apache receives it and passes it to PHP (anything larger than LimitRequestBody is rejected here)
  3. PHP saves it to a temporary directory (upload_tmp_dir in php.ini, or the system temporary directory if that is not set)
  4. The application (WordPress or another) moves it to its real destination with move_uploaded_file()

In other words, the "storage location" is decided by the application's settings, "whether it can be written" by OS permissions, and "how it is served" by the Apache configuration.

Check and change where WordPress stores uploads

By default, WordPress stores uploads in wp-content/uploads. If "Organize my uploads into month- and year-based folders" is checked under "Settings" (設定) → "Media" (メディア) in the admin screen, files are split into year/month folders such as wp-content/uploads/2026/11/.

To change the destination itself, define the UPLOADS constant in wp-config.php. Write the value as a relative path from the WordPress install directory (ABSPATH) (a path starting with / cannot be used). Do not add a leading slash.

<?php
// Put this in wp-config.php above the "That's all, stop editing!" line
define( 'UPLOADS', 'wp-content/media' );

This setting only changes where future uploads are saved. Files already uploaded are not moved, and image URLs written in post content stay as they were. If you also move existing files, you need separate work: copy the files to the new location and replace the URLs in post content (back up the database before you start).

Owner and permissions of the upload directory

The destination must be writable by the user running Apache (and the PHP running under it). That user differs by distribution.

Environment Apache user Where it is set
Debian / Ubuntu www-data /etc/apache2/envvars
RHEL / AlmaLinux / Rocky Linux apache User / Group in /etc/httpd/conf/httpd.conf

If you use PHP-FPM, the user that runs PHP is set in the FPM pool configuration (user / group). Give that user write permission.

Here is an example for Debian / Ubuntu with WordPress in /var/www/html.

sudo mkdir -p /var/www/html/wp-content/uploads
sudo chown -R www-data:www-data /var/www/html/wp-content/uploads
sudo find /var/www/html/wp-content/uploads -type d -exec chmod 755 {} \;
sudo find /var/www/html/wp-content/uploads -type f -exec chmod 644 {} \;

WordPress recommends 755 for directories and 644 for files. Setting 777 just because a directory is not writable lets other users and processes on the same server modify it too, so avoid it.

On systems with SELinux enabled (RHEL-based)

On RHEL, AlmaLinux and Rocky Linux, SELinux is often enabled, and Apache may be unable to write even when the permissions are correct. Give the destination a label that allows writing.

sudo dnf install -y policycoreutils-python-utils
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/wp-content/uploads(/.*)?"
sudo restorecon -Rv /var/www/html/wp-content/uploads
ls -Z /var/www/html/wp-content/uploads

If httpd_sys_rw_content_t appears in the ls -Z output, the label is set. Changes made with chcon are reverted by restorecon, so register the rule with semanage fcontext instead.

Do not let PHP run in the upload directory

The upload directory is a place that outsiders write to, so making sure PHP files are not executed even if one gets placed there limits the damage. Add the following to your Apache 2.4 configuration (the site configuration under /etc/apache2/sites-available/ on Debian-based systems, or a configuration file under /etc/httpd/conf.d/ on RHEL-based systems).

<Directory "/var/www/html/wp-content/uploads">
    Options -Indexes
    <FilesMatch "\.(?i:php|phar|phtml)$">
        Require all denied
    </FilesMatch>
</Directory>
  • Options -Indexes prevents a file listing from being shown for directories without an index.html
  • The FilesMatch part denies access to files with the extensions .php, .phar and .phtml (case-insensitive)

On rental hosting where you cannot edit the server configuration, you can also put the same FilesMatch part in wp-content/uploads/.htaccess, provided AllowOverride permits it.

After making the change, check the syntax and reload.

sudo apachectl configtest
sudo systemctl reload apache2

On RHEL-based systems, use sudo systemctl reload httpd. To confirm, place a test file, access it, check that 403 is returned, then delete it.

echo test | sudo tee /var/www/html/wp-content/uploads/test.php
curl -I http://localhost/wp-content/uploads/test.php
sudo rm /var/www/html/wp-content/uploads/test.php

Upload size limits

When large files cannot be uploaded, check the following settings in turn. The smallest value becomes the limit.

Setting Where Default
LimitRequestBody Apache configuration 1073741824 (1 GiB). 0 (unlimited) in Apache 2.4.53 and earlier
upload_max_filesize php.ini 2M
post_max_size php.ini 8M
max_file_uploads php.ini 20 (number of files that can be sent in one request)

post_max_size is the limit for the whole request, so set it to at least upload_max_filesize. After changing PHP settings, restart Apache if you use mod_php, or the FPM service if you use PHP-FPM. In WordPress, you can confirm the limit after the change from the maximum upload size shown on the screen for adding media.

When it does not work

  • Read the error log (/var/log/apache2/error.log on Debian-based systems, /var/log/httpd/error_log on RHEL-based systems). If you see Permission denied, check the owner, permissions and SELinux
  • If WordPress shows an error saying the uploaded file could not be moved to the destination, check the owner and permissions of the destination
  • If the error log does not show enough detail, raise LogLevel temporarily (How to set Apache LogLevel)

Narrowing down why image uploads fail in WordPress is covered in What to do when images will not upload in WordPress.

Summary

  • The application decides where files are stored; Apache handles passing them along and serving them
  • In WordPress, you can change the destination with UPLOADS in wp-config.php (a path relative to ABSPATH)
  • Make the destination writable by the Apache and PHP user and avoid 777. On RHEL-based systems, also set the SELinux label
  • Do not let PHP run in the upload directory, and do not show file listings
  • The size limit is the smallest of LimitRequestBody, upload_max_filesize and post_max_size

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲