Fix PHP Fatal Error: Maximum Execution Time of 30 Seconds Exceeded

beginner๐Ÿ˜ PHP2026-03-25| PHP 7.x/8.x on Linux/Windows, Apache, Nginx + PHP-FPM, Laravel, WordPress, shared hosting (cPanel), VPS

Error Message

Fatal error: Maximum execution time of 30 seconds exceeded
#php#timeout#execution-time#max-execution

The Error

Your script hits this wall and dies:

Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/html/process.php on line 142

PHP has a built-in execution time limit (max_execution_time) โ€” default 30 seconds. When a script runs longer, PHP kills it. No cleanup, no graceful exit. Just dead.

You'll typically see this during bulk CSV imports, calls to sluggish third-party APIs, PDF/report generation, image resizing pipelines, or any loop iterating over thousands of database rows.

Root Cause

The script is doing too much inside a single HTTP request. Either the logic itself is slow, or it's blocked waiting on something external โ€” a 10-second DB query, an unresponsive API, a 200 MB file loaded entirely into memory.

One important distinction: PHP CLI (php script.php) defaults to max_execution_time = 0 โ€” unlimited. This error almost exclusively happens in the web context (Apache or Nginx serving an HTTP request).

Fix 1: Increase the Limit in php.ini

The most permanent fix. It applies globally to your server. First, find your active php.ini:

php --ini | grep 'Loaded Configuration'
# or
php -r "echo php_ini_loaded_file();"

Edit it and raise the limit:

; php.ini
max_execution_time = 120

Then restart your web server:

# Apache
sudo systemctl restart apache2

# Nginx + PHP-FPM
sudo systemctl restart php8.2-fpm
sudo systemctl restart nginx

Avoid setting it to 0 on web-facing scripts. Unlimited execution time means a single slow request can hold a PHP-FPM worker hostage indefinitely โ€” that's a DoS waiting to happen.

Fix 2: Set It in the Script (Quick Override)

Can't touch php.ini? Shared hosting, someone else's server, emergency hotfix at 2 AM โ€” set_time_limit() works without any config access:

<?php
// Put this at the very top of your script
set_time_limit(120); // 120 seconds

// Or reset the clock on each loop iteration
foreach ($large_dataset as $item) {
    set_time_limit(30); // Reset to 30s per item
    process($item);
}

set_time_limit(0) disables the limit entirely for that script run. Fine for a CLI batch job processing 50,000 records. Not fine for a random public endpoint.

Note: set_time_limit() does nothing if PHP runs in safe mode โ€” rare in modern setups, but worth knowing if you're on legacy infrastructure.

Fix 3: .htaccess Override (Apache Only)

On shared Apache hosting with AllowOverride enabled, you can set this per-directory without touching anything global:

# .htaccess
php_value max_execution_time 120

No restart needed. Apache reads .htaccess on every request.

Fix 4: php.ini in Project Root (cPanel / Some Hosts)

Many shared hosts let you drop a php.ini or .user.ini directly in your project folder:

# public_html/php.ini  OR  public_html/.user.ini
max_execution_time = 120

Check your host's docs to see which file they honor. .user.ini is the PHP-FPM equivalent and is more common on hosts running modern stacks.

Fix 5: PHP-FPM Pool Config

Running PHP-FPM directly? Override the limit per pool instead of touching the global php.ini:

# /etc/php/8.2/fpm/pool.d/www.conf
php_admin_value[max_execution_time] = 120

Restart the FPM service to apply it:

sudo systemctl restart php8.2-fpm

Fix 6: For Laravel / Artisan Commands

For queued jobs or long Artisan commands, set the limit at the top of the handle() method:

<?php
public function handle()
{
    set_time_limit(0); // CLI ignores this anyway, but explicit beats implicit
    ini_set('max_execution_time', 0);

    // your long-running logic
}

Also check your queue worker timeout โ€” PHP's limit and Laravel's worker timeout are two separate clocks:

# Run worker with explicit timeout (seconds)
php artisan queue:work --timeout=120

Fix 7: Diagnose and Fix the Slow Code

Raising the limit buys time. It doesn't fix anything. If your script genuinely needs 5 minutes, that's a red flag worth investigating.

Start by measuring where the time actually goes:

<?php
$start = microtime(true);

// ... your code block ...

$elapsed = microtime(true) - $start;
error_log("Block A took: {$elapsed}s");

The usual suspects:

  • Slow DB query โ€” run EXPLAIN on it. A missing index on a 500,000-row table can turn a 10ms query into a 45-second table scan.
  • External API calls โ€” always set an explicit timeout: curl_setopt($ch, CURLOPT_TIMEOUT, 10). Without it, PHP will wait indefinitely for a server that never responds.
  • Large file processing โ€” stream it instead of loading everything into memory at once. Reading a 1 GB file with file_get_contents() will exhaust both memory and time.
  • Nested loops on big arrays โ€” O(nยฒ) logic hits a wall fast. Use chunking or rethink the algorithm.

For bulk operations, the real fix is to get them off the HTTP request entirely:

<?php
// Don't process 10,000 rows synchronously in a web request.
// Dispatch a job and return immediately.
ImportJob::dispatch($file_path);

return response()->json(['status' => 'processing']);

Verify the Fix

Don't trust what you set โ€” confirm what's actually active at runtime:

<?php
echo ini_get('max_execution_time');

Or use phpinfo() (never leave this on production) and search for max_execution_time. You'll see both the master value and the local value. If they differ, something is overriding your setting โ€” check .htaccess, pool config, or .user.ini in order.

Re-run the failing script and confirm it completes without the fatal error.

Quick Reference

  • php.ini โ€” max_execution_time = 120 (global, requires restart)
  • Script top โ€” set_time_limit(120); (per-script, immediate)
  • .htaccess (Apache) โ€” php_value max_execution_time 120 (no restart needed)
  • .user.ini โ€” max_execution_time = 120 (PHP-FPM per-directory)
  • CLI โ€” defaults to 0 (unlimited), this error won't happen there

Prevention

The pattern that causes this error is predictable: synchronous, blocking work inside an HTTP request. Break that pattern early.

Add execution time logging to any process that touches external systems. Set up slow-request monitoring โ€” Apache's %D format variable logs request duration in microseconds; New Relic and Datadog can alert you when a route consistently hits 10+ seconds.

As a rule of thumb: a web request should return in under 3 seconds. If you need 30 seconds or more, the work belongs in a background job โ€” a queue worker, a cron task, or an async process. The HTTP response should just say "I've queued it" and return immediately.

Related Error Notes