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


3 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