The most surprising thing about curl is that it’s not just for HTTP requests; it’s a full-fledged network transfer tool that treats FTP like just another protocol in its arsenal.

Let’s watch curl grab a file from an FTP server. Imagine you have a file named report.txt on an FTP server at ftp.example.com in the /public/data/ directory. You can retrieve it with this command:

curl -u username:password ftp://ftp.example.com/public/data/report.txt -o local_report.txt

Here’s what’s happening:

  • -u username:password: This provides the credentials for the FTP server. Replace username and password with your actual login details.
  • ftp://ftp.example.com/public/data/report.txt: This is the URL of the file on the FTP server. curl understands the ftp:// scheme and knows to use the FTP protocol.
  • -o local_report.txt: This tells curl to save the downloaded content to a file named local_report.txt on your local machine.

You can also upload files. To upload a local file named config.ini to the /configs/ directory on the same FTP server, you’d use:

curl -u username:password -T local_config.ini ftp://ftp.example.com/configs/config.ini
  • -T local_config.ini: The -T flag signifies that you want to upload the specified local file (local_config.ini) to the destination URL.

This flexibility comes from curl’s ability to abstract away the underlying network protocols. Whether it’s HTTP, HTTPS, FTP, SFTP, or SCP, curl presents a unified command-line interface. It handles the connection establishment, authentication, data transfer, and error checking for each protocol independently. The core problem curl solves for FTP is providing a simple, scriptable way to interact with FTP servers without needing dedicated FTP client software or interactive sessions.

Internally, when you use an ftp:// URL, curl invokes its FTP transfer module. This module performs the standard FTP handshake: it connects to the control port (usually 21), sends commands like USER, PASS, CWD (change working directory), TYPE I (for binary transfer), and then RETR (retrieve) or STOR (store) for data transfer. For data, it negotiates a separate data connection, either active or passive mode, depending on the server and curl’s configuration.

The levers you control are primarily the URL itself, specifying the protocol, host, path, and filename, along with the -u flag for credentials and -o or -T for output/input files. You can also influence the transfer behavior with other flags. For instance, -P (or --ftp-port) can specify the local port for passive mode connections, and -v (or --verbose) will show you the FTP commands being sent and received, which is invaluable for debugging.

Consider the difference between anonymous and authenticated FTP. For anonymous access, you often omit the -u flag, and curl will typically try to use anonymous as the username and your email address as the password, as per FTP conventions.

curl ftp://ftp.example.com/pub/README -o anonymous_readme.txt

If the server requires specific credentials, you must provide them. Using a password directly on the command line can be a security risk as it might be visible in shell history. For more secure handling, you can omit the password part of the -u flag, and curl will prompt you for it:

curl -u username ftp://ftp.example.com/private/data.zip -o secure_data.zip
# curl will then prompt: Enter host password for user 'username':

Many people don’t realize that curl also supports FTPS (FTP over SSL/TLS). To connect to an FTPS server, you’d typically use ftps:// in the URL and potentially add flags like --ssl-req-cert or --ssl-allow-insecure depending on the server’s SSL configuration.

curl -u username:password ftps://secure.example.com/secure_file.tar.gz -o secure_file.tar.gz

The most subtle aspect of curl’s FTP implementation, and one that trips people up, is its default behavior regarding passive versus active FTP modes. By default, curl prefers passive mode, which is generally more firewall-friendly as the client initiates the data connection. However, if you encounter connection issues on a restrictive network, explicitly forcing active mode with --ftp-port 20 (though this is rarely needed and often blocked) or ensuring passive mode is correctly negotiated can be the key. The server dictates which mode is available and preferred, and curl tries to adapt, but explicit control is possible.

Beyond basic transfers, you can list directory contents. To see what’s in /public/data/ on ftp.example.com, you can use:

curl -u username:password ftp://ftp.example.com/public/data/

This will often output the directory listing in a format similar to what an ls command would produce, though the exact format depends on the FTP server.

The next hurdle you’ll likely encounter is dealing with more complex FTP server configurations, such as explicit vs. implicit FTPS, or navigating servers that require specific port ranges for passive data connections.

Want structured learning?

Take the full Ftp course →