Vincent Zhang

Engineering, Design & Productivity

How to fix “The uploaded file could not be moved to wp-content/uploads/” in WordPress

If you use WordPress, you may have encountered this problem: you try to upload a new file and it throws this error message at you. This is also why you could not click the button in the dashboard to upgrade theme, plugin or WordPress versions.

In this article, let’s go through the problem’s cause and solution once and for all.

Why does it happen?

TL;DR: Your web server’s file ownership isn’t set up correctly. The web server process that takes browser requests does not have file ownership to your WordPress directory.

The wrong way to fix it

A common suggestion that you’ll find online regarding this problem is to change everything under your WordPress upload folder (for example wp-contents/uploads) to file permission mode 777 using chmod. This means anyone can read, write and execute files under that folder.

This is a horrible idea. Yes, this may temporarily mitigate the problem, but it does so by creating a huge security concern in your server. It’s never a good idea to give more than necessary permissions to anyone, especially the execute file permission, which opens up malicious injection possibilities that may cause server downtime or data loss.

The right way to fix it

The right way to fix this problem is to assign proper file ownership to the correct server processes.

Let’s start by log into your server. Identify the server software that powers your WordPress – this may be Apache (a common choice), or in my case, NGINX. We would like to search for any process instances running under the Unix’s process status (`ps`) panel:

root@li1007-15:~# ps aux | grep nginx
root      6246  0.0  0.0  76312   132 ?        Ss   Mar18   0:00 nginx: master process /usr/bin/openresty -g daemon off;
www-data  6312  0.0  0.1  78416  1412 ?        S    Mar18   4:14 nginx: worker process
root     13920  0.0  0.0  76312   364 ?        Ss   Mar19   0:00 nginx: master process /usr/bin/openresty -g daemon off;
www-data 13985  0.0  0.1  78436  1508 ?        S    Mar19   9:30 nginx: worker process
systemd+ 13997  0.0  0.1  34084  1548 ?        S    Mar19  16:55 nginx: worker process
root     22093  0.0  0.0  14428   936 pts/0    S+   16:38   0:00 grep --color=auto nginx
root     23776  0.0  0.0  17956     8 ?        Ss   Mar17   0:00 /bin/bash -c source ".profile" 2>/dev/null; docker-gen -watch -notify "nginx -s reload" /app/nginx.tmpl /etc/nginx/conf.d/default.conf
root     23777  0.0  0.0  17948     0 ?        Ss   Mar17   0:00 /bin/bash -c source ".profile" 2>/dev/null; nginx
root     23778  0.1  0.1  14552  1708 ?        Sl   Mar17 117:33 docker-gen -watch -notify nginx -s reload /app/nginx.tmpl /etc/nginx/conf.d/default.conf
root     23780  0.0  0.0  33596   216 ?        S    Mar17   0:00 nginx: master process nginx
root     31964  0.0  0.0  76312   156 ?        Ss   Mar18   0:00 nginx: master process /usr/bin/openresty -g daemon off;
www-data 32029  0.0  0.1  78436  1364 ?        S    Mar18   1:46 nginx: worker process
root     32584  0.0  0.0  76312    32 ?        Ss   Mar17   0:00 nginx: master process /usr/bin/openresty -g daemon off;
www-data 32650  0.0  0.0  78460   824 ?        S    Mar17   0:16 nginx: worker process

The first column is the user owning that specific process. The first row indicates that the NGINX master process is owned by the root user. This is good, because in my case, root user is already the owner of the WordPress directory.

The web server’s master process is also responsible for spawning many worker (child) processes for load balancing. The culprit is that these web server worker processes that actually handle requests from the browser belong to a different user than root: in my case, www-data, which does not have access to your WordPress directory.

Let’s change the ownership of your WordPress directory to the worker process user:

chown -R www-data /path/to/your/wordpress/directory

There is no need to restart the server since the alterations are applied to the file system only. Simply reload the WordPress dashboard in your browser and try uploading files again. This problem should be resolved.

Leave a comment