Skip to main content

Overview

The FTPClient class handles connections to FTP servers using Cloudflare Workers’ TCP Sockets API. You can connect with basic authentication or secure your connection with TLS/FTPS.

Basic connection

To connect to an FTP server, create an FTPClient instance and call the connect() method:
import { FTPClient } from 'workerd-ftp';

const client = new FTPClient('ftp.example.com', {
  user: 'username',
  pass: 'password',
  port: 21
});

await client.connect();
1

Create the client

Instantiate FTPClient with your server hostname and optional connection options.
2

Call connect()

The connect() method establishes the connection, performs feature discovery, and authenticates.
3

Start using the client

Once connected, you can perform file and directory operations.

Connection options

You can customize the connection behavior by passing a ConnectionOptions object:
user
string
default:"anonymous"
Username for authentication. Defaults to anonymous if not specified.
pass
string
default:"anonymous"
Password for authentication. Defaults to anonymous if not specified.
port
number
default:"21"
FTP server port. Standard FTP uses port 21.
secure
boolean
default:"false"
Enable TLS/FTPS with STARTTLS. When true, the client upgrades the connection to TLS.
activePort
number
default:"20"
Port for active mode data connections.
activeIp
string
default:"127.0.0.1"
IP address for active mode data connections.
activeIpv6
boolean
default:"false"
Use IPv6 for active mode connections.

Secure connections (TLS/FTPS)

To establish a secure connection using TLS, set the secure option to true:
const client = new FTPClient('ftp.example.com', {
  user: 'username',
  pass: 'password',
  secure: true  // Enable TLS
});

await client.connect();

How TLS works

When you enable the secure option, the client:
1

Discovers server features

Checks if the server supports AUTH TLS command.
2

Initiates STARTTLS

Sends the AUTH TLS command to upgrade the control connection.
3

Upgrades to TLS

Performs TLS handshake using startTls() with the expected server hostname.
4

Protects data channels

Sends PROT P command to encrypt data connections.
If the server doesn’t advertise AUTH TLS or PROT support but you’ve enabled secure: true, the client will warn you but attempt the connection anyway. Check your server’s capabilities to avoid connection failures.

Connection lifecycle

The connect() method performs several operations automatically:
// 1. Establish TCP socket connection
// 2. Wait for server ready message (220)
// 3. Send FEAT command to discover server features
// 4. (If secure) Upgrade to TLS with AUTH TLS
// 5. (If secure) Enable data channel protection with PROT P
// 6. Send USER command with username
// 7. Send PASS command with password (if needed)
// 8. Switch to binary mode with TYPE I

Feature discovery

During connection, the client automatically discovers server features using the FEAT command. This determines which advanced features are available:
  • MLST/MLSD - Machine-readable directory listings
  • AUTH TLS - TLS authentication methods
  • PROT - Data channel protection
  • MDTM - File modification time
  • REST - Resume capability
  • EPSV - Extended passive mode
The client stores discovered features internally and uses them to optimize operations. For example, if MLST is available, the stat() method will use it for detailed file information.

Closing connections

Always close your connection when you’re done to avoid resource leaks:
await client.close();
The close() method terminates both control and data connections.
In Cloudflare Workers, connections are tied to request lifecycle. Ensure you close connections before your worker finishes processing to maintain clean state.

Example: Complete connection workflow

import { FTPClient } from 'workerd-ftp';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const client = new FTPClient('ftp.example.com', {
      user: env.FTP_USER,
      pass: env.FTP_PASS,
      secure: true,
      port: 21
    });
    
    try {
      // Connect to server
      await client.connect();
      
      // Perform operations
      const files = await client.list();
      
      return Response.json({ files });
    } catch (error) {
      return Response.json({ error: String(error) }, { status: 500 });
    } finally {
      // Always close the connection
      await client.close();
    }
  }
};

Next steps

File operations

Upload, download, and manage files

Directory operations

Navigate and manage directories

Security

Best practices for secure FTP

Streaming

Stream large files efficiently

Build docs developers (and LLMs) love