Why NAT tears down connections during port forwarding
Port forwarding on Linux is not one rule but two independent translations. The incoming packet receives a new destination address, while the reply receives a new source address.
As long as both directions pass through one node with one ruleset, the connection survives; as soon as the symmetry breaks, the reply bypasses the state table, and the client sees a drop.
The first typical scenario is a change of external address: backup link, second provider, dynamic addressing.
A connection in the state table is tied to a pair of addresses, and when the link switches, the reply arrives from a different one — NAT cannot match it to the original flow. Short requests survive this; long sessions break midway.
An asymmetric reply breaks access more quietly: the internal host replies through a gateway different from the one the request arrived through, and leaves outward with an internal source address.
The external side drops such a packet, while the logs show only “connection established” with no payload.
Overlapping subnets add their own issue: when office and cloud ranges coincide, the port forward lands on the wrong host, and this cannot be fixed by editing one rule.
A separate category is loss of rules after reboot: iptables/nftables keep the ruleset in kernel memory, and until the saved configuration is applied, the node is either open to the outside or closed to its own.
Below is how each scenario is closed by an addressing scheme, persistent rule storage, and connection state monitoring.
What changes after putting rules in order
Order begins when address translation rules stop being a layer of accumulated edits. Each service gets its own set with a described purpose, and chains are assembled in a predictable traversal order.
A new service is added as one block by template — without searching for a place to wedge between old records.
Onboarding a new service is reduced from several hours to tens of minutes.
Previously, changing an address or port required edits in several places, and each was checked on staging; now the configuration is assembled from the service description, and a discrepancy is visible immediately, not at acceptance.
The most unpleasant class of failures is closed separately — asymmetric translation.
If a rule for inbound traffic exists, but the reverse translation is described differently, the connection establishes and breaks on the first reply: from the outside this looks like “works intermittently.” Connection stability rests not on luck but on consistency of both directions, and a single source of rules removes the very possibility of such a discrepancy.
Service recovery time is measured by reading, not searching: the drop log and chain counters show at which step the packet is discarded.
Changing the external address stops being an event — rules are bound to the interface and role, not to a specific value, so migration takes minutes.
Support requests decrease not because they are filtered, but because the reason disappears: a typical “port is not opening” is resolved by a configuration check before it reaches the on-call engineer.
NAT modes: comparison of approaches and limitations
The translation mode determines what the external side sees and how predictably the connection behaves as load grows. Below are four options actually chosen when configuring iptables or nftables, with their strengths and applicability boundaries.
The difference manifests not during initial setup, but during the first topology change: one mode survives service migration with a one-line edit, while the other requires rebuilding the rules entirely.
| Option | When it fits | Limitations |
|---|---|---|
| Dynamic translation (masquerade / SNAT) | Outbound access from the internal network, egress address changes or is unknown in advance | Inbound connection from outside is impossible; the remote side sees only the gateway address |
| Static SNAT binding | The external side verifies the source address by whitelist | One address per service; bindings are maintained manually and desynchronize during migrations |
| Service publishing by ports (DNAT) | The service listens on a fixed port and it cannot be changed | The port belongs to one recipient; changing the internal address or port means editing rules and reloading tables |
| Reverse proxying | Multiple services behind one name, certificates and routing by request name are needed | Adds a node to the chain; the real client address must be passed onward correctly |
The common limitation for all options is that translation works with addresses and ports but does not distinguish the meaning of traffic: content-based filtering and protection from application attacks remain on the service itself.
Therefore the mode is chosen together with the filtering policy, otherwise port forwarding exposes more outward than intended.
Order of work: NAT rule collection, verification, handover
Address translation rules are almost never assembled from scratch: a live machine already has chains, some added manually and documented nowhere.
Therefore we first record the actual topology and current ruleset, and only then design the target scheme — otherwise the first change breaks someone else’s traffic.
- Topology analysis. Which interfaces face outward, where the default gateway is, which services must be accessible externally and which only within the network.
- Rule audit. Dump the active iptables or nftables chains together with packet counters: rules without traffic, duplicates, overlaps, “temporary” entries that have lived for years. Result — a “rule — purpose — owner” table.
- Address translation scheme. Separate where source address substitution is needed and where inbound port forwarding to an internal node is needed, and split flows into separate chains so that editing one service does not affect neighboring ones.
- Rule verification on staging. Repeat the topology in a test environment and run the target rules: required connections pass, extra ones are closed. Save the configuration to a repository.
- Controlled cutover. Apply on the production node with rollback by timer: if the agreed checks fail, the configuration returns to the previous one automatically, without manual work.
- Observation. For the first day, monitor counters, logs, and the number of established connections, compare with the forecast, and separately check behavior under peak load.
- Configuration handover. Hand over the rules file under version control, the translation scheme, the port map, and rollback instructions, and walk through the order of further edits with your team.
Handover package: configurations, schemes, procedures
The handover package is not a set of console screenshots but working configurations in the customer’s repository.
Each artifact is intended for a person who in six months will edit a rule without having written it themselves: they must understand the purpose of the line, find the service owner, and roll back the change without a late-night investigation.
- Rules files with comments — divided by zones: external perimeter, public segment, internal networks. Each set states what traffic it allows, why the rules are in this order, and what breaks if they are reordered.
- Network flow diagram — address translation table, forwards, connection initiators, and return paths. Lets you answer in a minute where traffic came from and where it goes after translation.
- Access policies — a “service → source → port → justification” matrix. By default, everything not described in the matrix is closed, and each exception has an owner and a review deadline.
- On-call policy — response times to connectivity loss and suspicious activity, escalation order, planned change windows, and who approves a perimeter edit.
- Instructions for typical operations — add or remove a port forward, check rule counters, take a traffic dump, restore the set from backup. With commands, expected output, and signs that something went wrong.
- Configuration backups and a proven rollback procedure: restoration is rehearsed on staging, not described “in words.”
- Responsibility map — “rule → service → owner” correspondence, so approval does not stall on one engineer.
The package is designed so that edits can be made by your team, without contacting us for every line. During handover, we walk through each document on a live staging environment and answer questions as we go.
Case: connection drops after a network rebuild
The service ran smoothly until the network was rebuilt: a second external link was added and some subnets were moved behind a separate gateway.
During peak hours, up to 4% of requests ended with a connection error, and every reload of NAT rules on the gateway tore down already established sessions.
The planned change downtime window stretched to 30–40 minutes — some connections did not recover on their own, and clients came back with retries.
What we found
The cause turned out to be one shared connection tracking table for two external interfaces: reply packets returned not through the link the request left from, and the kernel marked the connection invalid.
The second layer was a full rules reload: the old set was removed entirely, and along with it the connection tracking table and all active traffic were lost.
What we changed
We switched to atomic ruleset application: the new configuration is loaded in one operation, old sessions survive, so planned changes stopped being downtime.
We separated connection tracking by perimeters, raised the maximum table size and hash bucket headroom, and aligned timeouts with the real traffic profile. Masquerading of outbound traffic was configured with random port selection — this removed collisions with two links.
What was achieved
After the migration, the share of failed requests at peak dropped from 4% to 0.15%, planned gateway changes proceed without breaking active sessions, and incidents requiring on-call dropped to zero–one per month instead of seven–nine.
The solution is reproducible on any gateway: the logic is the same for old rulesets and for the new stack (Netfilter).
How does masquerading differ from static address translation?
The difference is where the substituted address comes from. Masquerading is dynamic source translation: the kernel itself takes the address of the interface through which the packet leaves outward, and the rule works in the postrouting chain.
Static address translation operates with an explicitly specified “internal — external” pair, and this pair does not change when the route changes or the gateway restarts.
Masquerading covers only the scenario where the connection is initiated from inside: the session is opened by a client behind the gateway, and return packets come back via the state table.
A connection cannot be initiated from outside — the entry appears only from an outgoing packet. Static translation removes this limitation: port forwarding publishes an internal service, and an inbound connection is established independently of outbound traffic.
In maintenance, the modes diverge more strongly. Masquerading is not tied to a specific address and survives its change on the external interface without rule edits, but it hides the real source: to the external side all clients look like one node.
The static scheme is readable by addresses, but each change of external addressing requires a synchronous update of rules and routes.
The modes also break differently. Masquerading hits the state table: with many simultaneous connections to one external node, free source ports run out, new sessions are dropped, and the cause is visible only in kernel counters.
The static option fails with asymmetric routing and when accessing the external address from inside the same network.
The choice of mode comes down to one thing: if only outbound access is needed and the external address is not fixed — masquerading; if the service must accept connections from outside or the address is permanent — static translation.
Controversial points: double translation, asymmetry, logs
Double translation, asymmetric replies, and rule conflict are three cases where NAT behaves not as shown in the diagram. Below are short answers to questions that usually come up at acceptance and in the first month of operation.
We are behind someone else’s border node — double translation
The provider substitutes the address in the external header, and our node sees only it and cannot rely on the real source.
There is no need to pierce both translations: a tunnel to a node with a public address keeps internal addresses in the private range, and the rules on our side describe only our zone of responsibility.
The reply did not return — asymmetric routing
The packet left via one link, but the reply came via another: the connection state did not match, and the request is silently dropped even though the rule is in place.
We align routes — one ingress and one egress for traffic, and the routing policy for backup links takes the connection table into account.
Where to look to see which rule fired
Packet counters show which rules matched, but not the chain traversal order. During investigation, we place a logging rule before the terminal one — it records the packet before the verdict, and a conflict of two neighboring chains is visible immediately. We rate-limit logging so debugging does not fill the disk, and remove it after the investigation.
Let’s discuss the task: send the network diagram and current rules
The more precise the inputs, the fewer assumptions in the work estimate. Analyzing the network diagram and current ruleset takes less time than reconstructing the logic from fragments of configurations on the server — so we ask for exactly what removes uncertainty.
- A network diagram or subnet list: who should see whom, where the gateways are, which segments are isolated, and where connectivity currently breaks.
- Current rules as a dump: filtering, translation, and routing tables in full, not a fragment — some problems live at the junctions of chains and are visible only in the full output.
- What manages the configuration:
iptablesornftables, whether there is a wrapper likefirewalldorufw, and whether the state persists after reboot. - The task statement: port forwarding outward, translation for internet access, publishing a service behind a load balancer, or branch access.
- How rules are applied today: manually by instruction, via a configuration management system, by template, or by a unit at boot.
- Constraints: the allowed change window, rollback requirements, who has access to the nodes, and whether a backup link exists.
- Symptoms and logs: what exactly does not work, whether the failure reproduces constantly or under load, and what the kernel and services log at the moment of failure.
With this set, we return with an implementation plan and work estimate: the order of edits, checkpoints after each step, and a way to return to the previous state with one command.
Describe the task and attach what you already have at hand — we will clarify the rest in correspondence.







