Current situation: PHP2Shell. Two critical PHP vulnerabilities (CVSS 9.2) allow malicious code without login. Patches since 21 October 2026.What to do now →

The php.ini Explained: Structure, Levels and Precedence

Where PHP finds its settings, in which order they apply and who may override them. The basis for any hardening and for the interim measures for PHP2Shell.

Last updated 5 min readPHP knowledge

In short: The php.ini is PHP's central configuration file. PHP reads it at startup: once for server modules, on every call for CGI and CLI. In addition there are supplementary files from a scan directory, settings in the FPM pool or in Apache, .htaccess or .user.ini, and ini_set() in the code. Which level may change a value is determined by its change mode, such as INI_SYSTEM or INI_PERDIR. Values set via php_admin_value can no longer be overridden by the application.

What is the php.ini?

The php.ini is a text file with entries of the form name = value. It determines how PHP behaves: how much memory a script may use, whether errors appear in the browser, how sessions work and which extensions are loaded. Comments begin with a semicolon.

According to the PHP manual, the file is read when PHP starts. For server modules such as mod_php this happens only once, when the web server starts; for CGI and CLI, on every call. PHP-FPM reads its configuration at startup and when the service is reloaded. A change without a restart or reload therefore usually has no effect.

Since PHP 8.3, according to the manual, an entry can refer to an environment variable with a fallback value:

memory_limit = ${PHP_MEMORY_LIMIT}
max_execution_time = ${PHP_MAX_EXECUTION_TIME:-30}

Where does PHP look for the php.ini?

The manual describes a fixed order. First, an explicitly set path applies, such as PHPIniDir in Apache or the -c option for CGI and CLI. Otherwise PHP builds a search path from:

  1. the environment variable PHPRC,
  2. on Windows, the registry entry IniFilePath,
  3. the current working directory, except for SAPIs that disable this, such as the CLI,
  4. the directory of the PHP binary or module,
  5. the path set at compile time with --with-config-file-path.

In this search path, PHP first looks for a file php-SAPI.ini, for example php-cli.ini, and only then for php.ini. PHP then reads all .ini files from the scan directory, which is defined via PHP_INI_SCAN_DIR or at compile time via --with-config-file-scan-dir, in alphabetical order. Which files were loaded is shown by php --ini or the function php_ini_scanned_files().

php --ini
php -r 'var_dump(php_ini_loaded_file(), php_ini_scanned_files());'

Both commands show the state of the command line. A different file may apply to the website, because every SAPI can have its own configuration; more on this under SAPI explained.

Change modes: who may set which value?

Not every setting may be set everywhere. The manual assigns a change mode to each directive:

Mode Where the value may be set
INI_USER in scripts via ini_set(), in the Windows registry and in .user.ini
INI_PERDIR in php.ini, .htaccess, httpd.conf or .user.ini
INI_SYSTEM only in php.ini or httpd.conf
INI_ALL anywhere

Examples from the manual: memory_limit has the mode INI_ALL and the default 128M, post_max_size has INI_PERDIR and the default 8M, upload_max_filesize has INI_PERDIR and the default 2M. expose_php may only appear in the php.ini.

According to the manual, .user.ini files are read only by CGI and FastCGI SAPIs, which includes PHP-FPM. PHP searches for them from the script's directory upwards to the document root and by default re-reads them at most every 300 seconds (user_ini.cache_ttl). Under mod_php, .htaccess takes over this role.

Which value applies in the end? The levels in order of precedence

In simplified terms: the later a level takes effect, the more likely its value prevails, provided the change mode allows it.

  1. php.ini of the respective SAPI
  2. Supplementary files from the scan directory, alphabetically
  3. Server configuration: FPM pool with php_value or php_admin_value, Apache VirtualHost
  4. Directory level: .htaccess with mod_php, .user.ini with FPM and CGI
  5. Runtime: ini_set() in the application code

The exception is decisive: according to the manual, values set via php_admin_value or php_admin_flag cannot be overridden with ini_set(). This is how admins lock security-relevant settings against the application. This is described in detail under php_admin_value: locking settings against overriding.

The php.ini after PHP2Shell: what has changed

The interim measures from php.net for PHP2Shell are almost all INI settings: memory_limit=-1 locked against overriding, a limited post_max_size, session.upload_progress.enabled=0 and, on PHP 8.5, fatal_error_backtraces=0. php.net stresses that session.upload_progress.enabled should not be set via ini_set() or .user.ini but in php.ini, the FPM pool or the server configuration, followed by a restart of all workers. The full list is under Immediate measures if you cannot patch yet.

With the corrected versions 8.5.12, 8.4.27, 8.3.36 and 8.2.35, according to the manual, memory_limit has also changed: it is now a soft limit. The new setting memory_hard_limit_percent defines a hard limit. Without its own value, it is twice memory_limit, or, for more than 1G, that value plus 1G. If it is exceeded, the process ends immediately with exit status 137. How to choose both limits sensibly is explained in Setting memory_limit correctly.

Tip

According to the manual: if a PHP version does not recognise memory_hard_limit_percent, it is affected by CVE-2026-109618 and CVE-2026-109619. For distributions with backports, the package changelog additionally documents the state.

How does PHP SOS help?

  • Determine effective values: For each SAPI we read out which files are loaded and which value applies in the end.
  • Clean up the levels: We remove contradictory entries from panels, pools, .htaccess and .user.ini.
  • Set locks: As part of server hardening we fix security-relevant values via php_admin_value.
  • Provide evidence: We document settings and restarts so that the state can be proven later.

Frequently asked questions

Where is the php.ini located?

That depends on the operating system, package and SAPI. On Debian and Ubuntu there are separate folders per version and SAPI, such as /etc/php/8.3/fpm/php.ini and /etc/php/8.3/cli/php.ini. On the command line, php --ini shows the loaded files; for the website, php_ini_loaded_file() in a temporary test file helps.

In which order are the files in the conf.d folder read?

According to the PHP manual, PHP reads all files ending in .ini in the scan directory in alphabetical order. Later files override values from earlier ones. That is why many files carry numbers such as 20- or 99- in their names.

Why does my change to the php.ini have no effect?

Common reasons: the file of a different SAPI was changed, the service was not restarted, a value in the FPM pool, in the Apache configuration or in a .user.ini overrides it, or a hosting panel rewrites the file on the next save.

What is memory_hard_limit_percent?

A new setting that, according to the PHP manual, was introduced with the security releases of 21 October 2026. It defines a hard memory limit as a percentage of memory_limit. The manual points out that a PHP version that does not know this setting is affected by the PHP2Shell flaws.

Sources

Immediate contact

Every hour counts. Write to us now.

Briefly describe your system, PHP version and observation. We will get back to you, clarify the situation and work remotely until the incident is closed and documented.

Immediate help