In PHP, how do .user.ini files work, which SAPIs read them, and why can a change take minutes to appear?
answer
- CGI and FastCGI only, FPM included
- script's directory up to document root
- only INI_ALL, PERDIR and USER
- user_ini.cache_ttl default 300
- 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 sA **`.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; /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
Know that .user.ini lets a site change some PHP settings per directory when PHP runs through FastCGI or PHP-FPM.
Explain the directory walk up to the document root, the allowed modes, the 300-second cache, and the .htaccess alternative under Apache's module.
Diagnose delayed or ignored per-directory settings by SAPI, cache TTL and changeable mode, and keep dangerous directives at system level.
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.