IP filtering and whitelisting in CompleteFTP: what it supports and how to configure it

CompleteFTP has two independent network-level access controls: a rule-based IP filter that you configure, and auto-banning that reacts to failed logins on its own. They interact, and most of the trouble people have with either comes from that interaction or from entering a mask in a format the filter does not actually support. This article states precisely what the filter accepts, what it rejects, the order in which decisions are made, and the specific configurations that look correct but let everyone in.

Two things to get out of the way first, because they account for a large share of support tickets on this subject:

  • The rule-based IP filter requires Professional Edition or Enterprise MFT. In Free and Standard Edition the configured rules are ignored entirely, not partly, entirely. Auto-banning runs in every edition.
  • *CompleteFTP does not support CIDR notation or `` wildcards.** No version does, including 26.1.1. See the mask table below for what to write instead.

The short version

To block one address (Professional or Enterprise MFT): Site/Settings properties → IP filtering and Auto-banningIP filtering → ellipsis (...) → add a rule with action Deny and the address as the mask → close → APPLY CHANGES. The default precedence, Deny over allow, means the deny takes effect immediately against the default allow-all rule.

To whitelist (deny everything except listed addresses):

  1. Open the IP Filtering dialog as above.
  2. Select Allow over deny.
  3. Change the existing Allow all rule to Deny, or delete it.
  4. Add an Allow (unless autobanned) rule for each permitted address or address prefix.
  5. Click Test filter and check an address that should be allowed and one that should not.
  6. Close the dialog and click APPLY CHANGES.

To restrict one user without affecting anyone else, this is the case people most often get wrong. Do not set an allow rule for that user and stop there; on its own it changes nothing. Either:

  • set the user's Allowed IP addresses property and make the site deny by default (see "Restricting one user while leaving the rest alone" below), or
  • set the site's precedence to Allow over deny, then give the user two user-specific rules: Deny with mask all, and Allow for the permitted address. Other users stay unrestricted. The precedence change is not optional, under the default Deny over allow the user's own Deny wins over their Allow and they are locked out completely.

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

completeftp site ipfilter list default                          # show the rules
completeftp site ipfilter precedence default AllowOverDeny      # set the precedence
completeftp site ipfilter add default deny all                  # deny everything
completeftp site ipfilter add default allow 192.168.10.0-255    # except this range
completeftp site ipfilter remove default all                    # delete the rule whose mask is "all"
completeftp site ipfilter test default 192.168.10.7             # check an address

The all in the last two lines is the mask, not a keyword meaning "every rule". remove default all deletes the single site-wide rule, which in a default configuration is the inbuilt Allow all. Under Allow over deny that changes nothing; under Deny over allow it locks everyone out. Removing a specific rule takes the address instead: completeftp site ipfilter remove default 192.168.50.99.

What the filter accepts as a mask. And what it does not

Every rule is a mask plus an action, optionally scoped to one user. This is the complete list of mask forms the matcher understands.

Mask form Example Matches
Full IPv4 address 192.168.1.50 that address only
First one, two or three parts of an IPv4 address 10, 172.16, 192.168.1 every address beginning with those parts, on a dot boundary (192.168 does not match 192.1685.1.5)
Numeric range in the last octet of a full four-part mask 192.168.2.100-150 192.168.2.100 to 192.168.2.150
all, ALL, or a blank mask all every address. Displayed as All in the manager and stored as an empty string
Internet host name mail.example.com whatever the name resolves to, re-resolved every 60 seconds
LAN / NetBIOS network name FILESERVER01 as resolved by the host
IPv6 textual prefix 2001:db8:, fe80:: any address whose printed form starts with that text

And the forms that do not work:

Not supported What happens
CIDR, 192.168.1.0/24, 10.0.0.0/8 Rejected by the manager as an invalid entry. Accepted without validation by the CLI, AdminAPI and JSS config API, where it is stored and then never matches anything
Wildcards, 10.200.*.*, 192.168.1.* Same: rejected by the manager, silently inert everywhere else. Note that the CLI reference and its -h help text both show 192.168.1.* as a valid host format. That is wrong, and a rule copied from those examples never matches, which locks everyone out if the site also has a deny all
Several addresses in one mask, 10.0.0.1, 10.0.0.2 or 10.0.0.1;10.0.0.2 Invalid. One rule per address. (The user property Allowed IP addresses is the one place commas are accepted; it expands them into separate rules)
A dashed range across whole addresses, 192.168.1.0 - 192.168.128.255 Invalid. Only a single octet may carry a range
A range in a partial mask, 10.1-5 Accepted by the manager as valid, and matches nothing at all
A range in any octet other than the last, 10.0-5.1.1 Accepted, and matches far more than intended: octets after the range are not checked
::1 as an IPv6 mask Rejected as invalid (the pattern requires hex digits before the first colon). Loopback is allowed unconditionally anyway. See below

To convert a CIDR block, express it as a last-octet range or as a partial prefix. 10.20.30.0/25 becomes 10.20.30.0-127. 10.20.0.0/16 becomes 10.20. A block that is not aligned to an octet or expressible as one last-octet range needs more than one rule.

The three actions

Action (as shown in the manager) CLI keyword Effect
Deny deny Connection is refused
Allow (unless autobanned) allow Connection is permitted, but auto-banning still applies
Allow always allow-always Connection is permitted and auto-banning is switched off for that address

Allow always is the answer to "how do I stop CompleteFTP banning my proxy / load balancer / monitoring host?", and also the reason a whitelisted address can hammer the server with failed logins indefinitely. Use it deliberately, not as the default choice.

How a decision is reached

The order below is fixed. It is not the order the rules appear in the list, rule order in the grid has no effect on the outcome, only the actions and the precedence setting do.

  1. Edition check. If the edition is Free or Standard, your rules are not consulted at all. The server falls back to: allow the server machine's own addresses; on the Admin site allow 10.0.0.0/8, 192.168.0.0/16 and 172.16.0.0/12 and deny everything else; on every other site allow everything.
  2. The server's own addresses are always allowed, on every site, with auto-banning disabled for them. This covers loopback and every address bound to the machine. You therefore cannot test a filter by connecting from the server itself, such a connection is allowed no matter what the rules say.
  3. All IP filters run, in parallel and independently: the inbuilt rule-based filter plus any custom .NET or JSS IP filter extensions. Each returns Deny, Allow or Allow-always.
  4. Within the inbuilt filter, every rule whose mask matches the address and whose user scope matches is collected, then the strongest one wins according to the site's precedence.
  5. Across filters, the site precedence decides again:
    • Deny over allow (the default): any Deny anywhere wins; otherwise Allow-always; otherwise Allow.
    • Allow over deny: Allow-always wins; then Allow; then Deny.
  6. If nothing matched at all, the precedence supplies the answer: Deny over allow denies, Allow over deny allows. This is why deleting the Allow all rule under Allow over deny changes nothing, and why deleting it under Deny over allow locks everyone out.
  7. Only then is auto-banning consulted, and only if the filter's answer was Allow rather than Allow-always.

Two consequences worth spelling out:

  • A custom IP filter extension can be completely overridden by the inbuilt rules. If you write a geolocation filter that returns deny, and precedence is Allow over deny with the default Allow all rule still present, the inbuilt Allow wins and your extension's decisions are discarded. Delete the inbuilt rules, or keep the default Deny over allow precedence.
  • A custom filter that hits an error denies the connection. If a JSS or .NET filter raises an exception, or cannot be constructed at all, it returns Deny, with the error message as the reason. Under the default precedence that denies everyone, so a broken extension takes the site down rather than being ignored. The message appears in the log.

Per-site or per-user?

The filter belongs to a site. Each site has its own rule set and its own precedence, so in Enterprise MFT different sites can have entirely different policies. Rules are not per folder: there is no way to make one virtual folder require a particular source address. Folder-level access is controlled by permissions and by which users can see the folder, not by IP.

Individual rules can be scoped to one user, and that is the mechanism behind the user property Allowed IP addresses. Two things about user-scoped rules matter in practice:

  • They are hidden by default. In the IP Filtering dialog, tick show user-specific rules or you will be looking at an incomplete picture. Almost every "the rules look right but it doesn't work" case comes down to that box.
  • They are enforced at authentication, not at connection. Before a user has authenticated the server does not know who is connecting, so a user-scoped Deny is skipped and a user-scoped Allow is provisionally honoured. The TCP connection is therefore accepted and the client is rejected during login instead, with FTP/FTPS returning 421.

Setting Allowed IP addresses on a user

Users panel → select the user → Allowed IP addresses. Separate multiple entries with commas; host names are accepted. Each entry becomes one user-specific Allow (unless autobanned) rule, on every site the user is enabled on. An entry the filter cannot parse is refused when you apply:

Invalid IP address/host-name: 10.0.0.0/24

The property is only visible in Professional Edition and Enterprise MFT. If you cannot find it, that is your edition, not your version.

Setting it does not restrict anything by itself. With the default Allow all site rule in place, the user is already allowed from everywhere, and an extra allow rule adds nothing. You must also make the site deny by default, that is the whole point of the Allow over deny + Deny all combination in the guide's tutorial.

Clearing the field does not remove the rules. Blanking Allowed IP addresses is a no-op; the user's existing rules survive. To remove them, open the site's IP Filtering dialog, tick show user-specific rules, and delete the rules there.

Restricting one user while leaving the rest alone

The pattern that actually works, and the one to reach for when someone asks to lock down a single account:

  1. Precedence: Allow over deny.
  2. Delete the site-wide Allow all rule (with Allow over deny, "nothing matched" already means allow, so unrestricted users stay unrestricted).
  3. Add a user-specific rule: action Deny, mask all, user = that user.
  4. Add a user-specific rule: action Allow (unless autobanned), mask = the permitted address, user = that user.

The reverse pattern, deny all site-wide and allow per user, also works, but then every user needs a rule of their own, or they are locked out. That is the trap behind "I only wanted to restrict one user and now nobody can log in".

Auto-banning

Auto-banning is separate, runs in every edition, and is only configurable in Professional Edition and Enterprise MFT, in Free and Standard the whole IP filtering and Auto-banning category is hidden from the manager.

Setting Shipped default Meaning
Trigger count 10 Failures within the period that trigger a ban
Trigger period 300,000 ms (5 minutes) Rolling window over which failures are counted
Duration of ban 3,600,000 ms (1 hour) Set it to zero (x button in the manager) for a ban that lasts until the service restarts

Precise semantics, because the off-by-one matters when you tune it: the ban is applied when the number of failures within the window exceeds the trigger count. At the default of 10 it is the eleventh failure that bans, not the tenth. Setting the trigger count or the trigger period to zero disables auto-banning for that site.

Other behaviour that is not obvious:

  • A successful login clears the ban and the failure history for that address, on FTP/FTPS, SFTP and HTTP/HTTPS alike.
  • Bans are held in memory and are lost on restart. The Monitoring panel's auto-banned IP addresses tab lists the current ones, with Remove selected ban(s) and Permanently ban select IP address(es), the latter writes a real Deny rule into the IP filter. To permanently ban an address that has not been auto-banned, add the Deny rule directly; it will not appear in that list.
  • Auto-banning keys on the client IP only. It cannot be combined with a user name, and it cannot discriminate by port or protocol. All protocols on a site share one ban list.
  • Failed logins against accounts that do not exist still count: they are authentication failures like any other. What auto-banning does not see is repeated attempts within a single connection; for FTP and FTPS that is what the separate site setting Max. login attempts (default 3) is for, and it closes the control connection rather than banning the address.
  • An email notification or process trigger can fire on a ban. The event is called Auto-ban, "When an IP address has been auto-banned".

Disabling auto-banning in Standard Edition

There is no GUI for it. Enable the CLI and set the property:

completeftp site set default autoBanTriggerCount=0

The auto-ban settings are honoured in every edition, and the configuration API does not gate them by edition, so this takes effect on Standard. Note that IP filter rules added the same way on Standard are stored and then ignored.

Before disabling it, consider the two alternatives that solve the usual cause. If a proxy, NAT device or load balancer is being banned because every client appears to come from its address, either configure that device to preserve the original client address, or give the device's address an Allow always rule, which exempts exactly that one address instead of removing brute-force protection from the whole server.

Configuring it from the command line

The CLI is the administration interface on Linux and works out of the box. On Windows it is off by default and must be enabled by adding --enable-admin-cli to the service's binary path; see the guide's Windows CLI page. It is local-only.

# what is configured now
completeftp site ipfilter list default
completeftp site ipfilter precedence default

# add rules  (site, action, host, [user])
completeftp site ipfilter add default deny 192.168.50.99
completeftp site ipfilter add default allow 10.20.30.0-127
completeftp site ipfilter add default allow-always 10.0.0.50
completeftp site ipfilter add default allow 192.168.10 alice

# remove a rule by host  (use "all" for the allow-all rule)
completeftp site ipfilter remove default 192.168.50.99
completeftp site ipfilter remove default all

# precedence
completeftp site ipfilter precedence default AllowOverDeny
completeftp site ipfilter precedence default DenyOverAllow

list prints <action> <user-or-all> from <host-or-all>, so Allow all from all is the default rule and Deny all from 192.168.50.99 is a site-wide deny. The word in the middle is the user scope, not part of the address.

default (or ., or omitting the name) means the first site. Each add, remove and precedence change applies itself, there is no separate apply step as there is in the manager.

The same operations are available over the AdminAPI, which is useful for scripting rule sets across servers:

curl -k -u admin:<password> https://localhost:14985/Admin/API/config/sites/DefaultSite/ipFilter/toArray

curl -k -u admin:<password> --data "{\"arguments\":[\"Allow\",\"10.20.30.0-127\"]}" \
     https://localhost:14985/Admin/API/config/sites/DefaultSite/ipFilter/add

Over the AdminAPI the action names are the internal ones, Allow, AllowAlways, Deny (the CLI accepts allow-always and normalises it). Neither the CLI nor the AdminAPI validates the mask. Anything you send is stored verbatim, so a typo, a CIDR block or a wildcard becomes a rule that never matches, with no error at any point. Always follow a scripted change with a test.

There is no bulk import. The manager offers Export on the IP Filtering dialog but has no corresponding import, so a long list has to be scripted through the CLI or the AdminAPI.

Testing your rules

Two testers exist, and they differ in an important way.

  • Manager → IP Filtering dialog → Test filter. Enter addresses and it shows Allow or Deny plus the rule that decided it. It evaluates with no user, so it cannot show you the effect of user-specific rules, and it does not run custom IP filter extensions.
  • completeftp site ipfilter test <site> <ip> [user]. Takes a user name, so it can show per-user outcomes. It prints action: and reason:, where the reason is the deciding rule rendered as <action> <mask> <user>:

    action: Deny
    reason: Deny 192.168.50.99

Both testers deliberately bypass the "server's own addresses are always allowed" short-circuit, so a local address can test as Deny while a real connection from it would be allowed. That difference is by design; do not chase it.

When it doesn't work

Ordered by how often it comes up.

1. Nothing happens because the edition does not support it. Free and Standard Edition ignore the rules entirely. The IP filtering and Auto-banning category is not even shown in the manager. If you configured rules while trialling Enterprise MFT and then activated a Standard licence, the rules are still in the database and no longer consulted.

2. Changes were not applied. In the manager nothing reaches the server until APPLY CHANGES is clicked. The CLI and AdminAPI apply immediately; the manager and the JSS config API do not.

3. An Allow all rule is overriding everything. A whitelist needs the default allow-all rule changed to deny or deleted. Under Allow over deny an Allow all rule beats every deny rule, including denies from custom filter extensions. This is the single most common cause of "IP filtering is not working, I added the IP and I can still connect from a different one".

4. A user-specific rule exists but nothing denies by default. An allow rule adds permission; it never removes any. See "Restricting one user while leaving the rest alone" above. Tick show user-specific rules before concluding the rule set is what you think it is.

5. A rule that looks fine can never match. Check the mask against the table above. CIDR, * wildcards, comma-separated lists, and ranges in a partial mask are all either refused by the manager or stored inert. If a rule was added by CLI, AdminAPI or by editing the configuration directly, nothing validated it.

6. You are testing from the server itself. Connections from any address belonging to the server machine, including loopback, are allowed unconditionally and are never auto-banned. Test from another host.

7. The client's real address never reaches CompleteFTP. Behind a proxy, reverse proxy, NAT device or load balancer, every connection may present the intermediary's address. CompleteFTP can only filter and log what the TCP connection tells it. Configure the device to preserve the client address, or filter at the device instead.

8. A host-name mask is not resolving. Host-name and network-name masks are re-resolved every 60 seconds by a background thread. When resolution fails, the log records a warning and the rule matches nothing until it succeeds:

2026-07-30 09:14:02,551 WARN IPFilterNameResolver [IPFilter.1] Host not found: mail.example.com

A rule whose mask cannot be parsed as an IP address at all is treated as a host name, which is why a typo'd mask can turn up here.

9. The whole server locked itself out. Deleting the Allow all rule while precedence is Deny over allow, or adding Deny all without allow rules, denies everyone including remote administration. Recover from the server console, where local connections are always permitted, with completeftp site ipfilter add default allow all, or from CompleteFTP Manager running on the server machine itself.

10. Auto-banning is doing its job and it looks like a fault. A ban is applied on the eleventh failure in five minutes by default, and lifted an hour later. Check the log before changing settings: a legitimate client with a stale password, a scheduled job retrying, or an internet-facing server being scanned all produce exactly this.

What the log tells you

You do not need Debug logging for any of these, they are all INFO or WARN, so they appear in Diagnostics.log at the default level, and the WARN ones also appear in Errors.log.

Denied by a rule, before authentication (INFO). The reason is the rule that decided it:

2026-07-30 09:15:22,101 INFO SocketListener [Session.42:Default Site:user?:192.168.50.99] Denied connection on port 22 from 192.168.50.99 due to rule Deny 192.168.50.99

The client is sent the seven bytes BLOCKED and the socket is closed, which many clients report as an unexpected or malformed server response rather than as a refusal.

Denied by a user-specific rule, at authentication (WARN). Identical wording for FTP/FTPS, SFTP and HTTP/HTTPS:

2026-07-30 09:16:40,882 WARN SSHAuthService [Session.43:Default Site.22:alice:10.9.9.9] IP address 10.9.9.9 not allowed by filter: Deny all alice

If you are trying to find out which address a partner is connecting from in order to authorise it, this is the line to grep for, it carries the address.

Denied because the address is auto-banned (INFO):

2026-07-30 09:17:05,004 INFO SocketListener [Session.44:Default Site:user?:10.9.9.9] Denied connection on port 22 from 10.9.9.9 due to auto-ban

A ban being applied (INFO):

2026-07-30 09:17:04,900 INFO AutoBanner [Site.1] IP address 10.9.9.9 was banned for 3600s after 11 login attempts over a period of 300s

With a ban duration of zero this reads was banned until server restart instead.

Auto-banning switched off for an address by an Allow-always rule. Logged at INFO when the decision came from one of your rules, at DEBUG when it came from the inbuilt local-address rule, so seeing it at INFO confirms your Allow-always rule is being applied:

2026-07-30 09:12:00,120 INFO SocketListener [Session.40:Default Site:user?:10.0.0.50] Auto-banning disabled for 10.0.0.50 due to rule AllowAlways 10.0.0.50

Allowed, with the deciding rule (DEBUG only), Allowed connection on port ... due to rule .... Raise the logging level to Debug if you need to see which rule permitted a connection.

Things it cannot do, and what to do instead

Requirement Position
Block by country Not built in. Requires a custom IP filter extension (Enterprise MFT) calling a geolocation service, a JSS extension implementing function getFilterAction(ipAddress) that returns true/false, or "allow", "deny", "allowAlways", or {action, reason}. Sample code is in the guide
Per-folder IP restrictions Not possible. The filter is per site, optionally per user. Restricting a folder to a network needs a custom file-system extension, or a separate site
Restrict which of the server's own addresses a user may connect to Not a filter feature, the filter examines the client address. Use a separate site listening on the other address (Enterprise MFT) and enable the user on only one of them
Per-user logic inside a custom IP filter extension Not available. The extension interface receives the IP address only; the user is not passed to it. Per-user decisions belong in a custom authentication extension
Bulk import of a filter list No import function. Script it through the CLI or AdminAPI
Auto-ban that persists across a service restart Not available. Convert bans you want to keep into Deny rules from the Monitoring panel
Auto-ban by port, protocol or user name Not available. Auto-banning keys on the client address alone
CIDR notation Not supported in any release. Convert to a last-octet range or an octet prefix

Related