WordPress Security – File Permissions

We’ve been working recently on security, in this particular document we’re focussing on wordpress file and directory permissions.

This is a working document, which should be subject to updates and revisions

Firstly, there is no reason that I can find..that any of the files or directories in a wordpress installation should be world readable, so to that effect we can assume all permissions will end in a “0”, removing any world-readable permissions.

Secondly, we shouldn’t have the web user (apache, http, www-data for example) owning any of the files. In the very least a human should own the files in the web root, and the web process user should be accessing based on group permissions. The reason for this is, the owner can change file permissions as he wishes..which may well be undesirable in the event that a malicious script is executed for example. Additionally, we want the user to be able to write to files..whereas the web server process usually only a needs to *read* files. So in the very first place we’d do About something like this

[plain]chown -R (your username):(web server group) /path/to/webroot/[/plain]

so in the case of my system, I’d perhaps do something like this:

[plain]chown -R dcr226:apache /var/webroot/www[/plain]

So onto actual permissions for these user/group combinations. As stated before, there is no good reason to have anything in the webroot world readable. So from an overarching perspective, files can How be 640 and directories 750. To explain how this works:

Files – 640 permission

Owner has read/write access (in this case a human owner, *not* the web server) (rw)

Group members have read access (in this case the web server’s user – perhaps apache, httpd or www-data) (r)

Everyone else has no access whatsoever (-)

Directories – 750 permission

Basically the same as above, with the exception that directories need to execute in order to change directory, etc. So both the owner and group get execution permissions (rwx)

Fixing the file permissions:

So, now we know what file permissions we want to issue..its time to do the work. This can be done as follows:

[plain]find /path/to/webroot -type d -exec chmod 750 {} ;[/plain]

the above command searches (find) through the defined web root, looking for directories (-type d), then executes chmod 750 on each of them. Should take a few seconds to complete.

[plain]find /path/to/webroot -type f -exec chmod 640 {} ;[/plain]

the above command does the same, but this time for actual files, instead of directories. ‘find’ in this case will recurse through directories.


so it looks like centos6 at least ships with the httpd_unified boolean set. With the boolean on, Apache processes can read/write/execute all httpd_sys_content* labels. This isn’t what we want to achieve.

[plain]setsebool -P httpd_unified 0[/plain]

then, we can create read only selinux labelling for the web root using:

[plain]semanage fcontext -a -t httpd_sys_content_t “/path/to/www(.*)?”

restorecon -Rv www[/plain]

now, apache simply isn’t allowed to write to any files, or execute any files in the webroot.


you’ve got a choice here, you can either manually copy/paste and .htaccess settings into the file yourself, or allow apache to write to the file using the following permissions

[plain]chown dcr226:apache /path/to/www/.htaccess

chmod 660 /path/to/www/.htaccess[/plain]

then set the correct selinux context..

[plain]semanage fcontext -a -t httpd_sys_rw_content_t /path/to/www/.htaccess

restorecon -v /path/to/www/.htaccess[/plain]

some things need write contexts and permissions…

Apache needs to write to the wp-content/ directory in the very least to add new media, themes and plugins, so we need to make that happen with file permissions and a selinux context.

[plain]find /path/to/webroot/wp-content -type d -exec chmod 770 {} ;

find /path/to/webroot/wp-content -type f -exec chmod 660 {} ;

semanage fcontext -a -t httpd_sys_rw_content_t “/path/to/www/wp-content(.*)?”

restorecon -Rv /path/to/www/wp-content[/plain]

Optional – protect xmlrpc.php

unless you’re posting using a desktop/mobile application, or connecting to the xmlrpc api, then you likely want to prevent access to xmlrpc.php. This can be done a couple of ways, one of which is using your httpd.conf (or relevant config file in the drop directory) as such

Inside of your directive


Order allow,deny

Deny from all



When Its Time To Update The Whole Thing

When you need to update wordpress core files, if you want wordpress to update itself, then you’re going to need to give the web process writeable permissions throughout the webroot..

[plain]find /path/to/webroot -type d -exec chmod 770 {} ;

find /path/to/webroot -type f -exec chmod 660 {} ;


and if you’re using selinux..


chcon -R -t httpd_sys_rw_content_t /path/to/webroot


Not forgetting to re-set them again afterwards!

Leave a Reply