PHP Configuration: php.ini, FPM, and Per-Directory Files

Published Updated

PHP configuration controls runtime behavior such as error reporting, memory limits, upload limits, timeouts, and extension settings. Old tutorials often treated one php.ini file as the whole system, while current deployments may add PHP-FPM pools, scanned INI files, containers, managed-host settings, and per-directory overrides.

Picture the configuration as transparent sheets laid over a base drawing. The main php.ini supplies the base, later scanned files can cover individual values, the web server or PHP-FPM pool can add stronger settings, and a script may change only the directives allowed at runtime. Debugging starts by finding the process and the sheets it loaded.

Identify the SAPI

A SAPI is PHP's interface to the environment running it. CLI, PHP-FPM, the Apache module, and CGI-style installations can load different files or read them at different times.

Ask the command-line binary which configuration it loaded:

$ php --ini
Configuration File (php.ini) Path: /etc/php/8.5/cli
Loaded Configuration File:         /etc/php/8.5/cli/php.ini
Scan for additional .ini files in: /etc/php/8.5/cli/conf.d

The output identifies the CLI sheet stack. It does not prove that a web request uses the same path because PHP-FPM may run another binary, pool, chroot, or container image.

For a temporary local web diagnostic, print the SAPI and loaded file through the actual request path:

<?php
header("Content-Type: text/plain");

echo "SAPI: ", PHP_SAPI, PHP_EOL;
echo "php.ini: ", php_ini_loaded_file() ?: "none", PHP_EOL;

Request the file through the web server, record the result, and delete it. A public diagnostic endpoint exposes runtime details that should remain private.

How PHP Configuration Layers Override Each Other

PHP reads the base configuration first, then scanned files in their load order. A PHP-FPM pool or web-server directive can override allowed values for that request environment. A .user.ini file may add per-directory values for CGI and FastCGI SAPIs, and ini_set() can change runtime-modifiable directives for one script.

The last applicable sheet determines the effective value, subject to each directive's change mode. A php_admin_value in an FPM pool is stronger than an ordinary script setting, so ini_set() cannot cover it later.

Edit the Main php.ini File

The main php.ini file supplies the base runtime configuration. A production starting point can make error behavior and resource limits explicit:

display_errors = Off
log_errors = On
error_log = /var/log/php/error.log
memory_limit = 256M
upload_max_filesize = 16M
post_max_size = 20M
date.timezone = Australia/Melbourne

post_max_size must leave room for the entire request body, so it normally needs to be larger than upload_max_filesize. Keep application secrets out of this file. Database passwords, API tokens, and signing keys belong in deployment-controlled environment variables or a secret manager.

Restart PHP-FPM and Verify the Change

PHP reads php.ini when the process starts. A long-running PHP-FPM service therefore needs a restart or reload before web requests see the edited value. The service name depends on the operating system package; this Debian or Ubuntu example uses the PHP 8.5 service:

sudo systemctl restart php8.5-fpm
systemctl is-active php8.5-fpm

active

The final active output confirms that FPM restarted, but the diagnostic page still needs to confirm the effective setting. Check the same SAPI that serves the application because a successful CLI command only confirms the CLI configuration.

Scanned Configuration Files

Many installations split configuration across a base file and fragments in a conf.d directory. PHP scans files ending in .ini in alphabetical order, so a later fragment can cover an earlier setting.

; 90-app-production.ini
display_errors = Off
log_errors = On
memory_limit = 256M
max_execution_time = 30

An environment-specific fragment keeps application runtime choices visible without editing a distribution file that a package or image update may replace. Use php --ini or php_ini_scanned_files() to see the loaded order instead of guessing which sheet won.

Web-server and Pool Settings

PHP-FPM pools can set values for one site or service. The php_admin_value form prevents a script from overriding the deployment limit:

; www.conf
php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 16M
php_value[post_max_size] = 20M

Apache module deployments may set PHP directives in virtual-host or .htaccess configuration. Do not assume those files affect PHP-FPM, nginx, or a managed host. Identify the serving SAPI before choosing the layer.

Per-directory .user.ini Files

CGI and FastCGI SAPIs, including PHP-FPM, can read per-directory .user.ini files. They help on hosts that allow application-specific values without access to the service configuration.

upload_max_filesize = 16M
post_max_size = 20M
max_execution_time = 30

PHP caches these files according to user_ini.cache_ttl, so a changed value may stay stale until the cache expires. Keep the file away from user-writable upload directories and remember that it can change only directives permitted at the per-directory level.

Script-level Overrides

ini_set() changes an allowed directive for the current script. It can suit one bounded import or report that needs a different limit:

<?php

$previousLimit = ini_set("memory_limit", "512M");

if ($previousLimit === false) {
    throw new RuntimeException("memory_limit cannot be changed");
}

The return value is the previous setting as a string, or false when the option does not exist or cannot be changed. Shared error behavior, upload ceilings, and timeout policy belong in the runtime layer where operators can audit them.

Development and Production Settings

SettingDevelopmentProduction
display_errorsOnOff
log_errorsOnOn
error_reportingBroadBroad
upload_max_filesizeClose to productionDocumented limit
memory_limitClose to productionDocumented limit

Development can display more detail, but resource limits should stay close enough to production that uploads and imports fail during testing instead of after deployment.

Common Pitfalls & Debugging

A Value Stays Stale After an Edit

Symptom: the diagnostic page shows the old value after php.ini changes. Cause: the wrong file was edited, a later scanned file covered the value, or the serving process did not reload. Fix: inspect the web SAPI's loaded file and scanned list, restart the correct FPM or Apache service, then request the diagnostic again.

CLI and FPM Show Different Values

Symptom: php -i reports one limit while the web application reports another. Cause: CLI and FPM load separate sheet stacks. Fix: compare php --ini with php_ini_loaded_file() through the web request, then edit and reload the SAPI that owns the failing code.

Production Displays Error Details

Symptom: a failed request prints a stack trace or filesystem path in the browser. Cause: display_errors is enabled in a production layer. Fix: turn display off at the production runtime layer, keep log_errors on, verify the log destination, and return a safe response.

Frequently Asked Questions

How can you find the loaded php.ini file?

Run the php --ini command to inspect the command-line SAPI. For a web request, inspect php_ini_loaded_file() or a temporary phpinfo() page through the same web server and PHP-FPM pool that serves the application. Delete any public diagnostic page after recording the result.

Does every php.ini change require a restart?

A long-running server SAPI must reload its configuration before it sees a changed php.ini file. Restart or reload PHP-FPM for FPM requests, or reload Apache when PHP runs as an Apache module. CLI reads configuration when each command starts, so the next command sees the change.

Can ini_set() change every PHP directive?

No. Each directive has a change mode that controls where it can be set. Some directives can change inside a script, while system-level or php_admin_value settings cannot. Check the directive's documented change mode before relying on ini_set().

Should display_errors be enabled in production?

No. Production should log errors and return a safe response because displayed errors can expose filesystem paths, queries, configuration, and other application details. Keep display_errors available only in a private development environment and confirm that log_errors sends the full diagnostic to an operator.

Can a per-directory .user.ini setting override a php_admin_value set in the FPM pool?

No. The php_admin_value form fixes a directive at the pool level and blocks any later layer, including .user.ini files and ini_set(), from changing it. Use ordinary php_value in the pool when a directory-level .user.ini file should still be allowed to override the setting.

Next Steps

Return to the PHP tutorials hub, then connect runtime configuration to the LAMP request path. Use PHP security fundamentals for production error, upload, session, and output boundaries, and PHP and MySQL with PDO for the database layer.

Sources

  1. [1]
  2. [2]
  3. [3]
  4. [4]
  5. [5]
    ini_set
    (php.net)
  6. [6]