Hardening php.ini for Production Websites: Each Setting Explained
The php.ini settings that matter on a live website, what each one does, and a sample configuration you can hand to your developer or hosting provider.
Hardening php.ini for a production website comes down to a handful of settings: hide errors from visitors and log them instead (display_errors = Off, log_errors = On), stop advertising your PHP version (expose_php = Off), lock down session cookies (session.cookie_secure, session.cookie_httponly, session.cookie_samesite, session.use_strict_mode), and set sensible upload limits. disable_functions and open_basedir can add a layer, but the PHP manual itself says they are not complete security boundaries. Below, each setting is explained in plain language, with a sample configuration you can hand to your developer or hosting provider.
Before you change anything
- Find the right file. A server can have several php.ini files (command line, web, each PHP version). In a browser-facing context, the "Loaded Configuration File" line in
phpinfo()shows which one is active. Check it on a temporary page and delete that page right away. - Know where overrides live. Control panels such as cPanel (MultiPHP INI Editor) and Plesk (PHP Settings) write per-site overrides. PHP-FPM pool files can also set values with
php_admin_value, which scripts cannot change later. - Start from php.ini-production. PHP ships two templates,
php.ini-developmentandphp.ini-production. The production one already sets safer defaults such asdisplay_errors = Off. - Test on staging first, then reload PHP-FPM or the web server so changes take effect.
Error handling: log, don't display
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/site-error.log
What it does: with display_errors on, PHP prints warnings and errors straight into the web page. Those messages can reveal file paths, database names, query fragments and plugin versions, which helps an attacker plan. The PHP manual is direct about it: displaying errors "should never be used on production systems." Logging keeps the same information where your team can see it and visitors cannot.
Tip: make sure the log file is outside the web root and that someone actually reviews it or ships it to your monitoring.
Stop advertising your PHP version
expose_php = Off
What it does: by default PHP adds an X-Powered-By: PHP/8.x.y header to every response. Turning it off doesn't fix any vulnerability, but it stops automated scanners from instantly matching your site to known flaws for that exact version. It can only be set in php.ini, not per directory.
Session cookie flags
session.use_strict_mode = 1
session.use_only_cookies = 1
session.use_trans_sid = 0
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = Lax
If your site has logins (a client portal, WooCommerce, a members area), these settings protect the session cookie that keeps users signed in:
use_strict_moderejects session IDs that the server did not create. The PHP manual calls this "mandatory for secure sessions," yet it is off by default.use_only_cookiesanduse_trans_sid = 0keep session IDs out of URLs, where they leak through bookmarks, shared links and logs.cookie_httponlystops JavaScript from reading the session cookie, which limits the damage of a cross-site scripting bug.cookie_securesends the cookie only over HTTPS. Enable it when the whole site runs on HTTPS (it should).cookie_samesite(LaxorStrict) tells browsers not to send the cookie on most cross-site requests, which helps against cross-site request forgery.Laxis the safer choice if users arrive from email links and expect to stay signed in.
Many frameworks (WordPress, Laravel, Symfony) manage their own cookies, so check that the application's settings match too.

File upload limits
file_uploads = On
upload_max_filesize = 10M
post_max_size = 12M
max_file_uploads = 20
What it does: these cap how large an uploaded file can be and how much data one request can send. PHP's defaults are 2 MB per file and 8 MB per request; post_max_size must be larger than upload_max_filesize. Set them to what your site genuinely needs, not "as big as possible." Large limits make it easier to exhaust disk space or memory. If the site never accepts uploads, file_uploads = Off is the simplest protection of all.
Upload limits are only one part of safe uploads. Validating file types and blocking script execution in upload folders matter more.
Resource limits
max_execution_time = 30
max_input_time = 60
memory_limit = 256M
These keep a single slow or malicious request from tying up the server. Values depend on the application (some imports and reports need more), so treat these as starting points.
disable_functions: a speed bump, not a wall
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
What it does: removes PHP functions that run operating system commands. Many web shells rely on them, so disabling them can blunt a common post-exploitation step.
What to know:
- It can only be set in php.ini (not per directory or at runtime).
- The PHP manual warns that it "can be circumvented and should not be considered a sufficient security measure for shared hosting environments."
- Some legitimate software needs these functions (image processing, backup plugins, Composer, some control panels). Test before you apply it, and check the error log afterwards.
open_basedir: an extra safety net
open_basedir = /home/site/public_html:/home/site/tmp
What it does: limits which directories PHP scripts can open files from, so a compromised script on one site can't simply read files elsewhere on the server.
What to know: the PHP manual describes it as "just an extra safety net, that is in no way comprehensive." It doesn't restrict Unix sockets, it disables PHP's realpath cache (which can slow busy sites), and real separation comes from running each site as its own system user with its own PHP-FPM pool and correct file permissions.
Other settings worth a look
allow_url_include = Off(the default): keep it off so code can't include files from remote URLs.session.save_path: store sessions in a directory only that site's user can read, not a world-readable/tmp.session.cookie_lifetime = 0: the session cookie ends when the browser closes, unless the application implements "remember me" deliberately.
A short production checklist
- Correct php.ini (and per-site overrides) identified
-
display_errors = Off,log_errors = On, log stored outside the web root -
expose_php = Off - Session cookie flags set; HTTPS enforced site-wide
- Upload and post limits sized to real needs
-
disable_functionsandopen_basedirtested on staging, if used - Changes documented and PHP-FPM reloaded
Want a second set of eyes?
Configuration drift is common: a setting fixed once on one site rarely makes it to the next. Core Florida IT reviews and hardens PHP servers, cPanel/WHM and Linux hosts for businesses in Miami-Dade and South Florida, and remotely nationwide. Request a free IT assessment and we will review your current configuration with you.


