एरर समाधान · डेवलपर एरर

PHP failed to open stream: Permission denied - Fix Ownership

Fix PHP's failed to open stream: Permission denied warning by checking file owner, directory execute bits, the PHP-FPM user, SELinux and open_basedir.

Error messageWarning: fopen(/var/www/html/cache/file.txt): failed to open stream: Permission denied

लेखक: SiteDownCheck संपादकीय टीम · अपडेट: अक्टूबर 14, 2026 · 4 मिनट पढ़ने में

The message failed to open stream: Permission denied is a PHP warning raised when a function such as fopen(), file_put_contents(), include, require, copy() or move_uploaded_file() asks the operating system for a file and the OS says no. PHP does not decide permissions; the kernel does, based on which user the PHP process runs as. The full line looks like:

Warning: file_put_contents(/var/www/example.com/storage/cache/a.json): failed to open stream: Permission denied in /var/www/example.com/app/Cache.php on line 41

Note the two paths. The first (inside the parentheses) is the file PHP could not open. The second is the PHP script that tried. All your work happens on the first path.

Step 1: find out who PHP runs as

PHP runs as the web server's or PHP-FPM pool's user, not as you. Check:

ps -eo user,comm | grep -E "php-fpm|apache2|httpd|nginx" | sort -u
grep -E "^(user|group)" /etc/php/*/fpm/pool.d/www.conf

Common users are www-data (Debian, Ubuntu), apache or nginx (RHEL family), and a per-site user on shared hosts and panels. From a PHP script, echo get_current_user(); echo exec('whoami'); shows the owner of the script and the running user (remove it right after).

Step 2: check every directory in the path

A file is reachable only if the user has execute (x) permission on every parent directory, plus the right permission on the file itself. namei shows each hop:

sudo -u www-data namei -l /var/www/example.com/storage/cache/a.json
ls -ld /var/www/example.com/storage/cache

A parent such as /var/www/example.com with mode 700 owned by another user blocks everything below it, even if the final folder is 777. To read a file, the user needs r on the file and x on the directories. To create or delete files, the user needs w and x on the containing directory.

Step 3: set ownership and modes correctly

Prefer fixing the owner over opening permissions wide:

sudo chown -R www-data:www-data /var/www/example.com/storage /var/www/example.com/cache
sudo find /var/www/example.com/storage -type d -exec chmod 755 {} \;
sudo find /var/www/example.com/storage -type f -exec chmod 644 {} \;

If a deploy user owns the code and PHP only needs to write to a few folders, give only those folders to the PHP user, or use a shared group with the setgid bit (chmod g+ws) and add both users to it.

Step 4: SELinux or AppArmor

On RHEL, CentOS, Rocky and Alma, SELinux can deny access even when Unix permissions are right. Look for denials:

sudo ausearch -m avc -ts recent | tail
ls -Z /var/www/example.com/storage

Label writable directories so the web server may write to them:

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/example.com/storage(/.*)?"
sudo restorecon -Rv /var/www/example.com/storage

On Ubuntu, check AppArmor logs in dmesg or /var/log/syslog if a profile restricts PHP.

Step 5: other causes

  • open_basedir produces a different message ("open_basedir restriction in effect"), but check it anyway in php.ini or the pool config if only some paths fail.
  • Read-only file system or a full disk. df -h and touch a test file. A mount may have flipped to read-only after errors.
  • Wrong path. A missing directory typically gives "No such file or directory," but a path that points to a directory instead of a file gives "failed to open stream" with different wording. Print the real path with var_dump(realpath($path)).
  • Different user for CLI and web. A cron job run as root creates files that the web user cannot later overwrite. Run scheduled jobs as the same user as the web process.
  • Docker volumes. The host UID owning a bind-mounted folder may not match the container's PHP user. Align UIDs or use chown in the entrypoint.
  • Uploads: move_uploaded_file() fails when the destination is not writable. Check upload_tmp_dir as well.

WordPress and frameworks

In WordPress the same error appears when plugins write to wp-content, or when updates fail; see could not create directory. In Laravel, storage and bootstrap/cache must be writable by the PHP user. In both cases the fix is the same: correct owner, correct mode, correct label.

Verify

  1. Re-run the failing action and confirm the warning is gone from the PHP error log (/var/log/php-fpm/ or the web server's error log).
  2. Test as the PHP user: sudo -u www-data touch /path/to/dir/testfile.
  3. Check the page still returns the right status with the HTTP स्टेटस चेकर. A fatal error after the warning often returns a 500; see fix 500 internal server error.
  4. Monitor the page with an अपटाइम मॉनिटर and a keyword check.

Turn display_errors off in production and log errors to a file instead, since warnings print full server paths to visitors.

अगले कदम

सामान्य प्रश्न

Should I set permissions to 777?

No. It makes files writable by every user on the system. Fix the owner instead and use 755 for directories and 644 for files, with write access only where PHP needs it.

Why does it work from the command line but not the browser?

Your shell user and the web server user are different accounts. Test with sudo -u www-data to reproduce what PHP sees.

The directory is writable but I still get the error. Why?

A parent directory may lack execute permission, SELinux may be blocking, or the filesystem may be read-only. Use namei -l and ausearch.

Is this warning a security problem?

The warning itself is not, but showing it to visitors leaks paths. Log errors privately.

Get notified when this fix updates

Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.

यह पेज सामान्य जानकारी है, जिसे हमारी संपादकीय टीम ने लिखा है और दस्तावेज़ीकरण बदलने पर अपडेट किया जाता है। सर्वर और प्रदाता अलग-अलग होते हैं, इसलिए बदलाव पहले सुरक्षित तरीके से जाँचें। देखें: संपादकीय नीति और अस्वीकरण.