Setting open_basedir on a dedicated PHP-FPM pool limits which parts of the filesystem that site's requests can touch. That matters on shared hosts, multi-site stacks, and any server where one compromised application should not read a sibling document root, deployment secret, or writable data directory outside its own pool boundary.
When the directive is applied with php_admin_value[open_basedir] inside a pool file, every request handled by that pool inherits the same filesystem allowlist. That keeps the boundary attached to the web runtime for that pool instead of every PHP SAPI on the host, and the admin-level setting cannot be widened later with ini_set() from application code.
open_basedir is still only an extra safety net, not complete isolation. PHP resolves symlinks before checking access, disables the realpath cache while the restriction is active, and expects every legitimate base path to be listed explicitly, including the document root, session directory, upload temporary directory, cache or framework storage path, and any shared asset directory the site genuinely needs.
Related: How to disable PHP functions
Steps to set PHP open_basedir for a PHP-FPM pool:
- Locate the target PHP-FPM pool file and check whether it already defines open_basedir.
$ sudo grep -REn '^\[|php_admin_value\[open_basedir\]' /etc/php/8.3/fpm/pool.d /etc/php-fpm.d 2>/dev/null /etc/php/8.3/fpm/pool.d/example.com.conf:1:[example.com] /etc/php/8.3/fpm/pool.d/example.com.conf:492:php_admin_value[open_basedir] = /var/www/example.com:/var/lib/php/sessions:/tmp
On Debian and Ubuntu, pool files usually live under /etc/php/<version>/fpm/pool.d. On RHEL, Rocky, AlmaLinux, and CentOS Stream, they commonly live under /etc/php-fpm.d. If the pool name is known but no open_basedir line appears, add one to that pool file instead of assuming the main php.ini controls it.
Related: How to find PHP configuration files
Related: How to create a PHP-FPM pool - Back up the target pool file before changing the restriction.
$ sudo cp /etc/php/8.3/fpm/pool.d/example.com.conf /etc/php/8.3/fpm/pool.d/example.com.conf.bak-$(date +%Y%m%d%H%M%S)
Leaving out a required directory can break uploads, sessions, cache writes, framework storage, or log writes as soon as the pool is reloaded.
- Open the pool file and add or update the open_basedir allowlist with every directory the site must read or write.
$ sudoedit /etc/php/8.3/fpm/pool.d/example.com.conf
; keep the allowlist limited to this site and its required writable paths php_admin_value[open_basedir] = /var/www/example.com:/var/lib/php/sessions:/tmp
Use absolute directories only, keep the list tight to the current site instead of copying paths from another pool, and separate multiple Linux paths with a colon. If the application uses a custom session directory, cache directory, storage mount, or upload_tmp_dir, add that exact path as well.
PHP resolves symlinks before checking open_basedir, so add the real target directory when the application follows a symlink into another path.
The special value . depends on the current working directory and is less predictable than an explicit path list.
Related: How to set the PHP session save path
- Test the full PHP-FPM configuration and confirm that the pool resolves the expected open_basedir value.
$ sudo php-fpm8.3 -tt [25-Mar-2026 23:03:03] NOTICE: php_admin_value[open_basedir] = /var/www/example.com:/var/lib/php/sessions:/tmp ##### snipped ##### [25-Mar-2026 23:03:03] NOTICE: configuration file /etc/php/8.3/fpm/php-fpm.conf test is successful
Use the binary that matches the installed branch, such as php-fpm8.4 on newer Debian or Ubuntu packages or php-fpm on many RHEL-family hosts. -tt prints resolved pool values as well as the final test result, while -t is enough when only a pass or fail answer is needed.
- Reload the PHP-FPM service so new workers use the updated allowlist.
$ sudo systemctl reload php8.3-fpm
Ubuntu and Debian typically use a versioned unit such as php8.3-fpm, while many RHEL-family hosts use php-fpm.
- Create a temporary probe file inside the site's document root to read the runtime value from the web SAPI.
$ sudo tee /var/www/example.com/public/open-basedir-check.php >/dev/null <<'PHP' <?php header('Content-Type: text/plain'); echo ini_get('open_basedir'), PHP_EOL; PHPReplace /var/www/example.com/public/ with the document root served by the target virtual host.
Remove the probe file after the check so the configuration value is not left exposed over HTTP.
- Request the probe file over HTTP and confirm that the response matches the intended allowlist.
$ curl -s http://example.com/open-basedir-check.php /var/www/example.com:/var/lib/php/sessions:/tmp
Checking over HTTP confirms the value used by the web SAPI. A matching CLI value alone does not prove that the pool override is active.
If the response is narrower than the pool file, application code may be tightening open_basedir with ini_set() inside the allowed tree. If it is wider or still shows an older value, the request is likely not hitting the edited pool or another admin-level override such as web-server PHP_ADMIN_VALUE is taking precedence.
- Remove the probe file after the check succeeds.
$ sudo rm /var/www/example.com/public/open-basedir-check.php
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
