Skip to content
Products
AI Website Builder New VPS Hosting Cloud Servers Web Hosting Dedicated Servers Domains
Company
About Documentation Support Center Contact Get Started Call +91 75795 45488
Login
Hosting Panel — cPanel & Billing Console Panel — VPS Management
ALL SYSTEMS OPERATIONAL
Troubleshooting

PHP Execution Timeout: Fix Failed Large File Imports

Getwebup 7 min read

You kick off a big WooCommerce product import, a Duplicator migration, or a phpMyAdmin-adjacent database restore through a plugin, and it just stops. No progress bar update, no error dialog — the page either goes blank or spins forever before dumping a 500. Nine times out of ten, it's not the import that's broken. Something in the stack decided your request had run long enough and killed it.

Symptom: The Import Stops Partway, No Clear Error

This shows up a few different ways depending on what's timing out:

  • A white screen after the progress bar sits at 40% for a while — classic PHP fatal error, but nothing on screen because display_errors is off.
  • The browser tab spins for 30–60 seconds, then shows a 504 Gateway Timeout or 502 Bad Gateway.
  • The import "completes" but only half the rows landed — the process died silently mid-loop and the plugin never got a chance to report failure.
  • Your PHP error log has a line like:
PHP Fatal error:  Maximum execution time of 30 seconds exceeded in /home/user/public_html/wp-includes/class-wp-hook.php on line 324

That 30-second number is the giveaway — it's the default max_execution_time on most PHP configs, and it has nothing to do with how big your file is or how good your server is. It's a hard stopwatch.

Cause: There's Not Just One Timeout — There Are Four

This is the part that trips people up. They raise max_execution_time to 300 seconds, the import still dies at 60 seconds, and they assume the setting didn't take. Usually it did take — it just wasn't the layer that was actually cutting the connection. A single HTTP request on a typical LEMP/LAMP stack passes through several independent timeout clocks, and the shortest one wins:

LayerDirectiveTypical defaultWhere it lives
PHP itselfmax_execution_time30sphp.ini / MultiPHP INI Editor
PHP-FPM poolrequest_terminate_timeout0 (disabled) or inheritedpool .conf, e.g. /etc/php-fpm.d/*.conf
Nginx (reverse proxy)proxy_read_timeout, fastcgi_read_timeout60sserver/location block
ApacheTimeout60shttpd.conf / .htaccess (limited)
LiteSpeedConnection Timeout300sWHM/cPanel LiteSpeed config
CDN/proxy (e.g. Cloudflare free)Idle connection cutoff100sNot configurable on free plans

If you raise PHP's limit to 300 seconds but Nginx is still killing the proxied connection at 60, the browser sees the same 504 as before — and the PHP error log won't even show a "maximum execution time" message, because PHP was never given the chance to hit its own limit. That's the usual reason "I already raised the timeout" doesn't fix it.

Why this hits imports specifically

A normal page load runs in well under a second, so none of these limits ever matter. Imports are different: WooCommerce CSV imports process rows in a loop, Duplicator/All-in-One migrations read and write large archives, and SQL restores through a plugin (rather than CLI) route the entire file through PHP's request lifecycle. All of that work happens inside one HTTP request unless the tool explicitly chunks it — and one HTTP request only gets one shot at the clock before something upstream decides it's hung.

Fix: Raise the Right Layer, Then Stop Depending on the Browser

Step 1 — Find out which layer actually killed it

Check public_html/error_log (or WHM's Apache/PHP-FPM error log) right after a failed run:

  • A Maximum execution time of N seconds exceeded line means PHP's own limit fired — go to Step 2.
  • No PHP error at all, but the browser showed 502/504, means the web server or proxy cut the connection first — go to Step 3.
  • Allowed memory size exhausted is a different problem (memory, not time) — raise memory_limit alongside this, but it's a separate fix.

Step 2 — Raise PHP's execution time limit

In cPanel, go to Software → MultiPHP INI Editor, pick the domain, and set:

max_execution_time = 300
max_input_time = 300
memory_limit = 512M

If you're on a VPS without cPanel, edit the pool's php.ini directly and restart PHP-FPM:

sudo nano /etc/php/8.3/fpm/php.ini
# max_execution_time = 300
sudo systemctl restart php8.3-fpm

Some plugins call set_time_limit() at runtime to override this per-request — that only works if the hosting environment allows it. On restrictive shared hosting it's silently ignored, which is one more reason this can "not work" even after you've edited every config you can find.

Step 3 — Raise the web server and proxy timeouts to match

If you're behind Nginx as a reverse proxy in front of PHP-FPM or Apache (common on VPS setups), bump these in the relevant server or location block and reload:

location ~ \.php$ {
    fastcgi_read_timeout 300;
    ...
}

# if proxying to Apache/another backend
proxy_read_timeout 300;
proxy_send_timeout 300;
sudo nginx -t && sudo systemctl reload nginx

For PHP-FPM's own pool timeout:

# /etc/php/8.3/fpm/pool.d/www.conf
request_terminate_timeout = 300

On LiteSpeed (common on cPanel hosting), the setting lives in WHM → LiteSpeed Web Server → Configuration → General under Connection Timeout, or per-vhost if you need it isolated to one account.

If you're routing through Cloudflare on a free/pro plan, know that idle HTTP connections get cut around 100 seconds regardless of what your origin server allows — there's no setting to raise this on those tiers. An import that legitimately needs more than that has to run outside the browser entirely, which brings us to the actual fix.

Step 4 — For anything over a minute or two, don't run it through the browser at all

Raising timeouts buys you headroom, but it doesn't scale — a 200MB database or a 50,000-row product CSV will eventually outrun whatever number you pick, and cranking every timeout to 3600 seconds just means a stuck request ties up a PHP-FPM worker for an hour. The actual fix for large jobs is to run them outside the HTTP request/response cycle:

  • Database restores: use the MySQL CLI over SSH instead of phpMyAdmin or a plugin — it has no execution-time ceiling:
    mysql -u username_dbuser -p username_dbname < backup.sql
  • WordPress content/media imports: use WP-CLI, which runs as a CLI process, not through PHP-FPM's web pool:
    wp import products.xml --authors=create
    wp plugin install woocommerce-importer --activate
  • WooCommerce CSV imports: WooCommerce's own importer already chunks large files into background batches via Action Scheduler — if it's still timing out, check WooCommerce → Status → Scheduled Actions for stuck or failed jobs rather than re-running the whole file through the browser.
  • No SSH access on your plan: split the file. For SQL, break it into per-table chunks; for CSV, split by row count (1,000–2,000 rows per file is usually safely under any default timeout).

Prevention

  • For anything you know will be large going in — a full site migration, a multi-thousand-row product catalog — plan on SSH/WP-CLI from the start instead of discovering the timeout mid-import.
  • Run big jobs during low-traffic hours; a busy PHP-FPM pool with all workers saturated makes an already-slow import worse and more likely to hit a queue-related timeout on top of the execution one.
  • After any large plugin install/import, check WooCommerce → Status → Scheduled Actions or your import tool's log — a job that silently stopped without an error is often a background task stuck behind a timeout, not a completed one.
  • Keep the four-layer table above handy. When a timeout comes back after you've "already fixed it," the fastest diagnosis is checking the error log for which layer actually fired, not re-raising the same PHP setting again.

Frequently asked questions

I already raised max_execution_time in php.ini, why does the import still time out?

Because PHP's setting is only one of several timeout clocks on the request. If you're behind Nginx as a reverse proxy, LiteSpeed, or a CDN like Cloudflare, one of those can cut the connection before PHP ever reaches its own limit. Check the error log first: if there's no 'maximum execution time' message from PHP, the web server or proxy killed it, not PHP.

What's a safe max_execution_time to set without causing other problems?

300 seconds (5 minutes) is a reasonable ceiling for shared hosting or a small VPS. Going much higher just means a stuck request ties up a PHP-FPM worker for longer, which can starve other requests if it happens repeatedly. For anything that genuinely needs more than a few minutes, run it via SSH/WP-CLI instead of raising the browser-facing timeout further.

I don't have SSH access on my hosting plan. How do I import a large database or CSV?

Split the file. For a SQL dump, export or divide it per table so each chunk imports well under any default timeout. For a CSV, break it into batches of 1,000-2,000 rows. It's more manual, but it avoids depending on timeout settings you may not be able to change on shared hosting.

Does this affect uploads too, or just imports and migrations?

The same four layers apply to any long-running PHP request, including large media uploads, but uploads usually hit upload_max_filesize or post_max_size limits (and a 413 error) before they hit an execution-time limit. If you're seeing a 413 instead of a timeout, that's a different fix - the file size limits, not the clock.

#php #execution-time-limit #wordpress #cpanel #vps #php-fpm

Keep reading

Chat with Support