How to Modify .bashrc for SSH Login Messages Without Breaking SFTP File Transfers


30 views

Many developers need to customize their SSH login experience by setting environment variables or displaying welcome messages in their .bashrc. However, when these scripts output any text, it breaks SFTP connections (used by clients like FileZilla) because SFTP expects a clean protocol communication channel.

# Typical problematic .bashrc content
echo "Welcome to server $(hostname)"
export MY_VAR="development"

SFTP operates over the same SSH connection but expects a clean binary protocol. Any extraneous output from startup scripts interferes with this protocol handshake, causing:

  • Connection hanging indefinitely
  • "Connection closed by server with exit code 128" errors
  • File transfer failures in GUI clients

Here are several tested approaches to maintain both login customization and SFTP functionality:

Method 1: Check Session Type

The most reliable method checks if the session is interactive:

# In your .bashrc
if [[ $- == *i* ]]; then
    # Interactive shell code here
    echo "Welcome $(whoami)"
    export PATH="$PATH:/custom/path"
    
    # Your custom prompt
    PS1='[\u@\h \W]\$ '
fi

Method 2: Detect SFTP Specifically

For more precise control when using FileZilla:

# Check for SFTP environment variables
if [ -z "$SFTP_CONNECTION" ] && [ -z "$SSH_TTY" ]; then
    # Regular SSH session - safe to output
    echo "Loading development environment..."
    source ~/env_vars.sh
fi

Method 3: Separate Configuration Files

A clean architecture approach:

# In .bashrc
[ -f ~/.bash_interactive ] && . ~/.bash_interactive

# In .bash_interactive
# Put all your interactive-only configurations here
function welcome() {
    echo "=== System Information ==="
    uptime
    echo "=========================="
}
welcome

Verify your changes work with both SSH and SFTP:

# Test SSH connection
ssh user@server

# Test SFTP connection
sftp user@server

# Alternative test using SSH directly
ssh user@server -s sftp

For sophisticated environments where you need different behavior for different clients:

# Detect FileZilla specifically
if [ -n "$SSH_CLIENT" ] && 
   ! pgrep -f "FileZilla" >/dev/null 2>&1 && 
   [ "$TERM" != "dumb" ]; then
    # Your interactive customizations
    neofetch  # Example system info tool
fi
  • Never put echo statements outside conditional blocks
  • Avoid commands that always produce output (like fortune)
  • Remember that .bash_profile may also be read in some configurations
  • Environment variables may affect behavior - test with env -i bash --noprofile --norc

For most use cases, the interactive shell check ([[ $- == *i* ]]) provides the best balance of reliability and functionality. Combine this with proper sourcing of additional configuration files for maintainability.


When working with remote servers, we often encounter this dilemma: We want rich interactive shell experiences through SSH (custom prompts, environment variables, welcome messages) but simultaneously need clean SFTP file transfers. The fundamental issue stems from how OpenSSH handles different connection types:

SSH (interactive shell) → invokes .bashrc/.bash_profile
SFTP (file transfer) → expects pure binary protocol without shell initialization output

Common workarounds like TERM/dumb checks don't reliably work because:

  • Some SFTP clients still set terminal types
  • Modern shells may bypass these checks
  • Environment variables can interfere

The Filezilla-specific error "Connection closed by server with exit code 128" typically indicates shell initialization output corrupted the SFTP protocol handshake.

Here's the bulletproof method I've used in production systems:

# In ~/.bashrc
if [[ $- == *i* ]] && [[ -z "$SSH_CLIENT" ]] || [[ -n "$SSH_TTY" ]]; then
    # Interactive SSH session detected
    echo "Welcome to $(hostname)"
    export MY_VAR="custom_value"
    # Other interactive-only configurations
fi

For complex setups, maintain separate configuration files:

# ~/.bashrc
if [ -n "$PS1" ]; then
    # Interactive shell
    if [ -n "$SSH_CONNECTION" ]; then
        source ~/.bashrc_interactive_ssh
    else
        source ~/.bashrc_interactive_local
    fi
fi

Verify your setup works with these commands:

# Test SSH interactivity
ssh user@host "echo \$MY_VAR"

# Test SFTP connectivity
sftp user@host <

For Filezilla users experiencing timeout issues:

  1. Go to Edit → Settings
  2. Select "Transfer Settings"
  3. Increase timeout to 60 seconds
  4. Check "Limit number of simultaneous connections" (set to 2)

For advanced users with server access:

# In /etc/ssh/sshd_config
Match Group sftponly
    ForceCommand internal-sftp
    ChrootDirectory /home/%u
    PermitTunnel no
    AllowAgentForwarding no
    AllowTcpForwarding no
    X11Forwarding no