426 Connection Closed — Transfer Aborted Mid-File
증상
- File transfer begins normally but fails partway through with "426 Connection closed; transfer aborted"
- Large files fail consistently while small files (< 1 MB) succeed every time
- The transfer fails at roughly the same byte count on repeated attempts, suggesting a fixed timeout rather than random network loss
- Binary files arrive on the server but are truncated or corrupt
- Log shows "426 Failure writing network stream" — indicating a server-side write error rather than a client disconnect
- Large files fail consistently while small files (< 1 MB) succeed every time
- The transfer fails at roughly the same byte count on repeated attempts, suggesting a fixed timeout rather than random network loss
- Binary files arrive on the server but are truncated or corrupt
- Log shows "426 Failure writing network stream" — indicating a server-side write error rather than a client disconnect
근본 원인
- Network instability (packet loss, route flap) dropping the TCP data connection mid-transfer, especially on long-distance or wireless paths
- Stateful firewall or NAT session timeout killing idle data connections — when transfer speed drops below the NAT's activity threshold, the session is cleared and subsequent packets are dropped
- Server disk full (`df -h` shows 100% usage) — the server accepts the data stream but cannot write to disk, eventually closing the connection with 426
- vsftpd `data_connection_timeout` set too low (default 300 s) for slow client connections — the server closes the data socket before the transfer completes
- ASCII transfer mode selected for a binary file — line ending translation corrupts the stream and may cause the server to emit an error mid-transfer
진단
**Step 1 — Check transfer mode (ASCII vs Binary)**
```bash
# In the ftp> prompt:
type
# Output: 'Using ascii mode' or 'Using binary mode'
# For all non-text files, binary mode is required:
binary
# In FileZilla: Transfer → Transfer Type → Binary
```
**Step 2 — Check server disk space**
```bash
df -h /var/ftp/
# 100% or near-100% use means writes will fail mid-transfer
# Also check inode exhaustion (equally fatal):
df -i /var/ftp/
```
**Step 3 — Inspect vsftpd timeout settings**
```bash
grep -E 'data_connection_timeout|idle_session_timeout' /etc/vsftpd.conf
# Default: data_connection_timeout=300 seconds
# If missing, defaults apply
```
**Step 4 — Trace the TCP connection drop**
```bash
# On the server, watch for RST packets on the data port range:
sudo tcpdump -n 'tcp[tcpflags] & tcp-rst != 0 and port 50000'
# A RST arriving from the client's direction suggests a NAT timeout
```
**Step 5 — Test network stability**
```bash
# Measure packet loss on the path:
mtr --report --report-cycles 60 YOUR_SERVER_IP
# > 0.5% packet loss is enough to disrupt TCP transfers of large files
```
```bash
# In the ftp> prompt:
type
# Output: 'Using ascii mode' or 'Using binary mode'
# For all non-text files, binary mode is required:
binary
# In FileZilla: Transfer → Transfer Type → Binary
```
**Step 2 — Check server disk space**
```bash
df -h /var/ftp/
# 100% or near-100% use means writes will fail mid-transfer
# Also check inode exhaustion (equally fatal):
df -i /var/ftp/
```
**Step 3 — Inspect vsftpd timeout settings**
```bash
grep -E 'data_connection_timeout|idle_session_timeout' /etc/vsftpd.conf
# Default: data_connection_timeout=300 seconds
# If missing, defaults apply
```
**Step 4 — Trace the TCP connection drop**
```bash
# On the server, watch for RST packets on the data port range:
sudo tcpdump -n 'tcp[tcpflags] & tcp-rst != 0 and port 50000'
# A RST arriving from the client's direction suggests a NAT timeout
```
**Step 5 — Test network stability**
```bash
# Measure packet loss on the path:
mtr --report --report-cycles 60 YOUR_SERVER_IP
# > 0.5% packet loss is enough to disrupt TCP transfers of large files
```
해결
**Fix 1 — Switch to binary transfer mode**
```bash
# ftp> prompt
binary
# Or set it in FileZilla: Transfer → Transfer Type → Binary
# For automated scripts (Python ftplib):
ftp.sendcmd('TYPE I') # I = Image (binary)
```
**Fix 2 — Free up disk space on the server**
```bash
# Find large files:
sudo du -sh /var/ftp/* | sort -rh | head -20
# Remove old logs or temp files:
sudo find /var/log -name '*.gz' -mtime +30 -delete
```
**Fix 3 — Extend vsftpd data connection timeout**
```ini
# /etc/vsftpd.conf
data_connection_timeout=900 # 15 minutes for slow connections
idle_session_timeout=600
```
```bash
sudo systemctl restart vsftpd
```
**Fix 4 — Resume interrupted transfers (REST command)**
```bash
# ftp> prompt — resume from byte offset 1048576:
rest 1048576
retr largefile.zip
# In Python ftplib:
ftp.sendcmd('REST 1048576')
with open('largefile.zip', 'ab') as f:
ftp.retrbinary('RETR largefile.zip', f.write, rest=1048576)
```
**Fix 5 — Adjust NAT keepalive / session timeouts**
If a corporate or cloud NAT is clearing idle sessions, configure TCP keepalive on the data socket or switch to SFTP, which uses a single persistent SSH connection that is much more resilient to NAT timeouts.
```bash
# ftp> prompt
binary
# Or set it in FileZilla: Transfer → Transfer Type → Binary
# For automated scripts (Python ftplib):
ftp.sendcmd('TYPE I') # I = Image (binary)
```
**Fix 2 — Free up disk space on the server**
```bash
# Find large files:
sudo du -sh /var/ftp/* | sort -rh | head -20
# Remove old logs or temp files:
sudo find /var/log -name '*.gz' -mtime +30 -delete
```
**Fix 3 — Extend vsftpd data connection timeout**
```ini
# /etc/vsftpd.conf
data_connection_timeout=900 # 15 minutes for slow connections
idle_session_timeout=600
```
```bash
sudo systemctl restart vsftpd
```
**Fix 4 — Resume interrupted transfers (REST command)**
```bash
# ftp> prompt — resume from byte offset 1048576:
rest 1048576
retr largefile.zip
# In Python ftplib:
ftp.sendcmd('REST 1048576')
with open('largefile.zip', 'ab') as f:
ftp.retrbinary('RETR largefile.zip', f.write, rest=1048576)
```
**Fix 5 — Adjust NAT keepalive / session timeouts**
If a corporate or cloud NAT is clearing idle sessions, configure TCP keepalive on the data socket or switch to SFTP, which uses a single persistent SSH connection that is much more resilient to NAT timeouts.
예방
- Always use binary (TYPE I) transfer mode for non-text files in automated scripts — ASCII mode is almost never correct and causes silent corruption
- Monitor server disk usage and set alerts at 80% capacity; a full disk causes 426 errors that are hard to diagnose without server access
- Configure vsftpd `data_connection_timeout` based on the slowest expected client connection speed rather than leaving it at the default 300 seconds
- Implement transfer resumption logic (REST/RETR with offset) in batch upload scripts so interrupted transfers can continue without restarting from zero
- Switch long-running or large-file transfers to SFTP with a library that supports automatic reconnection (e.g., Paramiko with retry logic)
- Monitor server disk usage and set alerts at 80% capacity; a full disk causes 426 errors that are hard to diagnose without server access
- Configure vsftpd `data_connection_timeout` based on the slowest expected client connection speed rather than leaving it at the default 300 seconds
- Implement transfer resumption logic (REST/RETR with offset) in batch upload scripts so interrupted transfers can continue without restarting from zero
- Switch long-running or large-file transfers to SFTP with a library that supports automatic reconnection (e.g., Paramiko with retry logic)