Introduction
This guide assumes your web document root is the default for Apache and Nginx in /var/www/html
. If you followed one of our previous guides on setting up virtual hosts, your document root may be located in /var/www/example.com/html
. Just make sure you have the correct document root and update commands in this guide to match.
This guide was tested on Ubuntu Server 22.04, 20.04, 18.04 and 16.04, though it should also work with other Debian-based distributions without issue. If you are using CentOS, just substitute www-data
in this guide for apache
or nginx
. Any issues, please let me know in the comments.
I have provided two different methods in this guide for setting up SFTP access to your document root:
- Method One is a simple setup where you just add your SFTP user to the
www-data
group. - Method Two is far more secure and recommend if you want to limit where
www-data
has write access. I have included a special section for WordPress users and best security practises. See step 5.5: WordPress and www-data.
Regardless of the method you choose, Step 1, 2 and 3 below are the same.
This article also includes a section for WordPress users and best security practices.
1. Install SSH
SFTP is built upon the SSH transport layer and should be installed on most Linux server distributions by default. If it isn’t , you can install with:
sudo apt install ssh
If you see a message about “Pending kernel upgrade“, press ENTER
to continue.
If you see a message about “Daemons using outdated libraries“, press ENTER
to continue.
2. Configure SSH
Change the Subsystem to internal-sftp
in sshd_config
.
sudo nano /etc/ssh/sshd_config
Scroll to the bottom of the file and comment out the line Subsystem sftp
by adding #
before it and then add Subsystem sftp internal-sftp
below it.
<span class="red">#</span>Subsystem sftp /usr/lib/openssh/sftp-server
<span class="red">Subsystem sftp internal-sftp</span>
Save and exit (press CTRL
+ X
, press Y
, then press ENTER
)
This tells sshd
to use SFTP server code built into sshd
instead of running sftp-server
, which is now redundant and only kept for a backward compatibility.
Restart the sshd
service for changes to take affect.
sudo service sshd restart
3. Create SFTP User
It’s not recommended that you use the root account or any account with sudo privileges to upload files to the web server document root. For this reason, you should create a new user that only has SFTP access to the document root.
In this guide, we are calling the SFTP user webdev
– you can call this whatever you want. If you plan to give SFTP access to multiple users across different document roots, consider a naming scheme like username_domain_com
. For example john_devanswers_co
. This will make it easier to keep track of all your SFTP users.
For the purposes of this guide, we will name the SFTP user webdev
.
sudo adduser webdev
Generate a password and press enter to accept all defaults.
Two SFTP Configuration Methods
I am providing two different methods in this guide because there are some people who just want a quick and easy method to access the document root with SFTP, and others who want a more advanced security setup (which I use). It might be worth reading through both methods to see which one suits your needs.
- Method One: Quick Setup
Use this method if:- You just want a quick and simple method to give one or multiple SFTP users access to the document root by adding them to the
www-data
group. - You already have a live, busy site running on the document root and don’t want to risk accidentally taking it down by setting restrictive permissions in Method Two.
- You need to install a CMS from scratch such as WordPress before setting up more restrictive permissions in Method Two.
- You just want a quick and simple method to give one or multiple SFTP users access to the document root by adding them to the
- Method Two: Better Security and SFTP User Management
Use this method if:- You want the best security possible for your document root.
- You already have your CMS such as WordPress installed and running, and now want to lock it down.
- You want to restrict where
www-data
can write to. - You have multiple developers that need write access to multiple document roots hosted on your server.
4. Method One: Quick Setup
Using this method with the least amount of configuration, we will create a Match User
directive in the SSH config and add your SFTP user to the www-data
group.
4.1. Add Match User
Directive in SSH Config
Restrict the user webdev
to the document root and also disable their SSH access – we only want them to be able to log in over SFTP. We can do this by adding a Match User
directive in the SSH config file.
Begin by opening sshd_config
.
sudo nano /etc/ssh/sshd_config
Scroll down to the bottom of the SSH config file and add your new Match User
directive.
Make sure that ChrootDirectory
is the directory above your document root. For example, if your document root is /var/www/html/
, then the ChrootDirectory
is /var/www/
.
If you followed one of our previous guides on hosting multiple domains on Apache or Nginx, your document root may be located in /var/www/mydomain.com/html
, in that case, your ChrootDirectory
would be /var/www/mydomain.com/
.
Note you can add multiple users here separated by a comma, e.g. Match User webdev, webdev2, webdev3
Note: ForceCommand internal-sftp
will force SFTP access only and not allow this SFTP user to log in via SSH.
Match User <span class="red">webdev</span>
ChrootDirectory <span class="red">/var/www/</span>
ForceCommand internal-sftp
X11Forwarding no
AllowTcpForwarding no
PasswordAuthentication yes
Save and exit (press CTRL
+ X
, press Y
, then press ENTER
)
Test SSH config before restarting.
sudo sshd -t
If no errors, restart the sshd
service for changes to take affect.
sudo service sshd restart
4.2. Add SFTP User to www-data
Add your SFTP user webdev
to the www-data
group.
sudo usermod -a -G www-data webdev
Note: Linux groups do not take affect until the user logs out and in again. If you are already logged in as this user in your FTP client, close the program completely and then log in again.
4.3. Set Directory Permissions
SFTP is very strict when it comes to chroot directory permissions and if they are not set correctly, you will not be able to log in, so please follow these instructions carefully.
The chroot is usually the directory above your document root. For example, by default /var/www/
is the chroot and /var/www/html
is the document root.
Another example: If your document root is one directory deeper such as /var/www/domain.com/html
, then your chroot is /var/www/domain.com
.
- The chroot directory *and all of its parents* must not have group or world write capabilities, otherwise SFTP log in will fail with fatal: bad ownership or modes for chroot directory component “/var/www/”. In other words, you must make sure both
/var/
and/var/www/
are set to755
. (not775
, which gives group write permissions). - The chroot directory *and all of its parents* must be owned by
root
, otherwise SFTP log in will fail with fatal: bad ownership or modes for chroot directory component “/var/www/”. In other words, you must make sure both/var/
and/var/www/
are both owned byroot
. - If your chroot directory is not
/var/www
but, for example,/var/www/domain.com
, then you will need to apply these permissions androot
ownership to that folder as well and all of its parents!
Firstly, let’s check permissions and ownership for /var/
– they should be 755
and root
by default.
sudo ls -ld /var/
Output:
drwxr-xr-x 14 root root 4096 Jul 30 02:24 /var/
If they do not match above, set permissions and ownership below.
sudo chmod 755 /var/
sudo chown root:root /var/
Now let’s apply the same permissions and ownership for your chroot. Assuming that your chroot is /var/www/
, ensure that the directory is set to 755
.
sudo chmod 755 /var/www/
Ensure your chroot directory is owned by root.
sudo chown root:root /var/www/
To check permissions for this directory:
sudo ls -ld /var/www/
Output:
drwxr-xr-x 14 root root 4096 Jun 3 14:28 /var/www/
Make sure the document root is set to 775
, which will allow groups write to this directory.
sudo chmod 775 /var/www/html
Make sure that your document root and all contents are owned by www-data
.
sudo chown -R www-data:www-data /var/www/html*
Change all directories in the document root to 775
. This will allow both the owner (www-data
) and its group (which SFTP users belong to) to read, write and execute folders.
sudo find /var/www/html/ -type d -exec chmod 775 {} \;
Change all files in the document root to 664
, this will allow both the owner and the group to read, write and execute files.
sudo find /var/www/html/ -type f -exec chmod 664 {} \;
Make sure that any new files or folders created by SFTP users inherit the www-data
group.
sudo find /var/www/html -type d -exec chmod g+s {} \;
Now log into SFTP with you preferred FTP client and make sure you can create, edit and delete files and folders.
If you are not able to log in, check the auth log for the last 50 entries. Also try closing your FTP client and opening it again.
sudo tail -n 50 /var/log/auth.log
4.4. Adding More SFTP Users
If you need to provide other SFTP users write access to the document root, simply add their usernames separated by a comma, e.g. Match User webdev, webdev2, webdev3
in sshd_config
(step 4.1) and then add the SFTP user to the www-data
group (Step 4.2) .
5. Method Two: Better Security and SFTP User Management
In this method we will set up more restrictive permissions for your document root and use Linux user groups to manage SFTP users. This is the method I personally use for managing multiple virtual hosts, WordPress installs, and SFTP users on the one server.
Even if you are only hosting one website on your server, I strongly recommend this setup if you want the best security for your website’s document root.
This method removes www-data
write access to the entire document root. Consider a scenario where a PHP script or WordPress plugin is hacked, the attacker could gain write access to your entire document root. To mitigate this, we need to only give www-data
(and thus WordPress) write access to the directories where it only needs it to function properly.
If you haven’t installed your CMS yet, you should first carry out Method One, upload and install your CMS, then follow Method Two to lock down the CMS.
5.1. Create a New Linux User Group
With this method we will create a Linux User Group with the necessary access to the document root and then add our SFTP users to that group.
If you are hosting multiple websites on the one server with Apache or Nginx, you should name these groups so they correspond to your domain name. For example, sftp_example1_com
and sftp_example2_org
. This will make it a lot easier to keep track of your groups should they grow over time.
However, for the purposes of this guide, we will just call the group sftp_users
and restrict this group to the default document root /var/www/html
.
Add new group:
sudo groupadd sftp_users
5.2. Add SFTP User to Group
Add your SFTP user webdev
(or whatever you called it) to the sftp_users
group.
sudo usermod -a -G sftp_users webdev
Note: Groups do not take affect until the user logs out and in again. If you are already logged in as this user in your FTP client, close the program completely and then log in again.
5.3. Add Match Group
Directive in SSH Config
In Method One we used the Match User
directive, but by using the Match Group
directive, you can manage multiple users and document roots far more effectively.
This allows you to restrict an entire group to a particular document root, and then you just add your SFTP users to that group with no additional configuration. This can save you a lot of time.
Begin by opening sshd_config
sudo nano /etc/ssh/sshd_config
Scroll down to the bottom of the SSH config file and add your new Match Group
directive.
Make sure that ChrootDirectory
is the directory above your document root. For example, if your document root is /var/www/html/
, then the ChrootDirectory
is /var/www/
.
If you followed one of our previous guides on setting up multiple domains, your document root may be located in /var/www/mydomain.com/html
, in that case, your ChrootDirectory
would be /var/www/mydomain.com/
.
Note: ForceCommand internal-sftp
will force SFTP access only and not allow this SFTP user to log in via SSH.
Match Group <span class="red">sftp_users</span>
ChrootDirectory <span class="red">/var/www/</span>
ForceCommand internal-sftp
X11Forwarding no
AllowTcpForwarding no
PasswordAuthentication yes
Save and exit (press CTRL
+ X
, press Y
, then press ENTER
)
Test SSH config before restarting.
sudo sshd -t
If no errors, restart the sshd
service for changes to take affect.
sudo service sshd restart
5.4. Set Directory Permissions
SFTP is very strict when it comes to chroot directory permissions and if they are not set correctly, you will not be able to log in, so please follow these instructions carefully.
The chroot is usually the directory above your document root. For example, by default /var/www/
is the chroot and /var/www/html
is the document root. Another example, if your document root is one directory deeper such as /var/www/domain.com/html
, then your chroot is /var/www/domain.com
.
- The chroot directory *and all of its parents* must not have group or world write capabilities, otherwise SFTP log in will fail with fatal: bad ownership or modes for chroot directory component “/var/www/”. In other words, you must make sure both
/var/
and/var/www/
are set to755
. (not775
, which gives group write permissions). - The chroot directory *and all of its parents* must be owned by
root
, otherwise SFTP log in will fail with fatal: bad ownership or modes for chroot directory component “/var/www/”. In other words, you must make sure both/var/
and/var/www/
are both owned byroot
. - If your chroot directory is not
/var/www
but, for example,/var/www/domain.com
, then you will need to apply these permissions androot
ownership to that folder as well and all of its parents!
Firstly, let’s check permissions and ownership for /var/
– they should be 755
and root
by default.
sudo ls -ld /var/
Output:
drwxr-xr-x 14 root root 4096 Jul 30 02:24 /var/
If they do not match above, set permissions and ownership below.
sudo chmod 755 /var/
sudo chown root:root /var/
Now let’s apply the same permissions and ownership for your chroot. Assuming that your chroot is /var/www/
, ensure that the directory is set to 755
.
sudo chmod 755 /var/www/
Ensure your chroot directory is owned by root.
sudo chown root:root /var/www/
To check permissions for this directory:
sudo ls -ld /var/www/
Output:
drwxr-xr-x 14 root root 4096 Jun 3 14:28 /var/www/
Make sure the document root is set to 775
, which will allow groups write to this directory.
sudo chmod 775 /var/www/html
Make sure that your document root and all contents are owned by root
and the group sftp_users
.
sudo chown -R root:sftp_users /var/www/html*
Change all directories in the document root to 775
. This will allow the sftp_users
group to read, write and execute folders.
sudo find /var/www/html/ -type d -exec chmod 775 {} \;
Change all files in the document root to 664
, this will allow the sftp_users
group to read, write and execute files.
sudo find /var/www/html/ -type f -exec chmod 664 {} \;
Make sure that any new files or folders created by the SFTP user inherit the group of the document root, in this example the sftp-users
group.
sudo find /var/www/html -type d -exec chmod g+s {} \;
Now log into SFTP with you preferred FTP client and make sure you can create, edit and delete files and folders in the document root.
If you are not able to log in, check the auth log for the last 50 entries. Also try closing your FTP client and opening it again.
sudo tail -n 50 /var/log/auth.log
If you need to provide other SFTP users write access to the document root, simply add them to the sftp-users
group (Step 5.2).
5.5. WordPress and www-data
www-data
now has no write access to your document root, which is the preferred security setup. However, you may need to give www-data
write access to certain files and directories in order for WordPress to function properly.
.htaccess and Apache
If you get an Apache error after setting permissions in the previous steps “You don’t have permission to access this resource. Server unable to read htaccess file, denying access to be safe”, you may need to give both www-data
and the sftp-users
group ownership of .htaccess.
sudo chown www-data:sftp_users /var/www/html/.htaccess
WordPress Uploads Directory
If your users need to upload files through the WordPress media library, you will need to give www-data
write access to the uploads directory in /var/www/html/wp-content/uploads
.
sudo chown -R www-data:sftp_users /var/www/html/wp-content/uploads*
This will give both WordPress and all your SFTP users write access to the uploads directory.
WordPress Cache Directory
If you are using a caching plugin such as W3 Total Cache plugin, you should give www-data
write access to the cache directory in /var/www/html/wp-content/cache
.
sudo chown -R www-data:sftp_users /var/www/html/wp-content/cache*
As well as the above, if you’re using W3 Total Cache, give www-data
write access to the w3tc-config
folder.
sudo chown -R www-data:sftp_users /var/www/html/wp-content/w3tc-config*
If installing W3 Total Cache for the first time, you may have to give www-data
write access to wp-config.php
and .htaccess
, but you can revoke it later.
sudo chown www-data:sftp_users /var/www/html/wp-content/wp-config.php
sudo chown www-data:sftp_users /var/www/html/wp-content/.htaccess
WordFence
If you are using WordFence, you should give www-data
write access to /wp-content/wflogs/
.
sudo chown -R www-data:sftp_users /var/www/html/wp-content/wflogs*
If you are setting up WordFence Web Application Firewall (WAF), you will need to give www-data
write access to .htaccess
and the document root folder. You can revoke write access after WAF is configured.
WordPress Updates and Installing/Updating Plugins
WordPress normally uses www-data
in order to update itself and add/update plugins. This is the most common setup but it is not the most secure because any rogue plugin could compromise your entire document root.
You should instead upload the SSH SFTP Updater Support plugin to your WordPress plugins directory and then activate it in WordPress.
Once activated, if you need to update WordPress or add/update plugins, you will be prompted for your SFTP username and password/SSH key. You can save this password or SSH key in your browser password manager so you don’t have to type it every time.
If the SSH SFTP Updater Support plugin isn’t prompting you to enter password when you try to update WordPress or alter plugins, add an entry in your wp-config.php file for define('FS_METHOD', 'ssh2');
. Make sure there are no other entries for FS_METHOD
in the config file.
Auto update WordPress plugins
WordPress 5.5 (released Aug 2020) now has the capability of automatically updating plugins itself. If you have 20+ WordPress installs to maintain like I do, this can make your life a lot easier! If you want this feature, you would have to give www-data
write access to the plugins directory.
However, this introduces a new problem. If you have installed SSH SFTP Updater Support plugin, WordPress will not be able to update plugins itself despite having write access to the plugins folder. It will try to auto update the plugin but will be waiting for you to enter your SFTP username and password.
In this case, you should add define('FS_METHOD', 'direct');
in wp-config.php to force WordPress not to prompt for SFTP details and then it will auto update plugins itself. However, now you will not be able to update WordPress core in the control panel when an update is available. In this case you would have to temporarily comment out define('FS_METHOD', 'direct');
and then the WordPress core update will run. I haven’t found an easier method for this just yet, but as WordPress core updates aren’t as frequent as plugin updates, it’s not a big deal for me.
To allow WordPress to auto update plugins, give www-data
write access to the wp-content and plugins directory.
sudo chown www-data:sftp_users /var/www/html/wp-content/
sudo chown -R www-data:sftp_users /var/www/html/wp-content/plugins*
You may also need to give www-data
write access to the wp-content/upgrade
directory if it exists.
sudo chown -R www-data:sftp_users /var/www/html/wp-content/upgrade*
If you already have plugins in the plugins folder, make sure to update all directories and files.
sudo find /var/www/html/wp-content/plugins -type d -exec chmod 775 {} \;
sudo find /var/www/html/wp-content/plugins -type f -exec chmod 664 {} \;
And make sure any new files and directories inherit the correct permissions.
sudo find /var/www/html/wp-content/plugins -type d -exec chmod g+s {} \;
And don’t forget to add define('FS_METHOD', 'direct');
to wp-config.php.
Just bear in mind that if there is a security breach, the attacker will have write access to your entire plugins folder, but not your entire document root.
But can WordPress core update itself?
This is one caveat of restricting where www-data
can write to. If there is a critical security patch released, WordPress will not have the appropriate permissions to apply this patch on its own. You would have to give www-data
write access to the document root and all WordPress folders, which sort of defeats the purpose of restricting permissions.
However, if you are serious about WordPress security, you should be proactively maintaining and updating your WordPress install frequently anyway. And in such eventuality where there is a critical security hole discovered in WordPress core, a hacker will still not be able to gain write access to your entire document root.
You can be notified of critical WordPress and plugin security patches via email using a plugin such as WordFence and then update WordPress yourself using the SSH SFTP Updater Support plugin as previously mentioned. This is the method that I personally employ for all my WordPress installs.
Let me know if this helped. Follow me on Twitter, Facebook and YouTube, or 🍊 buy me a smoothie.
Hello,
Thanks for your blog, it works well with chroot folder /var/ww/
But what if I have multiple user, and i want each user chroot into their own folder
My folder structure is:
/var/www/html/website1
/var/www/html/website2
…
what configuration should i use and is this possible?
Just amazing awesome. This is exactly I was looking for.
Its just that this page was not coming up on google search engine with “configure sftp on linux”
Thanks. I added the phrase How to configure SFTP on Linux into the meta description 😉
Hi,
How do I create a ssh user for github with restricted access to a one documentRoot
thnx
Me again 🙂
I figured it out. It was just my missunderstanding.
I added the domain name as a folder and added html inside that folder, then I made the domain folder the chroot and added -d /html to ForceCommand internal-sftp
Now it works as I thougt it would.
Thanks for the great tutorial. Saved me a lot of time!
For Method 2: I followed the steps and can login on the server, create, delete folders or files in the configured DocumentRoot.
Maybe it’s a missunderstanding on my part, but when I log in I can see all DocumentRoots and I thought the sftp user will only see its DocumentRoot. I cannot create a new folder or delete one in the other DocumentRoots, but I can create files or look at the other files.
Is there a way to prevent this and make sure that the sftp user has only access to its DocumentRoot
I redid all the steps and I am sure I followed everything.
Chroot: /var/www/
DocumentRoot of sftp_user1: /var/www/domain1
There are several other domains under /var/www/
I hope it’s clear what I mean since English isn’t my first language.
Kind regards,
Markus
Method Two (Section 5) did not work for me, until I added sftp_users to AllowGroups and yyyyy to AllowUsers in /etc/ssh/sshd_config. The omission of either prevents access; the inclusion of both gets me in. But I did not see any reference to this in the article.