The permissions on a file are only the last check. Several other things can deny the write while the file itself looks perfectly correct, which is what makes this one so confusing.

1. A parent directory

namei -l /var/www/site/storage/logs/app.log

Every directory in the path needs execute permission for you to reach the file. namei prints the whole chain with its permissions, and the offending row is usually obvious.

2. It is the process, not you

ps aux | grep php-fpm | head -3            # which user is it running as\nsudo -u www-data test -w /var/www/site/storage && echo writable || echo NOT

Your shell is you; the web server is www-data. Testing as the right user is the difference between a five-minute fix and an afternoon.

3. SELinux, on RHEL, Alma and Rocky

ls -Z /var/www/site/storage\nsudo ausearch -m avc -ts recent | tail\nsudo chcon -R -t httpd_sys_rw_content_t /var/www/site/storage\nsudo setsebool -P httpd_can_network_connect on
SELinux denials do not appear in the application log at all. If permissions are right and the write still fails on one of those distributions, this is almost always why.

4. Immutable, or a read-only mount

lsattr /var/www/site/config.php     # ----i--------- means immutable\nsudo chattr -i /var/www/site/config.php\nmount | grep " / "                  # ro means read-only, often after a disk error
A filesystem remounted read-only is the kernel protecting a failing disk. Check dmesg -T | tail before doing anything else - that is a hardware problem, not a permissions one.

Then set it correctly

Not 777. Ownership and the group bit are the right tools - see ownership, umask and the group trap.