Passive FTP and FTPS data connection failures in CompleteFTP: login works, the directory listing times out

The user logs in successfully, and then nothing happens. The connecting client sits for a few seconds on the first directory listing or the first transfer, gives up, and reports Connection timed out after 20 seconds of inactivity and Failed to retrieve directory listing. SFTP to the same machine is fine. Sometimes it works from inside the network and fails from outside, or the other way round, or it worked yesterday and does not today.

FTP and FTPS use one connection for commands and a second, separate connection for every listing and every transfer. Logging in only proves the first one works. Everything below is about the second.

Throughout, the CompleteFTP server means the machine running CompleteFTP, and the connecting client means the FTP or FTPS program making the connection, whichever end you happen to sit at.

Which situation are you in?

Case Which end do you administer? What you are able to open Read
A The machine running CompleteFTP The session transcript, plus CompleteFTP Manager or the CLI, and the CompleteFTP server's log files Case A
B Only the connecting end The session transcript, and your own egress firewall Case B

Both cases see the same session transcript, so the transcript is not the discriminator. What separates them is whether you can open Diagnostics.log on the CompleteFTP machine: the passive listener writes there, and nothing in that transcript tells you what it said. If you administer both ends, for example while testing from a workstation on the same LAN, read Case A.

The short version

These two steps belong to both cases.

  1. Get the connecting client's transcript as far as the passive reply. A healthy exchange reads 227 Entering Passive Mode (a,b,c,d,p1,p2). followed by 150 Opening BINARY mode data connection for listing, and then stops. The reply carries the address a.b.c.d and the port p1 × 256 + p2.
  2. Decide whether the data connection reaches the CompleteFTP server at all. Nothing else in this article matters until you know the answer, and each case has its own way of finding out. In Case A, look for the OnConnected line in the server log (see Reading the log); if it is there, the connection arrived. In Case B, the transcript answers it: first check whether the address in the 227 reply is a private one while you are off-site, which is a diagnosis on its own, and if the address is right, note how the data connection failed. A connection refused straight away means something answered and rejected it; a wait that ends in a timeout means the packets were dropped in the path. Most clients distinguish the two in their own log.

Then:

  • Case A (below): set a passive port range and open that range inbound on every firewall and NAT device in front of the CompleteFTP server. Set the external IP address as well if the server is behind NAT and your clients send PASV and use the address they are given. Clients that send EPSV are never given an address, and many PASV clients ignore it and reuse the control connection's address, so a NAT'd server often works without it. Check a transcript before assuming either way.
  • Case B (below): if the address is a private one and you are off-site, the fix is at the CompleteFTP server end and there is a specific thing to ask for. If the address is right and the port is refused or silent while the session is still waiting, the block is in the path, and switching the connecting client to EPSV is the quickest test.

How a passive data connection is set up

Five facts decide every failure in this article. All five come from CompleteFTP's own behaviour, not from the protocol in general.

  1. The listener binds to the interface the control connection arrived on, not to all interfaces. On a multi-homed server the data port only exists on that one address.
  2. The port comes from the configured passive port range. With no range configured CompleteFTP asks the operating system for any free port, which is why an unconfigured server produces a different, unpredictable port every time.
  3. The 227 Entering Passive Mode reply carries an address and a port. The port is always the one the listener actually bound. The address is the interface address, unless the external IP address setting overrides it. The 229 reply to EPSV carries the port only, and the connecting client is required to reuse the control connection's address.
  4. One session uses one passive port, not one per file. The listener is created on the first PASV or EPSV and reused for every subsequent transfer in that session, keeping the same port throughout, so the range has to cover concurrent sessions. The port is returned to the range when the session ends, or when the connecting client switches to active mode with PORT or EPRT.
  5. The data connection must come from the same source address as the control connection, or from an address in the passive IP white-list. Anything else is rejected after the TCP connection is accepted, which looks exactly like a hang to the connecting client. The server logs the rejection:

    Data channel IP <data address> matches neither control channel IP <control address> nor any
    white-listed IP (<white-list>)

The settings, where they live, and what they default to

In CompleteFTP Manager: Sites → select the site → FTP / FTPSAdvanced FTP/FTPS SettingsPassive (PASV) Transfer Settings. Both of those groups are collapsed when the panel opens, which is the usual reason people report the settings are missing. The passive-wait timeout is elsewhere, in Limits and Timeouts on the same panel.

Setting (Manager caption) CLI / JSS property Default when never set What it changes
Minimum port number, Maximum port number, under Port Range pasvPortMin, pasvPortMax both unset, giving the range [0,0], meaning an operating-system-assigned port which ports the data listener binds
External IP address (IPv4 only) pasvIP unset, so the reply carries the interface address the control connection arrived on the address in the 227 reply, and nothing else
Use external IP address for redirectPassiveIPSecureOnly FTP/FTPS, that is, false whether the external IP address is also used for unencrypted FTP, or for FTPS only
IP white-list (in addition to client IP) pasvWhiteList empty, shown as Only client IP allowed which additional source addresses may open the data connection
Timeout for PASV wait (seconds), under Limits and Timeouts timeoutPassiveWait unset, meaning CompleteFTP waits indefinitely how long CompleteFTP holds the port waiting for the connecting client

Two things about that table are easy to get wrong.

  • The Manager shows the timeout in seconds and stores it in milliseconds. Every timeout on that panel does this. The CLI and JSS property is the stored value, so timeoutPassiveWait=60 is sixty milliseconds, and sixty seconds is timeoutPassiveWait=60000.
  • The port range is a range of individual ports, and the recommended size is 100. A range between 1024 and 65535 avoids the privileged ports.

From the command line (same on Windows and Linux):

completeftp site show "Default Site" pasvPortMin pasvPortMax pasvIP pasvWhiteList redirectPassiveIPSecureOnly timeoutPassiveWait
completeftp site set "Default Site" pasvPortMin=50000 pasvPortMax=50099
completeftp site set "Default Site" pasvIP=203.0.113.10 redirectPassiveIPSecureOnly=true
completeftp site set "Default Site" pasvWhiteList=10.20.0.7,10.20.0.8

site set applies the change itself, with no separate apply step. In the Manager you must click APPLY CHANGES or the change never reaches the CompleteFTP server. The CLI is the administration interface on Linux and works as installed; on Windows it is off by default and has to be enabled on the service. The same property names are used from JSS administration scripts, which require Enterprise MFT and an explicit config.applyChanges().

Case A: you administer the CompleteFTP server

1. Set a passive port range

Without one, the server binds whatever port the operating system hands it, and no firewall rule can be written for that. Pick a block of about 100 ports above 1024 that nothing else on the machine uses, and set both ends. Setting only one end is worse than setting neither: with a minimum and no maximum the range is empty, every PASV fails, and the client gets 501 Internal error. Contact server administrator.

2. Open the range inbound, on every device in front of the server

Every hop needs the rule: the host firewall on the CompleteFTP machine, the perimeter firewall, and any NAT or port-forwarding device. The rule is inbound to the server, for the whole range, in addition to port 21 (or 990 for implicit FTPS).

For plain FTP many firewalls read the PASV exchange and open the data port for that one connection by themselves, which is why plain FTP can work with no range configured at all. For FTPS the commands are encrypted, the firewall cannot read them, and the range has to be opened explicitly. A server that works over FTP and times out over FTPS, with everything else identical, is this and almost nothing else.

3. Set the external IP address only if you need it

Set it when the CompleteFTP machine has a private address and clients reach it through NAT on a public one. Leave it unset otherwise. It changes the address in the 227 reply and nothing else, so it never affects EPSV, IPv6, or SFTP.

The cost of setting it is that clients on the same LAN are then told to connect their data channel to the public address, which from inside the network frequently does not work. Three ways out, in the order worth trying:

  • Leave it unset and check whether your clients need it. A client that sends EPSV is never given an address, and many clients that send PASV ignore the address in the reply and reuse the control connection's address instead. Either behaviour makes a NAT'd server work with nothing configured. Confirm from a transcript before relying on it.
  • Set it to FTPS only. Choose FTPS only for Use external IP address for. Unencrypted FTP then keeps the LAN address in the reply, which is also what NAT routers that rewrite the PASV reply themselves expect to find. When this setting suppresses the override, the server logs PASV external IP address set but not used as configured for FTPS only at Information level.
  • Run a second site on a different port for internal users, with the external IP address unset. Multiple sites require Enterprise MFT.

The field accepts a literal IPv4 dotted-quad only. A host name, an IPv6 address or a value with stray whitespace is not accepted by the Manager field, and if such a value is written through the CLI or JSS the server logs PASV external IP address setting is invalid: and falls back to the interface address.

4. Apply, then confirm from the server's own log

Raise the server logging level first, because the lines that show the range and the bound port are written at Debug and raising the level is not retroactive. In CompleteFTP Manager that is the Monitoring panel → Log Files tab → Server logging level, followed by APPLY CHANGES; from the command line it is completeftp monitor set logging.level=Debug, which applies itself. Reconnect a client, read the log as described in Reading the log, then put the level back to Information.

Case B: you connect to a CompleteFTP server someone else administers

You cannot change anything on the server, so the job is to establish which of the two ends is at fault and hand over something specific.

  1. Read the address out of the 227 Entering Passive Mode (a,b,c,d,p1,p2). reply. If a.b.c.d is a private address (10.x, 172.16.x to 172.31.x, 192.168.x) and you are connecting from outside that network, the server is advertising an address you cannot route to. Some clients notice and say Server sent passive reply with unroutable address before falling back to the control connection's address.
  2. If your client fell back to the control address and the transfer still timed out, the address was not the problem. The port is blocked somewhere in between. Go to step 4.
  3. Try EPSV. Most clients have a setting for it. The EPSV reply carries no address, so if the listing succeeds under EPSV and fails under PASV, the address in the 227 reply is the fault and there is nothing wrong with the port. Rule out an FTP-aware firewall on your own side first, because one that rewrites or mangles the PASV reply produces the same signature. Then report the result to the server's administrator: it is exactly what they need to know.
  4. Check your own outbound rules for the whole passive range. Your firewall has to allow outbound connections to arbitrary high ports on the server. For FTPS it cannot infer them by reading the control connection, so a rule that permits port 21 only is not enough. Ask the server's administrator which range is configured, and allow that.
  5. Active mode is a fallback, not a fix. Your client can send PORT instead, which makes the server connect back to you, and that needs your own firewall to permit a connection arriving at your machine. Behind corporate NAT it usually cannot work. Those connections back to you normally leave the server from port 20, though the server falls back to an operating-system port if it cannot reuse port 20, so do not key a rule on the source port alone.

What to ask for, if the evidence points at the server end: a passive port range configured on the site and opened on the firewall for connections arriving at the server, and, if the server is behind NAT, the external IP address set, ideally for FTPS only. If your own outbound path uses a pool of source addresses, for example a cloud firewall or an SNAT pool, send the administrator the full list of addresses that pool can present. They need every one of them, for the reason given in When it doesn't work, item 5.

When it doesn't work

Ordered by how often each appears in support tickets. Each is tagged with the case it applies to.

1. No passive port range is configured (Case A). CompleteFTP hands out a different unpredictable port on every listing, so a firewall rule cannot exist. In the log at Debug, this is PASV port range [0,0]. Fix it as in Case A step 1.

2. The external IP address is set, and now the LAN cannot connect (Case A). The mirror image of the problem it was set to solve, and the single most common regression after a configuration change. Internal clients are told to open the data channel to the public address. Removing the setting fixes the LAN and breaks remote clients again, which is why the three options in Case A step 3 exist.

3. The range is open on one firewall but not on all of them (Cases A and B). On the server side: host firewall, perimeter firewall, cloud security group, NAT device. On the connecting side: your own egress firewall, which is the hop a Case B reader owns. The connection is silently dropped at whichever hop lacks the rule, and every hop produces an identical timeout at the connecting client.

4. The passive port range is exhausted, and CompleteFTP allocates outside it (Case A). Each concurrent session holds one port for its whole life, and the port is only returned when the session ends. Once every port in the range is in use, the next session gets an operating-system-assigned port instead, outside the range and therefore blocked by the firewall rule you wrote. The Debug log shows Next passive port = 0 followed by Bound to <interface address> on port 0, while the connecting client is sent a real, out-of-range port. Size the range for peak concurrent sessions, not for files: a session that uploads 500 files still uses one port. A session that is abandoned rather than closed holds its port until the connection is torn down, so set an idle-session timeout on a busy server: Timeout for idle sessions (seconds) under Limits and Timeouts, or timeoutIdle from the CLI, which like every timeout there is stored in milliseconds.

5. The data connection arrives from a different address than the control connection (Case A). An Azure Firewall, a load balancer, an outbound proxy or an SNAT pool at the connecting client's end can use one address for the control connection and another for the data connection. CompleteFTP accepts the TCP connection and then rejects it, logging a message containing matches neither control channel IP. The remedy is the IP white-list (in addition to client IP) setting: add every address the other end can present. It takes literal IP addresses only, comma-separated, with no ranges and no CIDR, so a pool of addresses has to be listed in full. An address in that white-list also bypasses IP filtering and auto-banning for the data connection, so keep the list to addresses you trust.

6. Connection attempt on PASV port 50017 without PASV sent - disconnecting (Case A). Something connected to the passive port when CompleteFTP was not expecting a data connection. The listener stays bound to its port for the whole session but only accepts one data connection per PASV or EPSV, so this is what a second connection to that port looks like. Two of the common sources are yours to stop: a firewall or security appliance that probes the port after seeing it advertised, and a load-balancer health check aimed at the passive range. The third, a client that opens the data connection twice, has to go back to whoever runs that client. It is a warning, not the cause, unless it appears in place of the transfer rather than alongside it.

7. The router rewrites the PASV reply for FTP but cannot for FTPS (Cases A and B). Some NAT routers replace the LAN address in the reply with the external one themselves, and some of those terminate the connection when they do not find a LAN address to replace. This is what Use external IP address for = FTPS only is for: the router keeps rewriting unencrypted FTP, and FTPS gets the hard-coded address it cannot see. The remedy is a server-side change, so a Case B reader asks for it rather than making it.

8. The IP filter or auto-ban drops the data connection (Case A). Both are applied to the passive data connection as well as the control connection. A filter that permits the connecting client's address but not the address its data connection comes from, or an auto-ban triggered by repeated failures, drops the data connection and only writes a line at Information level.

9. The CompleteFTP server is multi-homed and the connecting client reached an interface you did not expect (Case A). The listener binds only to the interface the control connection arrived on, so the firewall rule has to allow the range on that interface. Check the Listening IP addresses setting for the site.

10. Failed to bind port 50004 on interface 10.0.0.5 in the log (Case A). Another process holds that port. CompleteFTP tries the next port in the range and carries on, so a few of these are survivable, but a range that overlaps the operating system's dynamic port range will produce them continuously. If every port in the range fails, the connecting client gets a 501 and the log carries No available ports in the range.

11. Timed out waiting for RETR/STOR/LIST/APPE command (Case A). The connecting client opened the data connection and then never sent the command it was for. This only occurs when Timeout for PASV wait has been set, and the units trap in the settings table above is the usual explanation: a value intended as seconds was stored as milliseconds and now expires immediately.

12. One client works and another does not against the same server (Cases A and B). Almost always EPSV versus PASV, or a client that substitutes the control connection's address for the one in the reply while another obeys the reply literally. Compare the two transcripts at the 227 or 229 line before changing anything on the CompleteFTP server.

Reading the log

Server logs are in <dataDir>\Logs on Windows, where <dataDir> is %ProgramData%\Enterprise Distributed Technologies\Complete FTP, and in /var/log/completeftp on Linux. Diagnostics.log has everything; Errors.log carries Warning level and above, so the two lines that matter most reach it without raising the logging level at all.

Visible at the default Information level:

2026-08-03 09:14:02,551 WARN PassiveListener [Session.41:Default Site.21:ftpuser:192.168.1.50] The client instructed the server to await an incoming passive data connection, but it never arrived. Is a router or a firewall blocking connections? Read more at https://enterprisedt.com/products/completeftp/doc/guide/howtofirewalls
2026-08-03 09:14:02,551 WARN PassiveListener [Session.41:Default Site.21:ftpuser:192.168.1.50] Connection attempt on PASV port 50017 without PASV sent - disconnecting

The first line is the diagnosis for most of this article: CompleteFTP bound the port, told the connecting client about it, and no connection ever arrived. It is written when the session ends with the listener still waiting, which is normally the moment the connecting client gives up. It says nothing about which firewall, only that the data connection did not reach the CompleteFTP server.

In those two lines 192.168.1.50, the last field of the session tag, is the connecting client's address. In the block below, the address in Bound to is the CompleteFTP machine's own interface, which is a different thing and the one item 9 is about.

Only visible at Debug:

2026-08-03 09:13:58,120 DEBUG PassiveListener [Session.41:Default Site.21:ftpuser:192.168.1.50] PASV port range [50000,50099]
2026-08-03 09:13:58,121 DEBUG FTPConfiguration [Session.41:Default Site.21:ftpuser:192.168.1.50] Next passive port = 50017
2026-08-03 09:13:58,122 DEBUG PassiveListener [Session.41:Default Site.21:ftpuser:192.168.1.50] Bound to 192.168.1.10 on port 50017
2026-08-03 09:13:58,340 DEBUG PassiveListener [Session.41:Default Site.21:ftpuser:192.168.1.50] OnConnected

Raising the level takes effect on apply, without restarting the service, and it is not retroactive: the lines only exist for connections made after the change. A PASV port range line reading [0,0] means no range is configured. An OnConnected line means the data connection did arrive, which moves the investigation past everything in Case A steps 1 and 2. A transfer answered at once with 425 Can't open data connection. is a different failure again: the data socket could not be created, and the reason is written to Diagnostics.log at Error level.

The 20 seconds in Connection timed out after 20 seconds of inactivity is the connecting client's own timeout, not a CompleteFTP setting. Left unconfigured, CompleteFTP waits indefinitely for the data connection, so no server-side timeout needs adjusting to make a slow client work.

Related