FTP retrieval still appears in scheduled exports, legacy partner exchanges, and archive feeds where newer APIs are not available. GNU wget is a good fit for those jobs because it can authenticate, reuse saved credentials, resume interrupted transfers, and mirror a simple FTP subtree without leaving the shell.

An FTP transfer uses a control connection for login and commands plus a separate data connection for listings and file contents. The examples below use a masked FTP host, service-account names, and a redacted secret placeholder while keeping the real wget transcript shape intact. In wget, file URLs download directly, while options such as --user, --ask-password, ~/.netrc, --continue, --no-host-directories, and --cut-dirs control authentication, recovery, and local path layout. Passive FTP is the default, with active mode available when a firewall or NAT setup breaks directory listings or data transfers.

Plain FTP sends credentials and payloads without transport encryption unless the server offers FTPS or another protected layer. Keep credential files locked down, avoid putting passwords directly on shared command lines when possible, and switch protocols when the data is sensitive or crosses an untrusted network.

Steps to download files over FTP with wget:

  1. Create a dedicated working directory for the transfer set.
    $ mkdir -p ~/partner-feed-downloads
    $ cd ~/partner-feed-downloads
    $ pwd
    /home/user/partner-feed-downloads

    Using one path per FTP job makes verification and cleanup much easier after retries or partial runs.

  2. Download a file from an authenticated FTP path without exposing the password in shell history.
    $ wget --user=svc_partner_feed_ro --ask-password ftp://ftp.partner-feed.example.net/exports/settlement-2026-03-28.csv
    Password for user 'svc_partner_feed_ro':
    --2026-03-29 09:38:11--  ftp://ftp.partner-feed.example.net/exports/settlement-2026-03-28.csv
               => 'settlement-2026-03-28.csv'
    Resolving ftp.partner-feed.example.net (ftp.partner-feed.example.net)... 198.51.100.24
    Connecting to ftp.partner-feed.example.net (ftp.partner-feed.example.net)|198.51.100.24|:21... connected.
    Logging in as svc_partner_feed_ro ... Logged in!
    ==> SYST ... done.    ==> PWD ... done.
    ==> TYPE I ... done.  ==> CWD (1) /exports ... done.
    ==> SIZE settlement-2026-03-28.csv ... 29401
    ==> EPSV ... done.    ==> RETR settlement-2026-03-28.csv ... done.
    Length: 29401 (29K) (unauthoritative)
    
         0K .......... .......... ........                        100% 9.12M=0.003s
    
    2026-03-29 09:38:11 (9.12 MB/s) - 'settlement-2026-03-28.csv' saved [29401]

    Replace the masked host and service account with the target FTP values, and let the password prompt supply the secret without putting it on the command line.

  3. Store credentials in ~/.netrc for unattended or repeated jobs.
    ~/.netrc
    machine ftp.partner-feed.example.net
      login svc_partner_feed_ro
      password MASKED_PARTNER_FEED_PASSWORD
    $ chmod 600 ~/.netrc
    $ ls -l ~/.netrc
    -rw------- 1 user user 101 Mar 29 09:39 /home/user/.netrc

    If ~/.netrc is readable by other local users, credentials can leak and some clients may refuse to use it.

  4. Reuse the saved credentials for a scheduled or larger download.
    $ wget ftp://ftp.partner-feed.example.net/exports/nightly-ledger-2026-03-28.tar.gz
    --2026-03-29 09:39:46--  ftp://ftp.partner-feed.example.net/exports/nightly-ledger-2026-03-28.tar.gz
               => 'nightly-ledger-2026-03-28.tar.gz'
    Resolving ftp.partner-feed.example.net (ftp.partner-feed.example.net)... 198.51.100.24
    Connecting to ftp.partner-feed.example.net (ftp.partner-feed.example.net)|198.51.100.24|:21... connected.
    Logging in as svc_partner_feed_ro ... Logged in!
    ==> SYST ... done.    ==> PWD ... done.
    ==> TYPE I ... done.  ==> CWD (1) /exports ... done.
    ==> SIZE nightly-ledger-2026-03-28.tar.gz ... 52428800
    ==> EPSV ... done.    ==> RETR nightly-ledger-2026-03-28.tar.gz ... done.
    Length: 52428800 (50M) (unauthoritative)
    
    ##### snipped #####
    
    2026-03-29 09:39:50 (11.6 MB/s) - 'nightly-ledger-2026-03-28.tar.gz' saved [52428800]

    A clean unattended run proves the saved machine entry, service account, and remote path are all correct before the job is automated.

  5. Resume an interrupted FTP transfer from the existing local offset instead of restarting from byte zero.
    $ wget --continue ftp://ftp.partner-feed.example.net/exports/nightly-ledger-2026-03-28.tar.gz
    --2026-03-29 09:40:11--  ftp://ftp.partner-feed.example.net/exports/nightly-ledger-2026-03-28.tar.gz
               => 'nightly-ledger-2026-03-28.tar.gz'
    Connecting to ftp.partner-feed.example.net (ftp.partner-feed.example.net)|198.51.100.24|:21... connected.
    Logging in as svc_partner_feed_ro ... Logged in!
    ==> SYST ... done.    ==> PWD ... done.
    ==> TYPE I ... done.  ==> CWD (1) /exports ... done.
    ==> SIZE nightly-ledger-2026-03-28.tar.gz ... 52428800
    ==> EPSV ... done.    ==> REST 10485760 ... done.
    ==> RETR nightly-ledger-2026-03-28.tar.gz ... done.
    Length: 52428800 (50M), 41943040 (40M) remaining (unauthoritative)
    
            [ skipping 10240K ]
    ##### snipped #####
    
    2026-03-29 09:40:15 (12.8 MB/s) - 'nightly-ledger-2026-03-28.tar.gz' saved [52428800]

    The REST line shows that the server accepted a resume offset instead of restarting the transfer from the beginning.

  6. Mirror a bounded FTP directory while flattening the leading remote path segments in the local copy.
    $ wget --recursive --no-parent --no-host-directories --cut-dirs=1 \
      --directory-prefix=partner-feed-mirror \
      ftp://ftp.partner-feed.example.net/exports/
    --2026-03-29 09:40:29--  ftp://ftp.partner-feed.example.net/exports/
               => 'partner-feed-mirror/.listing'
    Connecting to ftp.partner-feed.example.net (ftp.partner-feed.example.net)|198.51.100.24|:21... connected.
    Logging in as svc_partner_feed_ro ... Logged in!
    ##### snipped #####
    Removed 'partner-feed-mirror/.listing'.
    --2026-03-29 09:40:29--  ftp://ftp.partner-feed.example.net/exports/settlement-2026-03-28.csv
               => 'partner-feed-mirror/settlement-2026-03-28.csv'
    ##### snipped #####
    --2026-03-29 09:40:29--  ftp://ftp.partner-feed.example.net/exports/nightly-ledger-2026-03-28.tar.gz
               => 'partner-feed-mirror/nightly-ledger-2026-03-28.tar.gz'
    ##### snipped #####
    --2026-03-29 09:40:34--  ftp://ftp.partner-feed.example.net/exports/archive/manifest-2026-03-28.txt
               => 'partner-feed-mirror/archive/manifest-2026-03-28.txt'
    ##### snipped #####
    FINISHED --2026-03-29 09:40:34--
    Total wall clock time: 4.90s
    Downloaded: 3 files, 50M in 4.80s (10.4 MB/s)

    --no-host-directories drops the host directory, and --cut-dirs=1 removes the leading /exports segment from the local tree.

  7. Verify the retrieved files before moving them into the next job stage.
    $ find ~/partner-feed-downloads -maxdepth 3 -type f | sort
    /home/user/partner-feed-downloads/nightly-ledger-2026-03-28.tar.gz
    /home/user/partner-feed-downloads/settlement-2026-03-28.csv
    /home/user/partner-feed-downloads/partner-feed-mirror/archive/manifest-2026-03-28.txt
    /home/user/partner-feed-downloads/partner-feed-mirror/nightly-ledger-2026-03-28.tar.gz
    /home/user/partner-feed-downloads/partner-feed-mirror/settlement-2026-03-28.csv

    Known files in known directories are the fastest confirmation that authentication, path selection, and recursion behaved as expected.