What Is SFTP?
SFTP (SSH File Transfer Protocol) is a secure file transfer protocol that runs over an SSH connection. Unlike traditional FTP, which transmits data — including credentials — in plaintext, SFTP encrypts the entire session: authentication, commands, and file data. It is the recommended method for transferring files to and from Linux servers.
SFTP is built into OpenSSH, which means it is available on virtually every Linux server without installing additional software. However, the default configuration allows any SSH user to transfer files and also open an interactive shell. For many use cases — such as granting web developers or content editors file access — you want to restrict users to SFTP only, without shell access, and confine them to specific directories.
SFTP vs FTP vs SCP
| Protocol | Encryption | Port | Resume Support | Firewall Friendly |
|---|---|---|---|---|
| SFTP | Full (SSH) | 22 (SSH) | Yes | Yes (single port) |
| FTP | None (FTPS adds TLS) | 21 + data ports | Yes | No (multiple ports) |
| SCP | Full (SSH) | 22 (SSH) | No | Yes (single port) |
SFTP is preferred over FTP because it uses a single encrypted connection (no separate data channel), and preferred over SCP because it supports directory listings, file resumption, and remote file management operations. SCP is being deprecated in newer versions of OpenSSH in favor of SFTP.
Creating an SFTP-Only User
The first step is to create a dedicated user account that is restricted to SFTP access with no interactive shell. Create a group for SFTP-only users, then create the user:
# Create the SFTP group
sudo groupadd sftpusers
# Create a user with no shell access
sudo useradd -m -g sftpusers -s /usr/sbin/nologin sftpuser1
# Set the user's password
sudo passwd sftpuser1
The /usr/sbin/nologin shell prevents the user from opening an interactive SSH session. They can only use SFTP.
Setting Up a Chroot Jail
A chroot jail confines the SFTP user to a specific directory tree, preventing them from browsing or accessing any other part of the filesystem. This is critical for security — without chroot, an SFTP user could navigate to /etc, /var/log, or other sensitive directories.
The chroot directory must meet specific ownership requirements enforced by OpenSSH:
- The chroot directory and every parent directory up to the root must be owned by root.
- The chroot directory must not be writable by any group or other user.
- A writable subdirectory inside the chroot is where the user actually uploads files.
# Create the chroot directory structure
sudo mkdir -p /home/sftpuser1/uploads
# Set ownership — chroot dir owned by root
sudo chown root:root /home/sftpuser1
sudo chmod 755 /home/sftpuser1
# The uploads directory is writable by the user
sudo chown sftpuser1:sftpusers /home/sftpuser1/uploads
sudo chmod 755 /home/sftpuser1/uploads
The user cannot write to /home/sftpuser1 (owned by root), but can read and write within /home/sftpuser1/uploads.
Configuring sshd_config with a Match Block
The SSH daemon configuration uses a Match block to apply SFTP-specific settings to users in the sftpusers group. Edit /etc/ssh/sshd_config and add the following at the end of the file:
# /etc/ssh/sshd_config — add at the END of the file
Match Group sftpusers
ChrootDirectory /home/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
PermitTunnel no
AllowAgentForwarding no
Key directives explained:
- Match Group sftpusers: Applies the following settings only to users in the
sftpusersgroup. - ChrootDirectory /home/%u: Confines the user to their home directory. The
%utoken is replaced with the username. - ForceCommand internal-sftp: Forces the use of OpenSSH's built-in SFTP server, overriding any other command the user might try to execute.
- AllowTcpForwarding no: Prevents the user from creating SSH tunnels.
- X11Forwarding no: Prevents X11 display forwarding.
Important: The Match block must be at the end of sshd_config. Any directives after a Match block are considered part of that block until the next Match or the end of the file.
Restart SSH to apply the configuration:
sudo systemctl restart sshd
Verifying the SFTP Configuration
Before deploying to production, validate the configuration. First, check for syntax errors:
sudo sshd -t
If there is no output, the configuration is valid. Then test the SFTP connection:
# Connect via SFTP
sftp sftpuser1@localhost
# Once connected, verify the chroot:
sftp> pwd
Remote working directory: /
sftp> ls
uploads
# Try to navigate outside the chroot (should fail):
sftp> cd /etc
Couldn't canonicalize: No such file or directory
# Upload a test file:
sftp> cd uploads
sftp> put testfile.txt
sftp> ls
testfile.txt
sftp> bye
Verify that the user cannot open a shell:
ssh sftpuser1@localhost
# Should output: "This service allows sftp connections only."
# Connection closed.
Restricting to Specific Directories
For more complex setups, you may want different SFTP users to access different directories. For example, a web developer might need access to /var/www/html while a content editor needs access to /var/www/uploads.
Using Bind Mounts
Since the chroot directory must be owned by root, you can use bind mounts to expose specific directories inside the chroot:
# Create mount point inside the chroot
sudo mkdir -p /home/sftpuser1/website
# Bind mount the web directory
sudo mount --bind /var/www/html /home/sftpuser1/website
# Make the bind mount writable
sudo mount -o remount,bind,rw /home/sftpuser1/website
# Make the bind mount persistent (add to /etc/fstab):
# /var/www/html /home/sftpuser1/website none bind 0 0
The user sees /website in their SFTP session but is actually reading and writing to /var/www/html.
Per-User Match Blocks
For individual user configurations, use Match User instead of Match Group:
Match User webdev
ChrootDirectory /var/www
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
Match User contenteditor
ChrootDirectory /var/www/uploads
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
Remember that each ChrootDirectory must be owned by root with appropriate permissions.
Common Troubleshooting
Connection Closes Immediately
This usually means the chroot directory permissions are incorrect. Check that the chroot directory and all parent directories are owned by root and are not group-writable or world-writable:
# Check ownership chain
namei -l /home/sftpuser1
# Every directory in the path must be root-owned
# and not writable by group or other
"Broken pipe" or "Write failed"
The user's writable directory inside the chroot does not exist or has incorrect permissions:
sudo ls -la /home/sftpuser1/
# uploads/ should be owned by sftpuser1:sftpusers
Checking Logs
SFTP activity is logged in the SSH authentication log. For more detailed SFTP logging, modify the ForceCommand directive:
ForceCommand internal-sftp -l VERBOSE
This logs file operations (uploads, downloads, directory listings) to syslog, useful for auditing file access.
Security Best Practices
- Use SSH keys when possible: Even for SFTP-only users, key-based authentication is more secure than passwords. See securing SSH for details on configuring key-based authentication.
- Limit concurrent connections: Add
MaxSessions 3to the Match block to prevent a single user from opening excessive connections. - Use fail2ban: Configure fail2ban to monitor SFTP authentication failures just as it monitors SSH. The default
sshdjail covers both. - Regularly audit users: Review SFTP user accounts periodically and remove accounts that are no longer needed. Part of your regular Linux security maintenance.
- Disable FTP entirely: If SFTP is configured, there is no reason to run an FTP server. Remove vsftpd, proftpd, or any other FTP daemon.
Secure File Transfers for Your Infrastructure
SFTP provides encrypted, authenticated file transfers without the complexity of FTP over TLS. Combined with chroot jails and proper SSH hardening, it gives your users secure file access with minimal attack surface. For additional server protection, including a web application firewall and DDoS mitigation, explore NOC.org's security plans.