Series 7 — Part 3 of 6

Apache runs PHP as www-data, not as your user. SSH keys and file permissions that work perfectly in your terminal will silently fail when Apache tries to use them. This article explains why, and how to fix it correctly.

The Problem

# As your user (deploy): works fine
ssh git@github.com  # succeeds
php webhook.php     # succeeds

# Apache (www-data): silent failure
# Apache tries to use /var/www/.ssh/id_rsa owned by deploy:deploy with permissions 600
# www-data cannot read the key → authentication fails with a cryptic error

The failure manifests as: "Permission denied (publickey)" or "Could not create directory '/var/www/.ssh'" — depending on which step fails first.

SSH Key Ownership

# Option 1: Give www-data its own SSH key
sudo -u www-data ssh-keygen -t ed25519 -f /var/www/.ssh/id_ed25519 -N ""
# Register the public key where needed (GitHub, server, etc.)

# Verify permissions (SSH requires these exact permissions)
ls -la /var/www/.ssh/
# drwx------  2 www-data www-data  4096  id_ed25519
# -rw-------  1 www-data www-data   411  id_ed25519
# -rw-r--r--  1 www-data www-data    97  id_ed25519.pub

# Configure SSH to use this key
echo "Host *" > /var/www/.ssh/config
echo "  IdentityFile /var/www/.ssh/id_ed25519" >> /var/www/.ssh/config
chmod 600 /var/www/.ssh/config
chown www-data:www-data /var/www/.ssh/config

File Permission Strategies for Shared Access

When both your user and www-data need to read/write the same directory (e.g., /var/log/the legal SaaS platform), use a shared group:

# Create a shared group
sudo groupadd webteam
sudo usermod -aG webteam www-data
sudo usermod -aG webteam deploy

# Set the directory to group-writable with sticky bit
sudo chown root:webteam /var/log/the legal SaaS platform
sudo chmod 2775 /var/log/the legal SaaS platform  # setgid: new files inherit the group

# Log files created by www-data will be group:webteam, readable by deploy

Why CLI Tests Pass but Web Requests Fail

When you run php test.php from the terminal, the script runs as you (deploy). When Apache runs the same script, it runs as www-data. Any resource the script accesses — files, SSH keys, environment variables, system calls — is accessed as www-data.

Test as www-data to reproduce the exact failure:

sudo -u www-data php /var/www/webhook.php
sudo -u www-data curl http://localhost:9010/health
sudo -u www-data ls /var/log/the legal SaaS platform

What to Watch For

  • HOME environment variable — When PHP runs as www-data, getenv('HOME') returns '/var/www' or is unset. Tools that look for config in ~/.config will look in /var/www/.config — which usually doesn't exist.
  • ACLs for fine-grained access — If group-based sharing is too coarse, use POSIX ACLs: setfacl -m u:www-data:rw /path/to/file. More precise but harder to audit.