WordPress gets stuck in maintenance mode when an update process is interrupted before WordPress can delete the .maintenance file it creates at the start of every core, plugin, or theme update. The fix takes under two minutes: delete that file via FTP, SFTP, or your host's file manager, and your site comes back immediately.
What Actually Happens During a WordPress Update
WordPress's update routine is deliberate: it gates the entire process behind a lockout file so visitors see a controlled message instead of a half-updated site throwing PHP fatal errors. Here's the exact sequence:
- WordPress writes
/.maintenanceto the site root containing a PHP timestamp (<?php $upgrading = 1735000000; ?>) - WordPress downloads the update package to a temporary directory
- The package is extracted and files are moved into place
- The database is updated if required
- WordPress deletes
/.maintenanceand redirects back to normal operation
The maintenance.php drop-in (or the built-in fallback in wp-includes/functions.php) checks for this file on every request. If the file exists and the timestamp is less than 10 minutes old, it serves the maintenance screen. If the file is older than 10 minutes, WordPress actually ignores it automatically—which is why some sites recover on their own and others don't.
The 10-minute TTL is intentional. The problem is that the file never gets deleted when the update process crashes, so the timestamp stays fresh on the next request, starting the clock over. That is why waiting it out doesn't work.
How to Fix It: Delete the .maintenance File
The fix is the same regardless of whether the stuck update was a core release, a plugin, or a theme.
Method 1: FTP or SFTP (most reliable)
Connect to your server with an FTP client like FileZilla or Transmit. Navigate to your WordPress root—the same directory that contains wp-config.php, wp-admin/, and wp-content/. You are looking for a file named .maintenance. Because the filename starts with a dot, it is hidden by default on most systems.
In FileZilla: Server → Force showing hidden files. In Transmit: View → Show Hidden Files. Once visible, select .maintenance and delete it. Your site is live the moment the file is gone.
Method 2: cPanel or Plesk File Manager
Log in to your hosting control panel and open File Manager. Navigate to public_html (or your WordPress root if it differs). Enable hidden files—cPanel has a "Settings" button in the top-right where you toggle "Show Hidden Files (dotfiles)." Delete .maintenance. Done.
Method 3: WP-CLI (fastest for SSH access)
wp eval 'if (file_exists(ABSPATH . ".maintenance")) { unlink(ABSPATH . ".maintenance"); echo "Deleted."; } else { echo "File not found."; }'
Or more directly:
rm /var/www/html/.maintenance
Replace /var/www/html/ with your actual WordPress root path.
Method 4: PHP script (use with caution)
If you have no FTP or SSH access and your host's file manager is inaccessible, you can upload a temporary PHP file to your server root via any available file upload mechanism:
<?php
$file = dirname(__FILE__) . '/.maintenance';
if (file_exists($file)) {
unlink($file);
echo 'Maintenance file removed.';
} else {
echo 'Maintenance file not found.';
}
Upload as fix-maintenance.php, visit https://yourdomain.com/fix-maintenance.php, then immediately delete the file. Leaving executable PHP scripts exposed in your web root is a security risk.
Why Did the Update Fail in the First Place?
Deleting the file gets your site back online, but it does not fix what caused the crash. Check your PHP error log (wp-content/debug.log if WP_DEBUG_LOG is enabled, or the server-level error log) before you try the update again.
PHP execution timeout
The most common cause. Shared and entry-level hosting environments often set max_execution_time to 30–60 seconds. Downloading a large plugin archive, extracting it, and moving files can easily exceed that. The update process dies mid-operation, .maintenance stays.
Fix: Increase max_execution_time in php.ini or .htaccess:
php_value max_execution_time 300
Or via wp-config.php:
@ini_set('max_execution_time', 300);
Memory exhaustion
If WordPress runs out of memory mid-update, PHP throws a fatal error and the process terminates. Check your log for Allowed memory size of X bytes exhausted.
Fix: In wp-config.php:
define('WP_MEMORY_LIMIT', '256M');
define('WP_MAX_MEMORY_LIMIT', '512M');
WP_MAX_MEMORY_LIMIT specifically covers admin and update operations.
Failed package download
WordPress fetches update packages from downloads.wordpress.org. If that request times out or the server blocks outbound HTTP, the download fails. Check wp-admin/update-core.php manually—if it reports a download error rather than hanging, this is your issue.
Fix: Verify outbound HTTP connectivity from your server. Some hardened hosting environments block outbound requests that aren't on an allowlist. Contact your host if your server cannot reach downloads.wordpress.org.
Concurrent update attempts
Multiple admins triggering updates simultaneously, or an automated updater (ManageWP, MainWP, a cron job) running at the same time as a manual update, can create race conditions. The second update process reads the .maintenance file left by the first and may exit immediately—leaving both processes in an undefined state.
Fix: Use a single update path. If you use an automated update plugin, disable manual updates or vice versa. Configure automated updates to run during low-traffic hours.
File permission errors
If WordPress cannot write to a directory during extraction, the update aborts. Check for directories set to 444 or files owned by a different user than your PHP process.
find /var/www/html/wp-content/plugins -not -writable -type d
Correct permissions: directories at 755, files at 644, owned by your web server user.
Checking What Happened to the Update Itself
After you restore access, you need to verify the update state. A partially applied update is worse than no update at all.
Check the plugin/theme version
Go to Plugins → Installed Plugins or Appearance → Themes and compare the version shown against the current version in the WordPress plugin directory. If they match, the update completed before the process crashed. If they don't, the update is in an unknown state.
For a partially updated plugin, the safest recovery is to deactivate it, manually delete its directory via FTP, and reinstall from the plugin directory. Do not just re-run the update on a partially extracted plugin—it will overwrite some but not all files depending on where the extraction failed.
For a partially updated WordPress core, use WP-CLI:
wp core verify-checksums
This compares your core files against WordPress.org's checksums for your version. Any mismatched or missing files are reported. Follow up with:
wp core update --force
to re-download and apply the full package.
Review update logs
WordPress 6.6+ logs update activity to the database. You can retrieve recent events:
wp option get auto_updater.lock
wp option get update_core
The auto_updater.lock transient in particular will tell you if an automated update process died while holding a lock—which can prevent future updates from running until the transient expires (typically 15 minutes).
Comparison: Recovery Options by Access Level
| Access Available | Best Method | Time to Fix | Notes |
|---|---|---|---|
| SSH / WP-CLI | WP-CLI unlink or rm | ~30 seconds | Fastest; also lets you run wp core verify-checksums immediately |
| SFTP / FTP | FileZilla / Transmit | 1–2 minutes | Enable hidden files display first |
| cPanel File Manager | Built-in file manager | 1–3 minutes | Toggle dotfiles in Settings |
| Plesk File Manager | Built-in file manager | 1–3 minutes | Identical to cPanel flow |
| Hosting dashboard only | PHP script upload | 3–5 minutes | Delete the PHP file immediately after |
| No file access at all | Contact host support | Varies | Should be last resort; any host worth using gives you SFTP |
How to Prevent WordPress Maintenance Mode From Getting Stuck
Prevention is straightforward once you understand that the problem is almost always a resource constraint or a process interruption.
Increase PHP limits before updating. Set max_execution_time to at least 300 seconds and memory_limit to at least 256M. On managed hosts this is typically handled for you. On shared hosting, do it via .htaccess or php.ini if your plan allows.
Update one item at a time during initial diagnosis. If you have 30 plugins queued for updates and you click "Update All," a timeout on plugin 12 leaves you unsure which plugin failed and in what state. Update core first, then plugins individually until you understand your server's performance envelope. This is especially relevant if you haven't done a thorough WordPress plugin audit to identify which plugins are heavy or poorly maintained.
Use a staging environment. Test updates in staging before production. A failed update in staging tells you about resource constraints without any user impact. Staging also catches the increasingly common problem of a new plugin version that is technically installed correctly but breaks your site's frontend—which the .maintenance cleanup cannot help with.
Keep an eye on the plugin directory. Plugins that have been removed from the WordPress directory often have broken update endpoints. Attempting to update a delisted plugin can hang the update process indefinitely. Our post on what happens when a plugin is removed from the WordPress directory covers how to identify and handle these cases.
Automate with guardrails, not blindly. Automated updates are good practice—according to Sucuri's 2024 Hacked Website Report, outdated plugins and themes account for 61% of known-vulnerability WordPress compromises. But automated updates without staging, version pinning, or rollback capability are how a smooth automated system turns into a 3 AM emergency.
How Managed Hosting Handles This Automatically
On properly configured managed WordPress hosting, the conditions that cause a stuck .maintenance file are largely engineered away before you encounter them.
PHP execution time limits on managed platforms are set at 300+ seconds specifically because update operations need headroom. Memory limits are pre-configured at 256M or higher. Update processes run in isolated contexts so a timeout on one plugin does not corrupt the overall operation. Many managed hosts also run updates through queued background jobs with retry logic, not inline PHP execution tied to a browser request.
TopSyde's managed WordPress hosting—available from $89/mo per site—manages core and plugin update scheduling with pre-update snapshots so that a bad update is a rollback, not a recovery operation. TopSyde Sentinel monitors the update process itself and flags when an update terminates abnormally, alerting the team within under 2 hours during business hours rather than waiting for a client to notice their site is down.
If you're evaluating whether managed hosting makes sense for your workload, the honest ROI breakdown in our managed hosting cost analysis walks through exactly when the automation premium pays for itself versus when DIY maintenance is fine.
For agencies managing multiple client sites, the maintenance mode problem is multiplied by your client count. The managed WordPress hosting workflow for agencies covers how update orchestration across a fleet is handled without requiring per-site babysitting.
Frequently Asked Questions
Why does WordPress show maintenance mode for more than 10 minutes if it's supposed to auto-clear?
The 10-minute timeout only triggers when WordPress can evaluate the timestamp in the .maintenance file. If the file is malformed (zero bytes or missing the PHP timestamp) due to a crash mid-write, WordPress may not be able to parse it correctly, meaning the auto-expiry logic never fires. Always inspect the file contents if the 10-minute rule seems to not be working.
Can I prevent the maintenance screen from showing at all during updates?
Not without removing the maintenance gate entirely, which is not advisable. You can customize the message shown to visitors by adding a maintenance.php file to your wp-content/ directory—WordPress will use that as the template instead of its default screen. This lets you show a branded message with your contact info, but the gate itself should stay in place; it exists to protect users from seeing a partially updated site.
Is it safe to run updates again immediately after deleting the .maintenance file?
Only if you have verified the previous update did not partially apply. Run wp core verify-checksums for core updates, and manually confirm the installed version for any plugin that was updating when the process failed. If a plugin is in a partially updated state, reinstall it cleanly before retrying. Attempting to update a partially extracted plugin file set can result in conflicts between old and new file versions that produce PHP fatal errors unrelated to the .maintenance file.
Does deleting the .maintenance file fix a broken update, or just the maintenance screen?
It only fixes the maintenance screen. The underlying update may be complete, partial, or untouched—deleting .maintenance has no effect on that. Think of the file as a door lock: removing it lets visitors in, but it says nothing about whether the room behind the door is tidy.
Will this problem happen again on the same plugin?
Possibly, if the root cause was a resource limit that you haven't addressed. A plugin that requires more memory or execution time than your environment provides will fail every time on that same step. Identify the constraint from your error log, fix it, then re-run the update. If you continue to see failures on the same plugin without a clear resource error, check whether that plugin has been flagged for removal from the WordPress directory or has open, unresolved GitHub issues related to the update process.
Topics

DevOps & Security Lead
12+ years DevOps, Linux & cloud infrastructure certified
Marcus leads infrastructure and security at TopSyde, managing the server fleet and AI monitoring systems that keep client sites fast and protected. Former sysadmin turned WordPress hosting specialist.



