Skip to content
ThemesIonic — home
Troubleshooting

Fatal Error: Maximum Execution Time of 30 Seconds Exceeded

The fatal names a file and a line, and that line is where PHP was when the clock ran out — not necessarily what was slow. Raising the limit buys time; reading the trace tells you what was spending it.

Updated 5 min read intermediate

PHP stopped the request at the time limit and reported where it was, not what was slow. The full message reads "Fatal error: Maximum execution time of 30 seconds exceeded in /path/to/file.php on line N"; the number is whatever the server allows — 30 is PHP's default, and 60, 120 or 300 seconds are common host settings. On a site with debugging off, visitors see "There has been a critical error on this website" instead, and the original line lands in the PHP error log — plus the recovery-mode email when a plugin or theme is to blame. Either way the message has two halves. The limit is a PHP setting you can raise. The location is a clue about what consumed the time, and raising the limit without reading it turns a 30-second failure into a 300-second one.

Raise the limit where it takes effect

max_execution_time can be changed by a running script with set_time_limit(), which is why snippets for wp-config.php exist — but hosts commonly disable the function, and the web server has its own timeout that ends the request regardless of PHP's. The settings that hold:

Setup Where
Control panel hosting PHP settings / PHP version selector
PHP-FPM php.ini or .user.ini in the site root: max_execution_time = 300
Apache with mod_php .htaccess: php_value max_execution_time 300
PHP-FPM pool request_terminate_timeout must be at least as high, or FPM kills the worker first
Nginx in front of FPM fastcgi_read_timeout 300; as well, or nginx returns 504 first
Apache as proxy to FPM ProxyTimeout or Timeout directive

The last two rows are why "I raised it and now get a 504 instead" happens: PHP continued, the web server gave up waiting. Match the layers. The procedure for .user.ini versus .htaccess is the same as for upload limits, and a php_value line on a server without mod_php causes a 500.

Step by step for common setups

  • cPanel. In MultiPHP INI Editor, or the PHP options page of "Select PHP Version" on hosts that use it, choose the domain and set max_execution_time to 300. The panel writes a .user.ini or php.ini for you; changes apply within a few minutes.
  • Plesk. The domain's PHP Settings page has a max_execution_time field among the performance settings. If it is greyed out, the administrator has locked it for the plan.
  • Ubuntu or Debian with PHP-FPM. Edit /etc/php/8.3/fpm/php.ini (match your version), then check request_terminate_timeout in the pool file under /etc/php/8.3/fpm/pool.d/, and reload:
sudo systemctl reload php8.3-fpm
sudo systemctl reload nginx   # after raising fastcgi_read_timeout
  • Docker, official WordPress image. Add an ini file and mount or copy it into /usr/local/etc/php/conf.d/, for example timeout.ini containing max_execution_time = 300, then recreate the container.
  • Local tools. Apps that manage PHP for you expose the php.ini per site; XAMPP and MAMP use the php.ini that phpinfo() reports. Edit, then restart the web server from the control panel.
  • Managed WordPress hosting. Limits are usually fixed and documented. Support can sometimes raise them; otherwise move the long task to WP-CLI or the host's background job feature.

Now read the error

The file and line are where the limit hit. Trace upward:

  • wp-db.php or class-wpdb.php — a query was running. Something above it issued it; a plugin querying inside a loop is the typical pattern. The slow query log on the database, or a query monitor plugin on a page that does not time out, shows which query and how many times.
  • class-wp-filesystem-direct.php, class-pclzip.php, file.php — an update or install was copying or unzipping. Disk speed, a huge plugin, or a host limit below the 300 seconds core requests. Update over WP-CLI, which has no web limit.
  • class-http.php, or a file under wp-includes/Requests/ — PHP was handling outbound HTTP responses, often many of them in a loop. A plugin calling an API for every item on a page is the usual pattern; cache the responses or batch the calls.
  • A plugin's own file — the plugin's code is the loop. Deactivate it and reproduce; if the time drops, the plugin is the cause, and plugin conflict testing confirms it.
  • wp-cron.php or wp-includes/cron.php — a scheduled task, started by a visitor's page load, ran under the web limit and took too long. Running cron from a system scheduler through WP-CLI takes it out of the web limit entirely.
  • An import, migration or regeneration tool — the task genuinely needs more than 30 seconds. Those tools usually batch; if yours does not, run it over WP-CLI.

Imports and bulk tasks

A CSV import of ten thousand products, a thumbnail regeneration, a search-index rebuild — these are the honest cases for a high limit. Even so, the limit is per request, and a tool that does everything in one request will hit any limit on a large enough site. Prefer tools that process in batches with a progress indicator, or run the task from the command line where max_execution_time is unlimited by default. An admin that is slow in general, rather than during one task, is a different diagnosis.

When the time is not CPU

On Linux, max_execution_time counts only the time PHP spends executing the script. Time spent waiting — on a database query, a remote API, a DNS lookup, sleep() or a file lock — does not count. So a request that hangs on a slow API usually does not produce this fatal at all: it runs into PHP-FPM's request_terminate_timeout or the web server's timeout and ends as a 502 or 504 with no PHP message. If you see a gateway timeout rather than this error, the fix is the slow dependency, not the PHP limit. When this fatal does appear, PHP genuinely spent the time working, which is why the file and line are worth reading: a loop in plugin code is fixed by optimisation, not by a bigger number. Windows servers are the exception, counting wall-clock time.

Verify

Reproduce the action that timed out after raising the limit. If it now completes, note how long it took: a task that needs 200 seconds will fail again when the site grows. If the error moved to a different file and line, the first bottleneck is gone and the next one is showing; the same reading applies. Then lower the limit back toward a sane value — 60 or 120 — once the slow task is batched or moved to the command line, so a runaway request cannot hold a PHP worker for five minutes.

Frequently asked

Many hosts list set_time_limit in disable_functions, and even where it works PHP-FPM's request_terminate_timeout kills the worker regardless of what the script asked for. The reliable place is php.ini, .user.ini, or the hosting panel, with the web server's own timeout raised to match.
Possibly. Since WordPress 5.2 a fatal error, including a timeout, is replaced for visitors by a generic error page, which current versions word this way. The real message goes to the PHP error log, and to the admin email when WordPress can pin the error on a plugin or theme. Open the log or enable WP_DEBUG_LOG to see whether it says Maximum execution time exceeded or something else.
Updates download, unzip and copy files inside one request. Core raises the limit to 300 seconds for its own updater, but a host that caps execution time below that, or a slow disk, still times out. Updating over WP-CLI runs without the web limit.

Related guides