How to Force SCP to Copy Hidden Files Like .htaccess: Complete Guide for Linux Users


25 views

When transferring web files between servers, hidden configuration files like .htaccess, .env, and .gitignore are often crucial yet frustratingly omitted by default SCP behavior. The standard recursive copy command:

scp -rp src/ user@server:dest/

fails to capture these dotfiles, leaving applications broken after deployment.

The behavior stems from Unix shell expansion rules - wildcard patterns like * explicitly exclude dotfiles by design for safety. SCP inherits this behavior when processing directory contents.

Here are three production-tested approaches:

1. Rsync Alternative

The most robust method uses rsync which handles hidden files properly:

rsync -avz --include='.*' --exclude='*' src/ user@server:dest/
rsync -avz src/ user@server:dest/

First command copies only hidden files, second copies everything else.

2. Tar Pipe Technique

For systems without rsync, create a tar archive including hidden files:

tar czf - -C src/ . | ssh user@server "tar xzf - -C dest/"

3. SCP Workarounds

If you must use SCP, these patterns work:

scp -rp src/{*,.*} user@server:dest/

Or explicitly list hidden files:

scp -rp src/.htaccess src/.env user@server:dest/

Watch for these special cases:

  • The . and .. entries when using .* patterns
  • Permission preservation with -p flag
  • Symbolic link handling with -L if needed

For CI/CD pipelines, use this bash function:

transfer_all_files() {
  local src=$1
  local dest=$2
  rsync -avz --no-perms --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r \
    --include='.*' --exclude='lost+found' \
    "$src" "$dest"
}

When transferring web files like .htaccess, .env, or Git's .gitignore, you'll notice SCP's default behavior ignores dotfiles. This isn't a bug - it's by design in many Unix tools to prevent accidental operations on system directories (. and ..).

The common pattern:

scp -rp src/ user@server:dest/

misses hidden files because:

  • Wildcard expansion happens before SCP sees the files
  • The -r flag doesn't override dotfile exclusion

Method 1: Explicit File Listing

Best for known files:

scp -rp src/{.htaccess,.env,config.php} user@server:dest/

Method 2: Rsync Alternative

For complete directory mirroring:

rsync -avz --include='.*' src/ user@server:dest/

Method 3: Tar Pipe (Most Reliable)

Works with complex permissions:

cd src && tar czf - . | ssh user@server "tar xzf - -C /path/to/dest"

Watch for:

  • Symlinks (add -L to rsync or -h to tar)
  • Special permissions (preserve with -p)
  • Maximum command length (tar avoids this)
Method Speed Reliability
SCP with wildcards Fast Medium
Rsync Variable High
Tar pipe Slowest Highest

For deployment scripts:

#!/bin/bash
SRC="/var/www/html/"
DEST="user@production:/var/www/html/"
EXCLUDE="--exclude=*.tmp --exclude=*.bak"

rsync -azP --delete $EXCLUDE --include='.*' $SRC $DEST