Configuring Passive Port Ranges Properly on Your FTP Server
Passive mode is the answer to most FTP connection problems — right up until you are the one running the server. Behind a firewall or a NAT router, passive mode stops being a free ride: your server must now accept incoming data connections on high-numbered ports, and it falls to you to decide which ports, open them everywhere they need opening, and make sure the server tells clients the right address to connect to. Get any of that wrong and you see the classic symptoms: logins that succeed, directory listings that hang, transfers that die with 425 errors — sometimes only for outside users, sometimes only at the busiest hour of the day.
The good news is that the whole job comes down to three settings and one rule about them: they must agree. This article walks through all three — the passive port range on the server, the matching firewall and NAT rules, and the announced external IP — plus the judgment calls most manuals skip: how big the range should be, which numbers to pick, and how to prove from the outside that it all works.
This is the hands-on server-side chapter of our Active vs Passive FTP series. Every term is defined as we go, so you can follow it even if this is your first FTP server.
Why a Server Needs a Defined Passive Range at All
A one-minute recap. An FTP session uses two connections: the control connection — the long-lived conversation on port 21 that carries commands and replies — and a fresh data connection for each transfer or directory listing. In passive mode, the client sends the PASV command, the server picks a port on its own machine and starts listening on it, and it replies with its IP address and that port number in a 227 response. The client then opens the data connection to that address and port. If any of this is fuzzy, start with our foundation article, active vs passive FTP explained.
The detail that matters here is the middle step: the server picks a port. Which one? Left unconfigured, most FTP servers answer "any free port above 1023" — one of roughly sixty-four thousand possibilities, different for every single transfer.
That is unmanageable for a firewall, because firewall rules have to name their ports ahead of time. A deliberate, narrow opening for a known port or small set of ports is called a pinhole, and you cannot cut a pinhole for a moving target. With an unconfigured server your options are both bad: open every port above 1023 to the machine, which mostly cancels the firewall's purpose, or leave them closed and watch data connections fail at random.
A defined passive port range resolves the standoff. It is a contract with two sides: the server promises to hand out data ports only from an agreed block — say 50000 to 50100 — and the firewall permanently allows exactly that block, nothing more. Small, predictable, auditable. The rest of this article is about writing that contract and making sure everyone honors it.
The Three Settings That Must Agree
The first setting is the passive port range itself, configured in the FTP server software — the pool the server draws from every time a client sends PASV. We will use 50000–50100 as the worked example throughout.
The second is the firewall and NAT configuration. Every barrier between clients and server must allow the range in: the operating system's firewall on the server itself and, if the server sits behind a router, the NAT device in front of it. NAT (network address translation) lets many machines share one public IP address; because of it, inbound connections reach your server only if the router is told to pass them along — a rule known as port forwarding.
The third exists only behind NAT: the announced external IP address. The 227 reply contains a literal IP address, and a server behind NAT knows only its private one. A server setting tells it to quote the public address instead, so clients on the internet dial somewhere that actually reaches you.
These three live in three different places — the server's configuration, one or more firewall consoles, sometimes a cloud provider's control panel — and often belong to different people or change tickets. That is exactly why they drift apart, and a disagreement between any two produces identical symptoms from outside: hangs and 425 errors. Practically every server-side passive failure you will meet is one of the three out of sync with the other two.
The diagram below puts them in one picture: the client on the internet, the firewall/NAT device with its two pinholes — port 21 and the bracketed passive range — the server behind it, and the external address the server announces in its passive replies.
Remember: when passive transfers break, resist the urge to change settings at random. Ask which of the three — range, firewall rules, announced address — no longer agrees with the other two. One of them is lying; find it before you touch anything.
How Big Should the Range Be?
Each data connection borrows exactly one port from the range and holds it for the life of that connection. A large download over a slow link holds its port for many minutes; a directory listing holds one for under a second. Either way, an occupied port is unavailable for anyone else's transfer.
So concurrency is the first sizing input. Thirty users mid-download means thirty ports gone at that instant, and every click through a folder tree briefly claims another for the listing. The range must comfortably exceed the number of data connections open at your busiest moment, not your average.
The second input is less obvious: ports are not returned the instant a connection closes. The operating system keeps a just-closed port reserved for a short cooling-off period called TIME_WAIT — typically a minute or two — so that stray, late-arriving packets from the old connection cannot be mistaken for part of a new one. A burst of short transfers therefore chews through ports faster than it gives them back; a busy sync job can park a surprising number of ports in TIME_WAIT at once.
Honest rule of thumb: size the range at a comfortable multiple — three to five times — of your realistic peak of simultaneous data connections, to absorb that churn. For a typical small-business server with a few dozen users browsing and transferring, a 100-port range like our 50000–50100 example is comfortably enough. And since port numbers cost nothing — a larger range uses no memory or bandwidth, it is only numbers in two config screens — err on the generous side: define 200 or 500 ports now and never think about it again. The only reason not to open "everything above 1024" is that a bounded range is something a firewall admin can read, reason about, and audit.
Under-sizing looks like this: quiet-hours tests pass, then at the day's peak a few unlucky clients get 425 Can't open data connection, and by the time you investigate, the rush has passed and all is well again. Intermittent failures that track business hours are the signature of an exhausted range.
Choosing the Actual Numbers
TCP ports run from 0 to 65535, in three neighborhoods. Ports 0–1023 are the well-known ports, reserved for standard services like FTP's own port 21 — never place a passive range there. Ports 1024–49151 are the registered ports, where thousands of applications have staked claims — a crowded campground. Ports 49152–65535 are the dynamic range, set aside for exactly this kind of short-lived, locally-decided use; high numbers in or just below it are the natural home for a passive range. (If port numbering itself is new to you, our blog post What port is FTP? is a five-minute primer.)
One collision hazard deserves a proper explanation. Your operating system constantly makes outgoing connections of its own — updates, DNS lookups, admin sessions — and for each one it grabs a temporary local port from a pool of ephemeral ports, which usually lives in this same high territory. If your passive range overlaps the pool, an outgoing connection can occasionally squat on a port at the exact moment the FTP server wants to listen on it. The result is a rare, unreproducible transfer failure — the worst kind. Check where your system's pool lives before you commit:
# Linux — show the ephemeral pool (low and high bounds) cat /proc/sys/net/ipv4/ip_local_port_range # Windows — show the dynamic (ephemeral) TCP range netsh int ipv4 show dynamicport tcp
If the pool overlaps your chosen block, either move the block outside the pool or tell the operating system to keep its hands off — Windows can exclude a range with netsh's excludedportrange option, Linux with the ip_local_reserved_ports setting. Collisions are uncommon even without this step, but reserving the block turns uncommon into impossible, and it is free.
Also look sideways before committing: other software on the same host may already claim a block of high ports — another transfer service, a VoIP or media system, a backup agent. A quick glance at current listeners (ss -tln on Linux, netstat -an on Windows) avoids pitching camp in an occupied neighborhood.
Then pick a single contiguous, memorable block. Nothing is magic about particular numbers; the pattern is what matters — round figures, easy to say over the phone, easy to spot in a firewall rule list. Our worked example, 50000–50100, scores on every count: high territory, 101 ports, instantly recognizable in every console it appears in.
| Candidate range | Verdict |
|---|---|
| 1024–1123 | Avoid — deep in registered territory where installed software often squats. |
| 8000–8100 | Avoid — a favorite neighborhood of web applications and development tools. |
| 32768–32868 | Risky — sits inside the default ephemeral pool on many systems. |
| 50000–50100 | Good — high, contiguous, memorable; reserve it from the ephemeral pool if the pool overlaps. |
Finally, write the choice down — the block, the reason, and the fact that the firewall rules mirror it — in the runbook and the firewall change ticket. A passive range only stays correct if the next administrator can discover that it exists.
Announcing the External IP Behind NAT
Now the third setting, and the single most common way passive mode breaks in the wild. The 227 reply contains a literal IP address, and a server behind NAT knows only the address on its own network card — a private one such as 192.168.1.20. Left to itself, it will cheerfully announce exactly that:
227 Entering Passive Mode (192,168,1,20,195,87)
An outside client obeys and dials 192.168.1.20 — an address that, on the client's own network, either does not exist or belongs to some unrelated device. The connection times out and the transfer fails. The cruel part: on the server, everything looks healthy. Login succeeded, PASV was answered, nothing was logged as an error — the failure happened somewhere the server never saw.
The fix is a server-side setting found in every serious FTP server, under names like external IP, NAT address, or public address. Set it to the address clients actually reach you at — 203.0.113.10 in our example — and the reply becomes 227 Entering Passive Mode (203,0,113,10,195,87), which an outside client can act on.
Two honest wrinkles. First, many modern desktop clients notice an obviously private address in a 227 and quietly substitute the address they already have from the control connection. That kindness hides the misconfiguration for months, until a script, an appliance, or a stricter client takes the reply at face value and fails — do not let a forgiving client convince you the server is right. (The newer EPSV command sidesteps this entire class of bugs by putting no address in the reply at all; see our guide to the four data-connection commands.)
Second, the internal-client wrinkle. Once the server announces the public address, clients on the inside of your network are told to connect to it too — and that only works if your router supports hairpin NAT, the ability to reach your own public address from inside. Some routers do not, and there, fixing passive mode for the world quietly breaks it for the office. Two honest ways out: some FTP servers can apply the external address only to connections arriving from outside, keeping the internal address for local clients — check whether yours offers that behavior — or you rely on the router's hairpin support. Test from both sides of the firewall and you will know where you stand.
Making the Firewall Match the Range
With the range chosen and the address set, the remaining work is making every barrier on the path agree. There are usually two, and in the cloud three.
Start with the operating system's own firewall on the server: it needs an inbound allow rule for port 21 (most setups already have one) and for the whole passive range:
# Windows Firewall — allow the passive range in netsh advfirewall firewall add rule name="FTP passive range" dir=in action=allow protocol=TCP localport=50000-50100 # Linux (ufw) — same idea ufw allow 50000:50100/tcp
Next, the NAT device. Port forwarding must send the entire range — not just port 21 — to the server's internal address, 192.168.1.20 here. Look for a port range forwarding feature; creating 101 single-port rules is the wrong road. On business firewalls the same idea appears as a destination NAT rule covering 50000–50100. And point the forward at the server alone, not a wider subnet.
Finally, the cloud caveat that catches almost everyone once: a virtual machine at a hosting provider sits behind two independent layers — the firewall inside the operating system, and an external security layer configured in the provider's console (often called a security group or firewall policy). Both must allow the passive range; either one alone silently blocks it. If you have ever opened Windows Firewall on a cloud VM and wondered why the port still tested closed, this is why — the packet was dropped before it ever reached the machine.
Verifying From the Outside
The cardinal rule of verification: test from a network genuinely outside yours — a phone hotspot, a machine at home, a rented VM. Inside tests can pass while the outside is broken (a missed forward), and, thanks to the hairpin wrinkle, the outside can work while the inside is broken. Only an external test exercises all three settings at once.
Start with a real FTP client: connect, browse a few folders, transfer a small file. Success is boring — the listing appears instantly and the transfer completes. Failure has a distinctive shape: login succeeds, then the first listing hangs for a long pause and ends in 425 Can't open data connection or a "failed to retrieve directory listing" message.
To see exactly what the server is announcing, hold the conversation yourself with a plain TCP tool such as nc or telnet, from that outside network:
$ nc ftp.example.com 21 220 FTP server ready USER alex 331 Password required for alex PASS ******** 230 Login successful PASV 227 Entering Passive Mode (203,0,113,10,195,87)
Read the 227 against your contract. The first four numbers are the announced IP — it must be the public address, never a 192.168 or 10. one. The last two encode the port: multiply the fifth by 256 and add the sixth, so 195 × 256 + 87 = port 50007 — which must fall inside your configured range. Those two checks catch most misconfigurations on their own.
The moment that 227 arrives, the server is listening on port 50007 and waiting. So from a second terminal on the same outside machine — promptly, since the server will not wait forever — make a plain TCP connection to it:
$ nc -vz 203.0.113.10 50007 Connection to 203.0.113.10 50007 port [tcp/*] succeeded! # what failure looks like instead: $ nc -vz 203.0.113.10 50007 nc: connect to 203.0.113.10 port 50007 (tcp) timed out
The two failure shapes tell different stories. A timeout means something silently dropped the attempt — a missing firewall rule or NAT forward; walk the path from the previous section. A connection refused means you reached a machine but nothing was listening there: you waited too long and the listener gave up, or the forward points at the wrong internal address. One caution: do not cold-scan a random port in the range and panic at a refusal. Nothing listens on a passive port until a PASV asks for it, so "refused" on a cold port is normal — it actually proves the path is open all the way to the server. A timeout on a cold port still exposes a blocking firewall, though a few routers refuse instead of dropping, so treat the live PASV-then-connect test as the authoritative one.
Remember: re-verify from the outside after every change to any of the three settings, not just the first time. Pairing "change" with "external test" is what keeps the three from drifting apart — and it takes two minutes with a phone hotspot.
When a test fails and the cause is not obvious, work the symptoms methodically rather than guessing — our companion article on diagnosing FTP mode failures maps each error message and hang pattern to its likely cause.
The Five Classic Mistakes
Five misconfigurations account for nearly every passive-range incident. Check them in order:
- The range is too small for busy periods. Symptom: intermittent
425errors clustering at peak hours, gone when you test at a quiet moment — concurrent transfers plus TIME_WAIT churn exhaust the pool. Fix: widen the range generously (ports are free) and update the firewall and NAT rules in the same change. - The server firewall is open but the NAT forward is missing (or the reverse). One barrier configured is not all barriers configured. Symptom: external data connections time out even though the rule you checked looks perfect. Fix: walk the full path from client to server and give every filtering device the range.
- The range was changed on the server but not on the firewall. A quick tweak moves or widens the range; the firewall keeps enforcing the old contract. Symptom: transfers that worked yesterday fail today, or a widened range only half-works. Fix: treat the server setting and the firewall rules as one atomic change, always edited together.
- Two services on one host hand out overlapping ranges. A second FTP server or a media service claims some of the same numbers, and ports are first come, first served. Symptom: sporadic bind or listen errors and patternless failures on both services. Fix: give each service its own block and document all the blocks in one place.
- The external IP setting still holds the old address. Your public IP changed — new ISP, new hosting — and the server faithfully announces the previous one, now a stranger's or nobody's. Symptom: passive mode "broke by itself" right after a network change. Fix: update the announced address, and put it on the checklist for every future IP change.
Doing It on a Real Server
Whatever software you run, the procedure is the same eight steps. This is the checklist worth pinning up:
- Choose the block — contiguous, high, memorable, sized at a multiple of peak concurrency (for example, 50000–50100).
- Enter the range in the FTP server's configuration.
- If the server sits behind NAT, set the announced external IP address.
- Allow inbound TCP to port 21 and the range in the server's OS firewall.
- Forward port 21 and the range on the NAT device — and in the cloud, allow them in the provider's security layer too.
- Apply the settings or restart the FTP service so the new range takes effect.
- Verify from an external network: real client first, then the
227-and-connect test. - Document the range, the announced address, and where each rule lives.
How much of that is fiddly depends on the software. On Windows, a server such as Sysax Multi Server keeps the passive port range and the announced external IP as ordinary settings in the server configuration — you enter the block and the address rather than editing configuration files — and the firewall and NAT steps are the same as above. A free trial is available from the download page if you want to follow the checklist along on a test machine first.
Keep the Three in Sync
Everything in this article folds back into one sentence: define a generous, high, contiguous passive range on the server; open exactly that range at every barrier in front of it; announce the public address when NAT stands between you and your clients — and never change one of the three without re-checking the other two from the outside. Do that, and passive mode becomes what it was always meant to be: the mode that just works.
From here, two directions are worth your time. If a transfer still misbehaves after the external tests pass, the systematic path is diagnosing FTP mode failures. If a load balancer or proxy fronts your server, the range and address story gains an extra layer, covered in FTP through load balancers and proxies. And for the fundamentals beneath it all, see our FTP Protocol series.
Frequently Asked Questions
Do FTP clients need to be configured with my passive port range?
How many ports should I open for passive FTP?
Why does my server tell clients to connect to a 192.168 address?
Why do transfers fail only during busy periods?
Is opening a hundred high ports a security risk?
From the Sysax team: we build secure file transfer software for Windows — Sysax Multi Server, an FTP, FTPS, SFTP, and HTTPS server, and Sysax FTP Automation for scheduled, scripted transfers. Free trials are on the download page.
