What are the recommended directory permissions?

  • I'm gearing up to deploy a Drupal 7 site and I can't find any documentation about what the recommended security-conscious file and directory permissions should be set to.

    Specifically default/files/ (also sub directories?), settings.php, .htaccess and anything else I should be aware of.

    There is a blog that explains this beautifully and covers every aspect of it in an abstract way. https://technologymythbuster.blogspot.com/2018/06/misconception-about-file-ownerships-and.html

  • Paul Jones

    Paul Jones Correct answer

    10 years ago

    Your web server should be able to read all of the files but not write to them. If your site involves uploading files then give the server permission to write to that one folder only.

    More information on how to set that up, as well as some things that can happen if you don't, is available in the Drupal docs.

    This is not true of the sites/default/files directory, which must be writable by the web server or you will have masses of troubles.

    @rooby Second sentence clarifies that.

    Although it says "If your site involves uploading files" but that is not the only reason you might need write access to the files directory. A very common example is js/css aggregation, which is unrelated users uploading files. Other modules may also expect that directory to be writeable.

  • That drupal page like so many is very long and confusing. But it contains this post by Jason, who hit the nail on the head:

    Posted by Jason Sale on November 1, 2010 at 12:40pm

    Thanks for writing this and everything, but all that I and 99% of people reading this page really want is a list of numbers next to a list of folders.

    • /default on 755
    • /default/files including all subfolders and files on 744 (or 755)
    • /default/themes including all subfolders and files on 755
    • /default/modules including all subfolders and files on 755
    • /default/settings.php and /default/default.settings.php on 444

    The topic is long and confusing. The page fits the reality of the situation.

    ... of the Drupal docs! And that's why it needs to be changed to something simple & understandable immediately! Especially when it comes to security. JUST BECAUSE it's about security! Because it belongs to all of us. An infected server is not only a problem of the unexperienced Drupal user. It's a problem of all of us then. So it isn't very helpful to keep up complex & badly written docs inspired by developers narcissm to show off complexity & intricacy and how good theirself can handle it. I don't see the point for leaving out a simple file permissions tree graphic. webchick aggrees on that.

    Why have 755 on /default/files? Shouldn't this always be 744 just in case someone manages to upload something malicious by hacking the front end (or through poor configuration)? Is there any reason to have 755? What about /tmp?

    The settings.php file should be 440. "Others" should not be able to see settings.php. 740 if you want to be able to write to it without sudo commands.

    just curious why .htaccess file in files and tmp folder are need to be written by webserver? isn't it safe to mark it 444 just as settings.php. Reason I am asking is if junkfile.php can be uploaded by hacker to files folder via webserver then .htaccess file could be replaced. am I right?

    @kiranking .htaccess is changed by some core updates so it needs to be writable by the webserver if you want/need to do it in the drupal backend

  • My practice around creating a new Drupal site on a server is to have a user that is a part of the web server (typically Apache) group, and have that user own all the Drupal files. On Ubuntu, these are the commands to get that set up:

    # Create a new example user, setting up /var/www/example as their home dir.
    useradd -s /bin/bash -d /var/www/example -m example
    # Now add that user to the Apache group. On Ubuntu/Debian this group is usually
    # called www-data, on CentOS it's usually apache.
    usermod -a -G www-data example
    # Set up a password for this user.
    passwd example

    Once I have that set up, I'll log in as that user and install Drupal at /var/www/example/docroot or similar, and then create the files directory by hand and copy over the settings.php file. Since we log in as our example user before copying in Drupal, our file ownership and permissions should automatically be properly configured on all the core Drupal files and scripts (including .htaccess files).

    su - example
    cd docroot
    cp sites/default/default.settings.php sites/default/settings.php
    # Temporarily give the web server write permissions to settings.php
    chgrp www-data sites/default/settings.php
    chmod g+w sites/default/settings.php

    Now let's set up the files directory.

    # Create the directory.
    mkdir sites/default/files
    # Now set the group to the Apache group. -R means recursive, and -v means 
    # verbose mode.
    chgrp -Rv www-data sites/default/files

    Next we'll set up permissions so that the web server can always write to any file that is in this directory. We do this by using 2775 in our chmod command. The 2 means that the group id will be preserved for any new files created in this directory. What that means is that www--data will always be the group on any files, thereby ensuring that web server and the user will both always have write permissions to any new files that are placed in this directory. The first 7 means that the owner (example) can R (Read) W (Write) and X (Execute) any files in here. The second 7 means that group (www-data) can also R W and X any files in this directory. Finally, the 5 means that other users can R and X files, but not write.

     chmod 2775 sites/default/files

    If there are any existing files in this directory, be sure the web server has write perms on them.

     chmod g+w -R sites/default/files

    Now Drupal is ready to be installed. When finished, it is VERY important to come back to settings.php and ensure that all users only have read permissions.

     chmod 444 sites/default/settings.php

    That's it! This set up ensures you avoid any situations where either the user that owns the directory or the web server can't write/change/remove files in the files directory.

    isn't it safe to set .htacess to 444 jsut as settings.php? anyway that file is rarely updated in drupal core.

    You can set .htaccess to 444, but it will add extra headache when upgrading Drupal core and doesn't make your site any more secure.

  • The Drupal files folder should be writable by the webserver. The safest way to do that is to change the group and make it group writable, like this:

    chgrp www-data sites/default/files
    chmod g+w sites/default/files

    The file upload folder aside, the safest is chmod 644 for all files, 755 for directories.

    This could be accomplished like this (when run in the Drupal-site folder, the . is for current path):

    find . -type f | xargs chmod 644
    find . -type d | xargs chmod 755

    Remember that you will need to set chmod g+w again after running the above command, since those will reset the chmod on all files and folders.

    This answer assumes: you are on ubuntu. Your site is the only one running on that server. It also only solves the problem once instead of making it happen automatically (e.g. by changing the umask).

    Not necessarily Ubuntu, and it's still a helpful measure, even if there's multiple sites running on the same server. Changing the umask (which should hopefully already have these settings) is not something I'd recommend to people not well-versed in Linux. I know this is not perfect security, but let's not let perfect be the enemy of good here.

    On a multisite I run `chgrp -R www-data sites/*/files` and `chmod -R g+w sites/*/files` to get rid of the status page errors.

  • Any advice to "chmod blah" or "chown X" is meaningless without knowing: what the default user:group is on the files and what user and groups your webserver runs as.

    The Drupal Docs others have linked to are quite good on the topic but one other resource is the Security Review Module which helps ensure you've got everything set properly.

  • I will reply considering the case the files are created on the server using FTP, using credentials different from the one under which is the web server runs (normally, Apache runs as nobody/nobody). This means that the user who owns the files manually created before running the Drupal installer (which includes also the files uploaded on the server from the Drupal archive) is not the user used to run the web server (neither the username or the group matches). This scenario applies also to the case where those files are created using SSH.

    • The settings.php file must be writable from the Drupal installer, but once the installation is done it is suggested to make it read-only (the installer suggest that, and Drupal will periodically check if the file is really read-only). In the scenario I am describing, the permission of this file should at least be 644.
    • The .htaccess files (which are present in at least two places) should have the permission 644. The user who created the file should still be able to overwrite the file, in the case a next version of Drupal comes with an .htaccess file that has been updated (it happened already once, when it has been added a line to that file to avoid a security issue). It is also possible to set the permissions to 444, but in that case, the permissions should be changed back to 644 when the file needs to be updated.
    • The directory containing the files created by the modules (the default/files directory) must be (for the user that is assigned to the web server processes, which is then the user assigned to to the PHP scripts running on that web server):
      • readable
      • writable
      • traversable (the modules must be able to reach default/files/<directory-used-by-the-module>/<sub-directory-used-by-the-module>)
  • Recommended file/directory permissions:

    • Drupal webroot should be world readable (see: updater.inc): 0755
    • for public upload directories: 0755 or 0775
    • for private upload directories: 0750 or 0770
    • for public uploaded files: 0644 or 0664
    • for private uploaded files: 0640 or 0660
    • for .htaccess within upload directories (see: file_create_htaccess()): 0444 (default) or 0644
    • for settings.php read-only for all (and other confidential files): 0440
    • for all other web directories: 0755
    • for all other web files: 0644

    Recommended file/directory ownership:

    • owner of all upload directories/files should be set to Apache user,
    • owner of all web/sources directories/files should be set to non-Apache user,
    • (optionally) group of all sources should be set to Apache group,

    Here are the variables which control default dir/file permissions for new items:

    file_chmod_directory: 0775
    file_chmod_file: 0664

    Here is some script for fixing permissions: fix-permissions.sh

    Read more:

    Here is script which I'm using to fix permissions on remote host for public/private directories:

    #!/bin/sh -e
    # Script to correct public/private directory and files permissions.
    [ -z "$1" ] && { echo Usage: $0 @remote.dst; exit 1; }
    DST="$1" && shift
    GET_HTTP_GROUP='ps axo user,group,comm | egrep "(apache|httpd)" | grep -v ^root | uniq | cut -d\  -f 1'
    drush $* $DST ssh 'PUB=$(drush dd %files) && PRIV=$(drush dd %private) && AGROUP=$('"$GET_HTTP_GROUP"') && chgrp -vR $AGROUP $PUB $PRIV && chmod -vR u+rwX,g+rwX,o+rX $PUB $PRIV'

    Note: Above code will try to retrieve Apache group and set it to GET_HTTP_GROUP variable.

    very nice cheat-sheet , bookmarked. Good job..

  • This shell script is found at the bottom of this page: https://www.drupal.org/node/244924

    I run it occasionally to ensure that my permissions are set up correctly.

    # Help menu
    print_help() {
    cat <<-HELP
    This script is used to fix permissions of a Drupal installation
    you need to provide the following arguments:
    1) Path to your Drupal installation.
    2) Username of the user that you want to give files/directories ownership.
    3) HTTPD group name (defaults to www-data for Apache).
    Usage: (sudo) bash ${0##*/} --drupal_path=PATH --drupal_user=USER --httpd_group=GROUP
    Example: (sudo) bash ${0##*/} --drupal_path=/usr/local/apache2/htdocs --drupal_user=john --httpd_group=www-data
    exit 0
    if [ $(id -u) != 0 ]; then
      printf "**************************************\n"
      printf "* Error: You must run this with sudo. *\n"
      printf "**************************************\n"
      exit 1
    # Parse Command Line Arguments
    while [ $# -gt 0 ]; do
      case "$1" in
        --help) print_help;;
          printf "***********************************************************\n"
          printf "* Error: Invalid argument, run --help for valid arguments. *\n"
          printf "***********************************************************\n"
          exit 1
    if [ -z "${drupal_path}" ] || [ ! -d "${drupal_path}/sites" ] || [ ! -f "${drupal_path}/core/modules/system/system.module" ] && [ ! -f "${drupal_path}/modules/system/system.module" ]; then
      printf "*********************************************\n"
      printf "* Error: Please provide a valid Drupal path. *\n"
      printf "*********************************************\n"
      exit 1
    if [ -z "${drupal_user}" ] || [[ $(id -un "${drupal_user}" 2> /dev/null) != "${drupal_user}" ]]; then
      printf "*************************************\n"
      printf "* Error: Please provide a valid user. *\n"
      printf "*************************************\n"
      exit 1
    cd $drupal_path
    printf "Changing ownership of all contents of "${drupal_path}":\n user => "${drupal_user}" \t group => "${httpd_group}"\n"
    chown -R ${drupal_user}:${httpd_group} .
    printf "Changing permissions of all directories inside "${drupal_path}" to "rwxr-x---"...\n"
    find . -type d -exec chmod u=rwx,g=rx,o= '{}' \;
    printf "Changing permissions of all files inside "${drupal_path}" to "rw-r-----"...\n"
    find . -type f -exec chmod u=rw,g=r,o= '{}' \;
    printf "Changing permissions of "files" directories in "${drupal_path}/sites" to "rwxrwx---"...\n"
    cd sites
    find . -type d -name files -exec chmod ug=rwx,o= '{}' \;
    printf "Changing permissions of all files inside all "files" directories in "${drupal_path}/sites" to "rw-rw----"...\n"
    printf "Changing permissions of all directories inside all "files" directories in "${drupal_path}/sites" to "rwxrwx---"...\n"
    for x in ./*/files; do
        find ${x} -type d -exec chmod ug=rwx,o= '{}' \;
        find ${x} -type f -exec chmod ug=rw,o= '{}' \;
    echo "Done setting proper permissions on files and directories"
    Copy the code above to a file, name it "fix-permissions.sh" and run it as follows:
    sudo bash fix-permissions.sh --drupal_path=your/drupal/path --drupal_user=your_user_name
    Note: The server group name is assumed "www-data", if it differs use the --httpd_group=GROUP argument.
  • Also if you are running fastcgi, the php runs AS the user, and will have access to all files that the user has access to unless you deliberately try to avoid this.

  • This helped me with my OSX Permission issues. I found it in https://www.drupal.org/node/244924#comment-3741738 by protoplasm user. I was like him having issues after a migration.

    [[email protected]]cd /path_to_drupal_installation/sites
    [[email protected]]find . -type d -name files -exec chmod ug=rwx,o= '{}' \;
    [[email protected]]find . -name files -type d -exec find '{}' -type f \; | while read FILE; do chmod ug=rw,o= "$FILE"; done
    [[email protected]]find . -name files -type d -exec find '{}' -type d \; | while read DIR; do chmod ug=rwx,o= "$DIR"; done

License under CC-BY-SA with attribution

Content dated before 6/26/2020 9:53 AM

Tags used