Fixing the Maximum Execution Time Error
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.
PHP stopped the request at the time limit and reported where it was, not what was slow. "Fatal error: Maximum execution time of 30 seconds exceeded in /path/to/file.php on line N" 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.
Now read the error
The file and line are where the limit hit. Trace upward:
wp-db.phporclass-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,class-requests.php— an outbound HTTP call hung. A plugin calling an API that did not answer; thehttp_request_timeoutfilter or the plugin's own timeout is too generous, and the remote service is the slow part.- 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.phporwp-includes/cron.php— a scheduled task ran inside a visitor's request and took too long. Moving cron to a real scheduler takes it out of page loads 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
A request can take 30 seconds while doing nothing: waiting on a locked database table, a remote API, a DNS lookup that times out, or a file lock. Raising the limit lets it wait longer. The location in the error separates waiting from working — an HTTP class or a sleep-like lock wait versus a loop in plugin code — and only the second is fixed by optimisation.
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.
- No. The line is wherever PHP happened to be when the limit hit, which is usually inside a database call or a loop in core. What is slow is the caller: a plugin running a query thousands of times, an import, or an update extracting a large zip. The stack above that line names it.
- 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.