Widgets, Menus or Customizer Settings Disappeared After Switching WordPress Themes
Your sidebar is empty, the header has no menu and the logo is gone. Nothing was deleted. Learn where each setting lives per theme and how to bring it back.
You click Save Menu and the last items vanish or never stick. Find out whether PHP is cutting off the form, a script is failing, or the save is timing out, and fix it.
You add a batch of items under Appearance, Menus, click Save Menu, see “Main Menu has been updated”, and then the bottom of the menu is gone. Sometimes new items never stick, sometimes nested items pop back to the top level, and sometimes a whole branch of the menu disappears from the front end. It almost always happens on menus with roughly 80 or more items, and it gets worse as the menu grows.
Under the hood, the menu screen is one big HTML form. Every menu item sends more than a dozen separate fields to the server. PHP has a safety limit, max_input_vars, that stops reading form fields after a set number (1000 by default) and silently throws the rest away. WordPress then saves what it received and treats the missing items as deleted. This guide shows you how to prove that is what happened, fix the limit properly, and deal with the other causes that look the same: broken scripts on the Menus screen, timeouts on very large saves, and block theme menus that live somewhere else entirely.
When you save a classic menu, your browser posts the update-nav-menu form to wp-admin/nav-menus.php. For each item, the form carries fields named like menu-item-db-id[ITEM_ID], menu-item-title[ITEM_ID], menu-item-url[ITEM_ID], menu-item-parent-id[ITEM_ID], menu-item-position[ITEM_ID] and so on. Current WordPress core sends about 15 of these per item: database ID, object ID, object, parent ID, position, type, title, URL, description, title attribute, target, CSS classes, XFN and a couple of ordering fields. Plugins that add custom fields to menu items (icons, mega menu settings, visibility rules) add more.
That is why trouble starts at around 70 to 100 items: 1000 divided by 13 to 15 fields is roughly 66 to 77 items, and plugin fields lower it further.
| Setting | Default | What it limits | Where it can be set |
|---|---|---|---|
max_input_vars |
1000 | Number of GET, POST and cookie variables PHP will parse per request | php.ini, .user.ini, pool config, .htaccess under mod_php or LiteSpeed. Not with ini_set() |
post_max_size |
8M in stock PHP | Total size of the POST body | Same places |
max_execution_time |
30 seconds in stock PHP | How long the save may run | Same places, plus the web server and proxy timeouts |
suhosin.post.max_vars, suhosin.request.max_vars |
Set by the Suhosin config | A second, separate variable cap on old servers running the Suhosin extension | suhosin.ini or php.ini |
There is an important detail most guides miss. Since WordPress 4.5.3, the Menus screen has its own workaround: when you click Save, a script copies the whole form into a single hidden field called nav-menu-data as JSON. That field is the first input in the form, so PHP always keeps it, and WordPress rebuilds the full list from it before saving. On a healthy modern site, a 300 item menu can save with the default limit of 1000.
So when you still lose items today, one of these is true: the JavaScript on the Menus screen did not run (a script error, a plugin replacing the screen, or a browser extension), you are on a very old WordPress version, a plugin saves menu data through its own form or AJAX request without the workaround, Suhosin or a firewall is truncating the large JSON value, or the save is running out of time halfway through. The steps below test each one.
You need a WordPress administrator login. For the server checks you need either your hosting control panel (for PHP settings and the error log) or SSH access. Before changing anything, take a backup of the database, or at least export the menu as described in Step 1, because a failed save on a big menu deletes items for real.
How to run commands in this guide:
wp-config.php). Your host may call the WP-CLI tool wp or wp-cli; use whichever exists..user.ini are files you edit in the site folder with SFTP, the file manager in your panel, or a terminal editor.First find out how big the menu really is, and take a copy you can rebuild from.
wp menu list --fields=term_id,name,slug,locations,count
wp menu item list MENU_SLUG --format=count
wp menu item list MENU_SLUG --fields=db_id,menu_item_parent,position,type,object,object_id,title,link --format=csv > menu-backup-YYYY-MM-DD.csv
Run these on the server over SSH, in the site folder.
MENU_SLUG is the menu’s slug from the first command’s slug column (for example main-menu). You can also use the menu’s name in quotes or its term_id.YYYY-MM-DD is today’s date, so you know which copy is which.--fields picks the columns to show. count in the first command is the number of items in each menu, and locations shows which theme location uses it.--format=count prints only the number of items. --format=csv with > writes the list to a file.Good output is a count that matches what you expect. If it is already lower, the damage has been saved and the CSV records what is left, not what was lost, so look for a recent database backup before rebuilding by hand. If the menu has fewer than about 60 items, max_input_vars is unlikely to be your cause and you should jump to Step 4.
When PHP hits the limit, it writes a warning like this:
PHP Warning: Unknown: Input variables exceeded 1000. To increase the limit change max_input_vars in php.ini. in Unknown on line 0
This is an example log line, not a command. The number after “exceeded” is the limit in force for that request. “Unknown on line 0” means PHP raised it while reading the request, before any WordPress code ran.
Because it happens before WordPress loads, this warning usually lands in the server’s PHP error log, not in wp-content/debug.log. Look in your panel’s error log page, or search the log over SSH:
grep -i "input variables exceeded" PHP_ERROR_LOG_PATH | tail -n 20
PHP_ERROR_LOG_PATH is the path of the PHP error log. Find it in your hosting panel, or run wp eval 'echo ini_get("error_log");', which shows the CLI value (often the same as the web value, but not always). Common places are a logs folder in your home directory or an error_log file in the site folder.-i makes the search ignore upper and lower case. tail -n 20 shows the last 20 matches.A match with a timestamp that lines up with your failed save confirms the cause. No match does not fully rule it out, because some hosts do not log warnings. Check the next two items as well.
Go to Tools, Site Health, Info, open the Server section, and read PHP max input variables. This is the value PHP uses for real page loads, which is what matters. Do not trust php -i on the command line: the CLI often reads a different php.ini from the one your website uses.
Open the Menus screen, open developer tools, and select the Network tab. Click Save Menu. Click the nav-menus.php POST request and open the Payload (Chrome) or Request (Firefox) tab. You should see a nav-menu-data field with a long JSON string. If it is empty or missing, the save script did not run, and PHP fell back to the plain form, which is where the limit bites. Switch to the Console tab and look for red errors on page load; Step 4 covers those.
max_input_vars is a per-directory setting. That means you can set it in php.ini, in a .user.ini file, in the PHP-FPM pool, or in .htaccess on some servers, but not with ini_set() in wp-config.php or a plugin. By the time PHP runs that code, the form has already been read.
Most panels have a PHP settings or PHP options page where you can set max_input_vars per site. Set it to 5000 for a big menu, save, and recheck Site Health. This is the cleanest route because the panel writes the setting where your PHP version actually reads it.
max_input_vars = 5000
Put this line in a file named .user.ini in the site folder (next to wp-config.php). If the file already exists, add the line rather than replacing the file.
5000 is a sensible value for menus with up to a few hundred items. Go to 10000 only if you have a reason; very high values make the server do more work on hostile requests..user.ini only every user_ini.cache_ttl seconds (300 by default), so wait five minutes or restart PHP from your panel.Good result: Site Health shows the new number. Bad result: it still shows 1000, which means your server does not read .user.ini (for example, Apache running PHP as a module). Use Option A, C or D instead.
php_value max_input_vars 5000
Add this to .htaccess in the site folder, outside the # BEGIN WordPress block. It only works where PHP runs as an Apache module or under LiteSpeed. On a PHP-FPM server it causes a 500 Internal Server Error on every page, so check the site straight after saving and remove the line if that happens.
php_admin_value[max_input_vars] = 5000
This goes in the site’s PHP-FPM pool file (the path depends on your distribution and PHP version, for example a file under /etc/php/PHP_VERSION/fpm/pool.d/). PHP_VERSION is the version number folder, such as the one your site runs. php_admin_value sets the value so site code cannot override it. Reload PHP-FPM afterward.
Suhosin is a hardening extension that was common on PHP 5 era servers and still turns up on old builds. It has its own variable caps that apply on top of max_input_vars, plus a cap on the length of a single value, which can cut off the large nav-menu-data JSON string. If Site Health or phpinfo() shows a Suhosin section, raise its limits too:
suhosin.post.max_vars = 5000
suhosin.request.max_vars = 5000
These lines go in php.ini or the Suhosin ini file on the server. post.max_vars caps POST fields and request.max_vars caps all request variables combined; both must be raised. Suhosin also logs its own messages (they mention “suhosin” and “dropped variable”), so search the logs for that word. A server still running Suhosin is almost certainly on an unsupported PHP version, so upgrading PHP is the real fix.
After the limit is raised and Site Health confirms it, rebuild the lost items using your CSV or backup as the reference. Then add one test item at the very bottom, save, reload the screen, and check it is still there.
If nav-menu-data was empty in Step 2, or the limit is already high and items still vanish, a JavaScript error is the likely cause. It also produces other clues: you cannot drag items, the item panels do not open, or “Add to Menu” does nothing.
/wp-content/plugins/PLUGIN_SLUG/).A plugin named in the console error is your lead. The Health Check and Troubleshooting plugin lets you disable plugins for your own session only, so visitors are not affected. If a speed plugin also optimizes admin scripts, turn that off.
Saving a menu updates every item, and each item is a post with several metadata rows. With hundreds of items, a slow database or plugins hooked into each save, the request can outrun max_execution_time or a proxy timeout. Items processed before the cut-off are saved; the rest end up out of order, under the wrong parent, or missing.
Signs: the save hangs, then ends in a 502, 504 or blank page; or the PHP error log shows “Maximum execution time of 30 seconds exceeded” naming a file in wp-includes/nav-menu.php or a plugin. Raise max_execution_time to 120 for the admin (the same places as in Step 3), retry, and seriously consider making the menu smaller (see Step 7). FG-13 covers gateway timeouts in depth.
A firewall or ModSecurity rule may refuse a large request body. That shows as a 403, 406 or 413 on the nav-menus.php POST in the Network tab; FG-49 covers security rule blocks.
If your theme is a block theme (Appearance shows “Editor” instead of “Customize” and “Menus”), the header menu is probably a Navigation block, not a classic menu. Navigation block menus are stored as posts of the wp_navigation post type, and the editor saves them through the REST API as one JSON document. max_input_vars does not apply to them at all.
wp post list --post_type=wp_navigation --fields=ID,post_title,post_status,post_modified
wp post get NAVIGATION_ID --field=post_content | head -c 2000
Run these over SSH in the site folder.
NAVIGATION_ID is an ID from that list.--field=post_content prints the block markup of the menu, and head -c 2000 shows the first 2000 characters so the terminal is not flooded.Common surprises: the header uses a different Navigation menu from the one you edited, the block still renders a fallback Page List, or you are editing a classic menu that was imported into a Navigation menu long ago. In the Site Editor, open the header template part, select the Navigation block, and use its menu options to see which menu it displays, switch menus, or use Import classic menus. If saving fails with a message about a JSON response, the REST API is the problem, which FG-21 covers.
A header menu with hundreds of links is hard to save and hard for visitors to use. Once saving works, consider:
If you want hard numbers instead of estimates, count the fields the form actually posts. Open the Menus screen, open the browser console, and paste this:
document.querySelectorAll('#update-nav-menu [name]').length
This runs in the browser console on the Menus screen. It counts every named field in the save form, which is the number of variables PHP must parse if the JSON workaround is not used. There are no placeholders.
Compare the result with your limit. A result of 1400 with a limit of 1000 means about 400 fields, the last 25 to 30 items, will be dropped by a plain form post. Then count fields per item to see whether a plugin is inflating them:
document.querySelectorAll('#update-nav-menu [name$="[MENU_ITEM_ID]"]').length
This also runs in the browser console. MENU_ITEM_ID is the ID of one item; find it by hovering over an item’s panel or from the db_id column in Step 1. Core on its own gives about 15. A number like 25 or 30 means a plugin adds its own fields, and you can use the result to set a limit with headroom: items multiplied by fields per item, plus 100, rounded up.
Cookies and query string variables count toward the same limit, and other heavy admin forms (theme option panels, product variation screens) hit it the same way, so fix the limit at the server level rather than working around it.
Open a ticket if you cannot change PHP settings yourself, if Site Health does not show the new value after you changed it, or if saves fail with 403, 413, 502 or 504 errors. Include:
nav-menus.php POST from the Network tab.Not sure where your problem fits? Try our diagnostic tools or the Emergency Room.
Did this fix it?
Still stuck? Start again from what you can see in the Emergency Room.
Still stuck?
Print a free one-page checklist to keep, or open the Emergency Room if the symptom does not match.
Get the free checklistsKeep reading
Search the field manual
Or jump straight to a symptom
Press / to search from any page, Esc to close