
Security News
Insecure Agents Podcast: Certified Patches, Supply Chain Security, and AI Agents
Socket CEO Feross Aboukhadijeh joins Insecure Agents to discuss CVE remediation and why supply chain attacks require a different security approach.
Modern, extensible FTP Server
ftp-srv is a modern and extensible FTP server designed to be simple yet configurable.
npm install ftp-srv --save
// Quick start, create an active ftp server.
const FtpSrv = require('ftp-srv');
const port=21;
const ftpServer = new FtpSrv({
url: "ftp://0.0.0.0:" + port,
anonymous: true
});
ftpServer.on('login', ({ connection, username, password }, resolve, reject) => {
if(username === 'anonymous' && password === 'anonymous'){
return resolve({ root:"/" });
}
return reject(new errors.GeneralError('Invalid username or password', 401));
});
ftpServer.listen().then(() => {
console.log('Ftp server is starting...')
});
new FtpSrv({options})URL string indicating the protocol, hostname, and port to listen on for connections. Supported protocols:
ftp Plain FTPftps Implicit FTP over TLSNote: The hostname must be the external IP address to accept external connections. 0.0.0.0 will listen on any available hosts for server and passive connections.
Default: "ftp://127.0.0.1:21"
pasv_urlFTP-srv provides an IP address to the client when a PASV command is received in the handshake for a passive connection. Reference PASV verb. This can be one of two options:
const { networkInterfaces } = require('os');
const { Netmask } = require('netmask');
const nets = networkInterfaces();
function getNetworks() {
let networks = {};
for (const name of Object.keys(nets)) {
for (const net of nets[name]) {
if (net.family === 'IPv4' && !net.internal) {
networks[net.address + "/24"] = net.address
}
}
}
return networks;
}
const resolverFunction = (address) => {
// const networks = {
// '$GATEWAY_IP/32': `${public_ip}`,
// '10.0.0.0/8' : `${lan_ip}`
// }
const networks = getNetworks();
for (const network in networks) {
if (new Netmask(network).contains(address)) {
return networks[network];
}
}
return "127.0.0.1";
}
new FtpSrv({pasv_url: resolverFunction});
127.0.0.1 to the client.If not provided, clients can only connect using an Active connection.
pasv_minThe starting port to accept passive connections.
Default: 1024
pasv_maxThe ending port to accept passive connections.
The range is then queried for an available port to use when required.
Default: 65535
greetingA human readable array of lines or string to send when a client connects.
Default: null
tlsNode TLS secure context object used for implicit (ftps protocol) or explicit (AUTH TLS) connections.
Default: false
anonymousIf true, will allow clients to authenticate using the username anonymous, not requiring a password from the user.
Can also set as a string which allows users to authenticate using the username provided.
The login event is then sent with the provided username and @anonymous as the password.
Default: false
blacklistArray of commands that are not allowed.
Response code 502 is sent to clients sending one of these commands.
Example: ['RMD', 'RNFR', 'RNTO'] will not allow users to delete directories or rename any files.
Default: []
whitelistArray of commands that are only allowed.
Response code 502 is sent to clients sending any other command.
Default: []
file_formatSets the format to use for file stat queries such as LIST.
Default: "ls"
Allowable values:
ls bin/ls formatep Easily Parsed LIST formatfunction () {} A custom function returning a format or promise for one.
name parameterlogA bunyan logger instance. Created by default.
timeoutSets the timeout (in ms) after that an idle connection is closed by the server
Default: 0
ftp-srv also comes with a builtin CLI.
$ ftp-srv [url] [options]
$ ftp-srv ftp://0.0.0.0:9876 --root ~/Documents
urlSet the listening URL.
Defaults to ftp://127.0.0.1:21
--pasv_urlThe hostname to provide a client when attempting a passive connection (PASV).
If not provided, clients can only connect using an Active connection.
--pasv_minThe starting port to accept passive connections.
Default: 1024
--pasv_maxThe ending port to accept passive connections.
The range is then queried for an available port to use when required.
Default: 65535
--root / -rSet the default root directory for users.
Defaults to the current directory.
--credentials / -cSet the path to a json credentials file.
Format:
[
{
"username": "...",
"password": "...",
"root": "..." // Root directory
},
...
]
--usernameSet the username for the only user. Do not provide an argument to allow anonymous login.
--passwordSet the password for the given username.
--read-onlyDisable write actions such as upload, delete, etc.
The FtpSrv class extends the node net.Server. Some custom events can be resolved or rejected, such as login.
client-errorftpServer.on('client-error', ({connection, context, error}) => { ... });
Occurs when an error arises in the client connection.
connection client class object
context string of where the error occurred
error error object
disconnectftpServer.on('disconnect', ({connection, id, newConnectionCount}) => { ... });
Occurs when a client has disconnected.
connection client class object
id string of the disconnected connection id
id number of the new connection count (exclusive the disconnected client connection)
closedftpServer.on('closed', ({}) => { ... });
Occurs when the FTP server has been closed.
closingftpServer.on('closing', ({}) => { ... });
Occurs when the FTP server has started closing.
loginftpServer.on('login', ({connection, username, password}, resolve, reject) => { ... });
Occurs when a client is attempting to login. Here you can resolve the login request by username and password.
connection client class object
username string of username from USER command
password string of password from PASS command
resolve takes an object of arguments:
fs
root
fs is not provided, this will set the root directory for the connection.cwd
fs is not provided, will set the starting directory for the connectionroot directory.blacklist
whitelist
reject takes an error object
server-errorftpServer.on('server-error', ({error}) => { ... });
Occurs when an error arises in the FTP server.
error error object
RETRconnection.on('RETR', (error, filePath) => { ... });
Occurs when a file is downloaded.
error if successful, will be null
filePath location to which file was downloaded
STORconnection.on('STOR', (error, fileName) => { ... });
Occurs when a file is uploaded.
error if successful, will be null
fileName name of the file that was uploaded
RNTOconnection.on('RNTO', (error, fileName) => { ... });
Occurs when a file is renamed.
error if successful, will be null
fileName name of the file that was renamed
See the command registry for a list of all implemented FTP commands.
The default file system can be overwritten to use your own implementation.
This can allow for virtual file systems, and more.
Each connection can set it's own file system based on the user.
The default file system is exported and can be extended as needed:
const {FtpSrv, FileSystem} = require('ftp-srv');
class MyFileSystem extends FileSystem {
constructor() {
super(...arguments);
}
get(fileName) {
...
}
}
Custom file systems can implement the following variables depending on the developers needs:
currentDirectory()Returns a string of the current working directory
Used in: PWD
get(fileName)Returns a file stat object of file or directory
Used in: LIST, NLST, STAT, SIZE, RNFR, MDTM
list(path)Returns array of file and directory stat objects
Used in: LIST, NLST, STAT
chdir(path)Returns new directory relative to current directory
Used in: CWD, CDUP
mkdir(path)Returns a path to a newly created directory
Used in: MKD
write(fileName, {append, start})Returns a writable stream
Options:
append if true, append to existing file
start if set, specifies the byte offset to write to
Used in: STOR, APPE
read(fileName, {start})Returns a readable stream
Options:
start if set, specifies the byte offset to read from
Used in: RETR
delete(path)Delete a file or directory
Used in: DELE
rename(from, to)Renames a file or directory
Used in: RNFR, RNTO
chmod(path)Modifies a file or directory's permissions
Used in: SITE CHMOD
getUniqueName(fileName)Returns a unique file name to write to. Client requested filename available if you want to base your function on it.
Used in: STOU
See CONTRIBUTING.md.
This software is licensed under the MIT Licence. See LICENSE.
FAQs
Modern, extensible FTP Server
The npm package ftp-srv receives a total of 5,266 weekly downloads. As such, ftp-srv popularity was classified as popular.
We found that ftp-srv demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket CEO Feross Aboukhadijeh joins Insecure Agents to discuss CVE remediation and why supply chain attacks require a different security approach.

Security News
Tailwind Labs laid off 75% of its engineering team after revenue dropped 80%, as LLMs redirect traffic away from documentation where developers discover paid products.

Security News
The planned feature introduces a review step before releases go live, following the Shai-Hulud attacks and a rocky migration off classic tokens that disrupted maintainer workflows.