{"id":10923,"library":"ftp-srv","title":"Modern Extensible FTP Server","description":"ftp-srv is a modern and extensible FTP server library for Node.js, currently stable at version 4.6.3. It provides a promise-based API for creating FTP and FTPS (Explicit & Implicit TLS) servers with support for both passive and active data transfers. The project maintains an active release cadence, frequently publishing bug fixes and minor features, as seen in recent updates like recursive directory creation and TypeScript declaration improvements. Its key differentiators include a highly extensible file system per connection, allowing for custom storage backends, and comprehensive support for various FTP commands and connection modes. It differentiates itself by offering detailed control over passive connection URLs and port ranges, addressing complexities often found in network deployments behind NATs or firewalls. It requires Node.js version 12 or higher.","status":"active","version":"4.6.3","language":"javascript","source_language":"en","source_url":"https://github.com/autovance/ftp-srv","tags":["javascript","ftp","ftp-server","ftp-srv","ftp-svr","ftpd","ftpserver","server","typescript"],"install":[{"cmd":"npm install ftp-srv","lang":"bash","label":"npm"},{"cmd":"yarn add ftp-srv","lang":"bash","label":"yarn"},{"cmd":"pnpm add ftp-srv","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"While CommonJS `require` is shown in older examples, ESM `import` is the preferred pattern for modern Node.js applications and TypeScript projects using this library, which ships with type definitions.","wrong":"const FtpSrv = require('ftp-srv');","symbol":"FtpSrv","correct":"import { FtpSrv } from 'ftp-srv';"},{"note":"The `errors` object, containing custom error types like `GeneralError`, is a named export from the library, useful for standardized error handling within the `login` event and other handlers.","wrong":"const { errors } = require('ftp-srv');","symbol":"errors","correct":"import { errors } from 'ftp-srv';"},{"note":"For TypeScript users, the `FtpSrvOptions` interface provides type definitions for the configuration object passed to the `FtpSrv` constructor, ensuring type safety and autocomplete.","symbol":"FtpSrvOptions","correct":"import { FtpSrv, type FtpSrvOptions } from 'ftp-srv';"}],"quickstart":{"code":"import { FtpSrv, errors } from 'ftp-srv';\nimport path from 'path';\nimport { promises as fs } from 'fs'; // For creating the dummy ftp_root directory\n\nconst port = 21;\nconst ftpServer = new FtpSrv({\n    url: `ftp://0.0.0.0:${port}`,\n    anonymous: true\n});\n\nftpServer.on('login', ({ connection, username, password }, resolve, reject) => {\n    // In a real application, you would validate credentials against a database\n    // For this example, 'anonymous' allows access to a specific root path\n    if (username === 'anonymous' && password === 'anonymous') {\n        const userRoot = path.join(process.cwd(), 'ftp_root');\n        // A real scenario might dynamically create/select a user's directory\n        return resolve({ root: userRoot });\n    }\n    return reject(new errors.GeneralError('Invalid username or password', 401));\n});\n\nftpServer.listen().then(() => {\n    console.log(`FTP server started on ftp://0.0.0.0:${port}`);\n    console.log('Connect with anonymous:anonymous to access', path.join(process.cwd(), 'ftp_root'));\n}).catch((err: Error) => {\n    console.error('Failed to start FTP server:', err);\n    process.exit(1);\n});\n\n// Helper to ensure the FTP root directory exists and contains a sample file\nasync function ensureFtpRootDirectory() {\n    const rootPath = path.join(process.cwd(), 'ftp_root');\n    try {\n        await fs.mkdir(rootPath, { recursive: true });\n        const welcomeFilePath = path.join(rootPath, 'welcome.txt');\n        await fs.writeFile(welcomeFilePath, 'Welcome to the ftp-srv server!\\nThis is a sample file.', { flag: 'wx' }); // 'wx' to only write if file doesn't exist\n        console.log(`Ensured FTP root directory at ${rootPath}`);\n    } catch (error: any) {\n        if (error.code !== 'EEXIST') { // Ignore if directory/file already exists\n            console.error('Error setting up FTP root directory:', error);\n        }\n    }\n}\nensureFtpRootDirectory();\n","lang":"typescript","description":"Demonstrates how to initialize an FTP server, handle anonymous logins, and specify a root directory. It includes basic error handling and prepares a dummy `ftp_root` directory in the current working directory, making it fully runnable out of the box."},"warnings":[{"fix":"Upgrade to `ftp-srv@4.3.4` or newer immediately to mitigate the vulnerability and ensure secure operation.","message":"Version 4.3.4 introduced a critical security fix to disallow PORT connections to alternate hosts. Running older versions (prior to 4.3.4) exposes the server to potential security vulnerabilities (GHSA-jw37-5gqr-cf9j).","severity":"breaking","affected_versions":"<4.3.4"},{"fix":"Set `pasv_url` to your server's static external IP address or provide a resolver function that returns the correct external IP based on the client's connection. Example: `new FtpSrv({ pasv_url: 'YOUR_EXTERNAL_IP' });`","message":"When deploying `ftp-srv` behind a NAT or firewall, the `pasv_url` option is crucial for passive mode connections. It must be correctly configured with the external (WAN) IP address; otherwise, external clients may hang when attempting data transfers.","severity":"gotcha","affected_versions":">=4.0.0"},{"fix":"To allow external connections, configure the `url` option to listen on all available network interfaces using `url: \"ftp://0.0.0.0:21\"`.","message":"The `url` option's hostname (e.g., `ftp://127.0.0.1:21`) must be `0.0.0.0` or the specific external IP address of the server to accept connections from outside the local machine. Using `127.0.0.1` will restrict client connections to localhost only.","severity":"gotcha","affected_versions":">=4.0.0"},{"fix":"Ensure your Node.js environment is updated to version 12 or newer. The `engines` field in the package's `package.json` specifies this minimum requirement.","message":"`ftp-srv` requires Node.js version 12 or higher. Deployments on older Node.js runtimes will fail to start or operate correctly due to reliance on newer language features and APIs.","severity":"breaking","affected_versions":"<4.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Change the `port` in the `url` option to an available port, or ensure no other process is listening on the desired port before starting `ftp-srv`. On Linux, you can use `lsof -i :<port>` to find the process.","cause":"The specified port (e.g., 21 for FTP) is already being used by another application or a previously terminated instance of the FTP server that hasn't fully released the port.","error":"Error: listen EADDRINUSE: address already in use :::21"},{"fix":"Review your `login` event handler to ensure it correctly resolves credentials. Verify that the username and password being sent by the FTP client match the expected values defined in your server-side logic.","cause":"This error is returned to the FTP client when the provided username and/or password do not pass the validation logic within your `ftpServer.on('login')` event handler.","error":"530 Not logged in. Invalid username or password."},{"fix":"Configure the `pasv_url` option with your server's external (WAN) IP address and ensure that the port range specified by `pasv_min` and `pasv_max` is open and accessible through your firewall.","cause":"This issue typically arises from an incorrect passive mode (`PASV`) configuration, often due to Network Address Translation (NAT), firewalls, or an improperly set `pasv_url` option, preventing the client from connecting to the server's data port.","error":"FTP client hangs during PASV data transfer or directory listing (e.g., after 'PASV' command)."}],"ecosystem":"npm"}