When PHP scripts create files through different execution contexts (web vs CLI), we often encounter permission mismatches. Here's a typical scenario:
// test_permissions.php
<?php
file_put_contents('web_generated.txt', 'Created via web');
echo 'Running as: '.exec('whoami');
Browser output shows apache
user while cron output shows your personal username. This creates permission issues when files need modification from both contexts.
1. Proper User Context Execution
The most elegant solution is running the cron job under the Apache user:
# In crontab -e
* * * * * /usr/bin/sudo -u apache /usr/bin/php /path/to/script.php
Requires sudoers configuration (add to /etc/sudoers):
yourusername ALL=(apache) NOPASSWD: /usr/bin/php /path/to/script.php
2. Group-Based Permission Management
Create a dedicated group for web content:
sudo groupadd webcontent
sudo usermod -a -G webcontent apache
sudo usermod -a -G webcontent yourusername
sudo chgrp -R webcontent /path/to/webdir
sudo chmod -R 2775 /path/to/webdir
The 2775
sets SGID (2) for group inheritance and gives read/write to group (7/5).
3. PHP-Level Permission Control
Modify your PHP script to enforce permissions:
<?php
$file = 'generated_image.jpg';
file_put_contents($file, $image_data);
// Set group ownership to Apache's group
chown($file, fileowner($file));
chgrp($file, 'apache');
chmod($file, 0664); // rw-rw-r--
For more granular control on modern systems:
sudo setfacl -Rm u:apache:rwx,d:u:apache:rwx /path/to/dir
sudo setfacl -Rm u:yourusername:rwx,d:u:yourusername:rwx /path/to/dir
Here's how we implement this in our deployment scripts:
#!/bin/bash
WEB_USER="apache"
WEB_GROUP="apache"
CONTENT_DIR="/var/www/generated_content"
# Ensure proper ownership
sudo chown -R ${WEB_USER}:${WEB_GROUP} ${CONTENT_DIR}
sudo chmod -R 2775 ${CONTENT_DIR}
# Set ACL for development team
DEV_USERS=("user1" "user2")
for user in "${DEV_USERS[@]}"; do
sudo setfacl -Rm u:${user}:rwx ${CONTENT_DIR}
done
This maintains security while allowing both web and CLI access to generated files.
When your PHP script executes through different contexts (web vs CLI), you encounter ownership discrepancies. Through Apache, files are created as apache:apache
, while cron jobs default to your user account (youruser:yourgroup
). This creates a permissions nightmare for file management.
1. Running Cron as Apache Directly
The most elegant solution is to execute the cron job under the Apache user identity. Here's how to implement it:
# In crontab (edit using 'sudo crontab -e')
* * * * * /usr/bin/sudo -u apache /usr/bin/php /path/to/your/script.php
Prerequisites:
# Allow your user to run PHP as Apache without password
youruser ALL=(apache) NOPASSWD: /usr/bin/php
2. Group-based Permission Management
Create a shared group for both Apache and your user:
# Create new group
sudo groupadd webdev
# Add users to group
sudo usermod -a -G webdev apache
sudo usermod -a -G webdev youruser
# Set directory permissions
sudo chown -R :webdev /path/to/web/directory
sudo chmod -R 2775 /path/to/web/directory
This implements the SETGID bit (2) to ensure new files inherit the group.
3. PHP-based Permission Adjustment
Modify your PHP script to explicitly set permissions:
<?php
// After file creation
chmod('/path/to/newfile.jpg', 0664);
chown('/path/to/newfile.jpg', 'apache'); // Requires root privileges
?>
For this to work, you'll need to either:
- Run the script via sudo in cron (as shown in solution 1)
- Configure sudoers to allow specific chown/chmod commands
While universally writable permissions solve the immediate problem, they introduce security vulnerabilities. If you must use this approach:
// In your PHP script
umask(0); // Dangerous!
$fp = fopen('/path/to/file', 'w');
fwrite($fp, $data);
fclose($fp);
Always prefer more granular solutions when possible.
For production systems, I recommend combining solutions 1 and 2:
- Create a
webdev
group with proper directory permissions - Run cron jobs as Apache user via sudo
- Implement proper umask settings in PHP
# Example complete solution
sudo groupadd webdev
sudo usermod -a -G webdev apache
sudo usermod -a -G webdev youruser
sudo chown -R :webdev /var/www/html/uploads
sudo chmod -R 2775 /var/www/html/uploads
sudo chmod g+s /var/www/html/uploads
This ensures all newly created files will be writable by both Apache and your user while maintaining security.