The chown www-data:www-data is applied only to the .htaccess file, and only inside the if block (i.e., only on first run when .htaccess doesn't exist yet). The uploads/ directory itself — created by mkdir -p — is never chowned. It stays root:root.
Additionally, on subsequent deploys where .htaccess already exists, the entire if block is skipped, so no chown happens at all.
The Fix
Add a chown on the uploads/ directory outside the if block, so it runs on every startup:
The added line chown -R www-data:www-data /var/www/html/wp-content/uploads/ ensures that:
The uploads/ directory itself is owned by www-data (not just the .htaccess inside it)
It runs on every container start, not just the first time
The -R flag covers any files/subdirectories that may have been created as root (e.g., by backup restores or other processes)
This is safe to run repeatedly — chown on already-correct ownership is a no-op, and the uploads/ directory is typically small enough that the recursive operation is negligible.
Fixes https://git.coopcloud.tech/coop-cloud/wordpress/issues/56
## The Bug
The `chown www-data:www-data` is applied **only to the `.htaccess` file**, and **only inside the `if` block** (i.e., only on first run when `.htaccess` doesn't exist yet). The `uploads/` directory itself — created by `mkdir -p` — is never chowned. It stays `root:root`.
Additionally, on subsequent deploys where `.htaccess` already exists, the entire `if` block is skipped, so no chown happens at all.
## The Fix
Add a `chown` on the `uploads/` directory **outside** the `if` block, so it runs on every startup:
```bash
UPLOADS_HTACCESS=/var/www/html/wp-content/uploads/.htaccess
if [ ! -f "$UPLOADS_HTACCESS" ]; then
mkdir -p /var/www/html/wp-content/uploads
cat > "$UPLOADS_HTACCESS" <<'EOF'
# Prevent PHP execution in uploads directory
<<FilesMatch "\.(?i:php|phtml|phar)$">
Require all denied
</FilesMatch>
EOF
chown www-data:www-data "$UPLOADS_HTACCESS"
fi
chown -R www-data:www-data /var/www/html/wp-content/uploads/
```
The added line `chown -R www-data:www-data /var/www/html/wp-content/uploads/` ensures that:
1. The `uploads/` directory itself is owned by `www-data` (not just the `.htaccess` inside it)
2. It runs on **every** container start, not just the first time
3. The `-R` flag covers any files/subdirectories that may have been created as `root` (e.g., by backup restores or other processes)
This is safe to run repeatedly — `chown` on already-correct ownership is a no-op, and the `uploads/` directory is typically small enough that the recursive operation is negligible.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #56
The Bug
The
chown www-data:www-datais applied only to the.htaccessfile, and only inside theifblock (i.e., only on first run when.htaccessdoesn't exist yet). Theuploads/directory itself — created bymkdir -p— is never chowned. It staysroot:root.Additionally, on subsequent deploys where
.htaccessalready exists, the entireifblock is skipped, so no chown happens at all.The Fix
Add a
chownon theuploads/directory outside theifblock, so it runs on every startup:The added line
chown -R www-data:www-data /var/www/html/wp-content/uploads/ensures that:uploads/directory itself is owned bywww-data(not just the.htaccessinside it)-Rflag covers any files/subdirectories that may have been created asroot(e.g., by backup restores or other processes)This is safe to run repeatedly —
chownon already-correct ownership is a no-op, and theuploads/directory is typically small enough that the recursive operation is negligible.Sorry for introducing this bug and thank you for fixing it.
@@ -55,6 +55,8 @@ EOFchown www-data:www-data "$UPLOADS_HTACCESS"Maybe this line can be deleted, because it's redundant with
chown -Ron/var/www/html/wp-content/uploads/You're right! Just done that.