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
- The browser sends the file
- Apache receives it and passes it to PHP (anything larger than
LimitRequestBodyis rejected here) - PHP saves it to a temporary directory (
upload_tmp_dirinphp.ini, or the system temporary directory if that is not set) - 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 -Indexesprevents a file listing from being shown for directories without anindex.html- The
FilesMatchpart denies access to files with the extensions.php,.pharand.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.logon Debian-based systems,/var/log/httpd/error_logon RHEL-based systems). If you seePermission 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
LogLeveltemporarily (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
UPLOADSinwp-config.php(a path relative toABSPATH) - 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_filesizeandpost_max_size
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)