When you’re setting up FTP access, especially for shared hosting or development environments, getting file and directory permissions right is paramount for security. The surprising truth is that the most secure default is often to deny access to everything, and then selectively grant only what’s absolutely necessary.

Let’s walk through a common scenario: you have a web server running an FTP daemon (like vsftpd) and you need to allow a user to upload files to a specific directory (/var/www/html/uploads) but prevent them from seeing or modifying anything else on the server.

Here’s how you’d set that up on a Linux system. First, create a dedicated FTP user and group. It’s crucial not to use your main system user for FTP.

sudo groupadd ftpusers
sudo useradd -g ftpusers -s /sbin/nologin ftpuser

The -s /sbin/nologin is key here; it prevents this user from logging in via SSH, significantly reducing the attack surface.

Next, create the directory they’ll need access to and set its ownership.

sudo mkdir -p /var/www/html/uploads
sudo chown ftpuser:ftpusers /var/www/html/uploads

Now, let’s talk permissions. The standard Unix permission system uses three sets of read ®, write (w), and execute (x) permissions for the owner, the group, and others.

For the uploads directory itself, the ftpuser (the owner) needs to be able to create files, so they need write permissions. They also need execute permissions to traverse into the directory.

sudo chmod 750 /var/www/html/uploads

Here’s what 750 means:

  • The first 7 (owner): rwx (read, write, execute). The ftpuser can list contents, create/delete files, and enter the directory.
  • The second 5 (group): r-x (read, execute). The ftpusers group can list contents and enter the directory, but not create files.
  • The third 0 (others): --- (no permissions). No one else can access this directory.

Now, consider the files within that directory. If ftpuser uploads a file, what permissions should it have? If they need to be able to edit it later, they’ll need write permissions. However, if it’s a web-accessible file, you might want to restrict write access to only ftpuser.

A common practice is to set a default umask for FTP users. The umask is a value that’s subtracted from the default permissions when a file or directory is created. For files, the default is often 666 (rw-rw-rw-), and for directories, 777 (rwxrwxrwx).

If you want ftpuser to create files that are readable by the web server (often running as www-data or apache) but not writable by others, and directories that are traversable by the web server, you’d configure vsftpd to use a specific umask.

In /etc/vsftpd.conf, you might have these lines (uncommented and set):

local_umask=022
anon_umask=022

With local_umask=022, when ftpuser creates a file (default 666), 666 - 022 = 644 (rw-r--r--). When they create a directory (default 777), 777 - 022 = 755 (rwxr-xr-x). This means files are readable by everyone, but only writable by the owner (ftpuser). Directories are readable and traversable by everyone, but only writable by the owner.

However, if your web server user (www-data) is part of the ftpusers group, you might want them to be able to modify files that ftpuser creates. In that case, you’d adjust the umask or, more commonly, use the file_open_mode and dir_list_enable directives in vsftpd.conf.

A more robust approach for web content is to use POSIX Access Control Lists (ACLs) if your filesystem supports them. This allows for finer-grained permissions beyond owner/group/other. For example, to allow ftpuser to upload and the www-data user to modify files in the uploads directory:

sudo setfacl -m u:ftpuser:rwx /var/www/html/uploads
sudo setfacl -m g:ftpusers:rx /var/www/html/uploads
sudo setfacl -m u:www-data:rw /var/www/html/uploads
sudo setfacl -R -m d:u:ftpuser:rwx /var/www/html/uploads
sudo setfacl -R -m d:g:ftpusers:rx /var/www/html/uploads
sudo setfacl -R -m d:u:www-data:rw /var/www/html/uploads

Here, -m modifies the ACL for the directory, and -R -m d: sets default ACLs for new files and subdirectories created within it. This ensures that ftpuser can create files and directories, the ftpusers group can list and traverse, and www-data can read and write to files, even if ftpuser isn’t in the www-data group. The d:u:www-data:rw is particularly powerful, allowing www-data to edit files ftpuser creates.

The one thing most people don’t realize is how critical the execute permission is for directories. Without it, a user or process cannot cd into a directory, nor can they ls its contents, even if they have read permissions. They also can’t create or delete files within that directory, as the system needs execute permission on the parent directory to perform those operations.

Once you’ve set these permissions, you’ll want to test thoroughly. Try logging in as ftpuser and uploading a file, then try to delete it. Then, as the web server user (or a simulated user), try to access or modify that file.

The next common hurdle after getting permissions sorted is ensuring your FTP server is configured to disallow write access to critical system directories and to prevent users from escaping their designated home directories.

Want structured learning?

Take the full Ftp course →