Yesterday I spent a bit of time becoming better familiar with how WordPress menus work. Particularily how they are stored in the database and how PHP’s max_input_vars setting can come into play.
While helping a friend try to troubleshoot and figure out some performance issues on her WordPress site I also went in to add an item to the main menu of the site. That’s when things took a turn for the worse. The menu is fairly large and uses a theme which includes a mega menu. So already there were over 100 menu items added to the main menu, but only main items and then many that fall in fancy drop downs. That’s why when I added the item, pressed save, and only about 60 menu items were left when the page refreshed I was a little paniced.
As it turns out it also neglected to save the theme location for the menu which caused the theme to display the default menu which is just a list of all pages all as main headers. As you can imagine over 125 menu items filling the screen didn’t look the best on a live site. I tried setting up the menu again and adding in the missing menu items, but when I saved the same thing happened. It’s like it made it to a certain part in the menu and just stopped saving. After some research that’s exactly what happened.
Luckily there as been a lot written about with tips on how to fix it. But before I got there I was just focused on how to get the menu working again and have the site look right. I found that in working with the menu from the Customizer instead of in WP Admin I was able to get all the menus to save. The only problem was the mega menu that was built into the theme added a setting where you enabled the mega menu drop down but it was only in WP Admin, the setting wasn’t there in the Customizer. This led me looking through the database to see how the menus were stored to see if I could enable the setting there.
The following goes over how menus are stored by default in a fresh install of WordPress 4.3.1, default theme Twenty Fifteen with a simple menu I created. Things were different on the site I was working on because of some extra settings which were added by the theme, but the main concepts stand.
Menus have settings saved in a number of tables in the database. In this example I used the default database table prefix of wp_ so that’s how all tables are named. We’ll start in wp_terms which holds the name of the menu. You can see the name, the slug and the term_id which will come into play.
Here are most of the main settings you can choose for a menu item.
Each menu item is saved in the wp_posts table with the post type set as nav_menu_item. There are more fields in this table by default, but I’m only showing the ones we’re really interested in for menu items.
A number of the settings are stored here for a menu and you can see how the settings in WP Admin map over to the fields in the database table. Depending on the type of menu item the post_name and post_title will hold different settings. The rest of the settings are stored in the wp_postmeta table. Here is an image for just one menu item. This combined with the wp_posts table tells us that the menu item with post_id 8 which is the second item in the menu is a post_type menu item and it points to a page with the post_id of 4 and it has no parent item so it is a main heading in the menu.
Here is a look at more settings for other menu items as well.
You can see the rest of the settings here and differnt types like custom links which have a url, and the sub menu items which have a parent id showing which item the fall under in the menu. If you look back to the image of the menu, and the settings image you can see them all shown here.
It was in the wp_postmeta table where there was an extra setting for each menu item that could either be set to active or blank. That specified if a main heading item in the menu should format itself as a mega menu. Finding this I was able to go in and set it manually by editing the database field needed so that the appropriate ones had the mega menu setting active.
The one other table that menus live in is the wp_term_relationships. It takes the term_id from the wp_terms field and ties each of the menu items id’s to show that they are part of that menu.
Now that I had the menu working and looking the way it should I could spend the time to find out what was causing all the menu items to save. It really comes down to php settings. There is a setting called max_input_vars in PHP. On my hosting provider the default value of this is set to 1000. What this means is that when you submit a form to PHP to handle it will only process a maximum of 1000 form fields. Once it gets there it just ignores the remaining. If your hosting provider has the PHP security feature suhosin installed there would be an additional two settings called suhosin.post.max_vars and suhosin.request.max_vars as well which essentially do the same thing.
In the image above you can see that visibly each menu item has seven form fields associated with it. There are also some hidden form fields. If you look at the source code generated for a single menu item you can see there by default 13 form fields.
That means to reach the maximum of 1000 input variables it would only take 77 menu items. That doesn’t even include the other form fields that make up the menu page. It took even less than that in the site I was working on as the theme added extra fields to each menu item as well. This also explains why I was able to save the menu using the Customizer because it was only showing a few fields for each menu item, so it was still below the max_input_vars setting.
As my test menu in this example only has a few items to test I set the max_input_vars to be 50 and then saved the menu. This was the result. It only saved 4 menu items, and you can see it no longer has the theme location setting saved below either.
Now knowing what the problem is I knew how to fix it and what I did to cause it to stop working on my friends site. As part of the performance enhancements I was trying to do I changed her server to use PHP version 5.6 instead of version 5.5. The max_input _vars had been set to 5000 for version 5.5 but was a lower default for version 5.6 and I didn’t notice. So making this change fixed things so I could once again save the menu.
To make the change it is just a matter of editing the php.ini file that holds all the settings. For me the setting wasn’t in the file so I just added it with the value of 5000 that I wanted. If the line was there you can just edit the number to be higher.
Depending on your host there might be other ways to change this through a control panel, or you may need to contact them directly. Running the phpinfo() function you can see what the value is currently set to as well as the location of the php.ini file is so you can edit it if you have access.
As I mentioned before there is quite a bit written about this where people have run into problems saving menus, but in my reading I didn’t see an explanation of what that max_input_vars setting really means and why what seems like a lower number of menu items might trip that limit even if it seems high. Since I dug into the database I figured I would include that in here as well. Hopefully this is helpful to someone if they run across it in the future.