skip to content

In PHP, how do .user.ini files work, which SAPIs read them, and why can a change take minutes to appear?

level: middleimportance: should knowfreq 34%

answer

  1. CGI and FastCGI only, FPM included
  2. script's directory up to document root
  3. only INI_ALL, PERDIR and USER
  4. user_ini.cache_ttl default 300
  5. Apache module uses .htaccess

basics

~20 s

.user.ini files are per-directory ini files read only by the CGI and FastCGI SAPIs, PHP-FPM included. PHP scans from the script's directory up to the document root and caches them for user_ini.cache_ttl, 300 seconds by default.

solid answer

~40 s

A **`.user.ini`** file holds ini directives for one directory tree. Only the **CGI/FastCGI SAPIs**, which include PHP-FPM, process them; under Apache's module you use `.htaccess` with `php_value` and `php_flag` instead. For each request PHP looks in the requested script's directory and every parent up to the document root; a script outside the document root gets only its own directory scanned. Only directives whose mode is `INI_ALL`, `INI_PERDIR` or `INI_USER` are honoured, so `INI_SYSTEM` ones such as `disable_functions` are ignored. The files are cached: `user_ini.cache_ttl` defaults to `300` seconds, so an edit can take up to five minutes to apply. The file name comes from `user_ini.filename`, default `.user.ini`; an empty value turns the feature off.

code

ini · 6 lines
ini
; /var/www/contest/public/.user.ini
upload_max_filesize = 20M
post_max_size = 24M
memory_limit = 256M
; Ignored: INI_SYSTEM directives cannot be set here
disable_functions =

go deeper

for a junior

Know that .user.ini lets a site change some PHP settings per directory when PHP runs through FastCGI or PHP-FPM.

for a middle

Explain the directory walk up to the document root, the allowed modes, the 300-second cache, and the .htaccess alternative under Apache's module.

for a senior

Diagnose delayed or ignored per-directory settings by SAPI, cache TTL and changeable mode, and keep dangerous directives at system level.

for a principal

Set the rules for what tenants may configure per directory and how operators audit and lock the rest across shared infrastructure.

## What .user.ini is for On shared hosting you rarely control php.ini, yet sites need per-application settings: bigger upload limits for a photo-contest site, a longer time limit for an export page. For PHP running as **CGI or FastCGI**, which includes **PHP-FPM**, the answer is a **`.user.ini`** file: an ordinary ini file placed in a directory of the site. ## Which SAPIs read it | How PHP runs | Per-directory mechanism | |---|---| | PHP-FPM (`fpm-fcgi`) | `.user.ini` | | `php-cgi` (`cgi-fcgi`) | `.user.ini` | | Apache's module (`apache2handler`) | `.htaccess` with `php_value` / `php_flag` | | CLI (`cli`) | neither; use `-d` or php.ini | The manual states it directly: these files are processed only by the CGI/FastCGI SAPI, and under Apache's module `.htaccess` files give the same effect. ## How PHP finds the files 1. It starts in the **directory of the requested PHP file**. 2. It walks **up** directory by directory to the **document root** (`$_SERVER['DOCUMENT_ROOT']`), reading each `.user.ini` it finds. 3. If the script lives **outside** the document root, only its own directory is scanned. So a `.user.ini` in the document root applies to the whole site, and one in `admin/` adds or overrides values for scripts under `admin/`. ## What it may set Only directives whose changeable mode is **`INI_ALL`**, **`INI_PERDIR`** or **`INI_USER`** are recognised. That covers the usual needs: - `upload_max_filesize`, `post_max_size` (`PERDIR`); - `memory_limit`, `max_execution_time` (`ALL`); - `output_buffering` (`PERDIR`). `INI_SYSTEM` directives are ignored, so a tenant cannot, for instance, unset `disable_functions` or change `extension_dir` from a `.user.ini`. Loading extensions is also out of reach. An operator can additionally lock values for a pool so that per-directory files and `ini_set()` cannot override them; how that is done in FPM pool files is a separate topic. ## Caching: why changes are slow Reading every parent directory on every request would be expensive, so PHP caches the parsed files per directory. Two directives, both `INI_SYSTEM`, control the feature: | Directive | Default | Meaning | |---|---|---| | `user_ini.filename` | `.user.ini` | name of the file to look for; empty string disables the scan | | `user_ini.cache_ttl` | `300` | seconds before a cached file is re-read | With the default, an edit to `.user.ini` can take **up to five minutes** to take effect, a common source of "I changed it and nothing happened". Waiting out the TTL or reloading PHP-FPM applies it sooner. ## Verifying the effect - Call `ini_get('upload_max_filesize')` from a script **inside** the directory tree; a script elsewhere does not see the file. - In `phpinfo()`, the **local value** reflects `.user.ini` while the **master value** still shows php.ini's. - Remember that `php` in a shell ignores `.user.ini` entirely, so testing from the CLI proves nothing. ## A worked example The photo-contest site runs on PHP-FPM with the document root `/var/www/contest/public`. The team adds `public/.user.ini` raising upload limits, and `public/admin/.user.ini` raising `memory_limit` for the image-processing admin pages. - A request for `public/upload.php` reads `public/.user.ini` only. - A request for `public/admin/resize.php` reads `public/admin/.user.ini` and then `public/.user.ini`, so it gets both the upload limits and the higher memory limit. - A cron script in `/var/www/contest/bin/` is run by the CLI, which ignores `.user.ini` altogether. For the first five minutes after the files are created, requests may still use the previous values, because of the cache TTL. ## Security notes - A `.user.ini` is a plain file inside the document root, so make sure the web server does not serve it to visitors. - Anyone who can write files in the web root can also write a `.user.ini`, which is one reason `INI_SYSTEM` keeps the dangerous directives out of its reach.

  • You edit .user.ini and the value stays old for a few minutes. Why?
    PHP caches parsed .user.ini files for user_ini.cache_ttl seconds, 300 by default. Until the cache entry expires, requests keep the previous values. Waiting or reloading PHP-FPM applies the change.
  • Can a tenant re-enable a disabled function through .user.ini?
    No. disable_functions is INI_SYSTEM, and .user.ini only honours INI_ALL, INI_PERDIR and INI_USER directives, so the line is ignored. Only server-level configuration can change it.
  • Does a .user.ini in the document root affect a script in a sibling directory outside it?
    No. PHP scans from the script's own directory up to the document root. For a script outside the document root, only that script's directory is scanned.

saying these in an interview costs you the question

  • .user.ini is read by every SAPI, including the CLI and Apache's module.
  • Changes to .user.ini apply on the very next request.
  • A .user.ini can load extensions or change disable_functions.
  • PHP reads .user.ini only from the document root, never from subdirectories.
  • Testing ini_get() from the shell confirms a .user.ini works.