WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm looking for ways to prevent "private" and "draft" pages from appearing (to non-logged-in admins) in a site's Primary Navigation menu which is based on a custom menu - other than manually removing such pages from the WP custom menu and then dragging them back when they are published, that is.

FWIW, I'm surprised the behavior I'm looking for isn't the default.

Using: WP 3.4.2

share|improve this question
To clarify: what code are you using to output your "custom menu"? – Ben Oct 11 '12 at 18:58
None (if I understand your question). This happens using the current Twenty-Ten theme (v1.4) with no customization - other than having created a Custom Menu in the "Appearance" subpanel and assigning it to Primary Navigation in the "Theme Locations" section. – Jeff Cohan Oct 11 '12 at 19:36
Here is a solution that filters drafts from wp_nav_menu: wordpress.org/support/topic/… – user24720 Dec 12 '12 at 11:04

I used this as a solution for hiding draft pages. You can hide the (draft page) menu items with the li.draft class.

add_filter('nav_menu_css_class' , 'nav_menu_add_post_status_class' , 10 , 2);
function nav_menu_add_post_status_class($classes, $item){
    $post_status = get_post_status($item->object_id);
    $classes[] = $post_status;
    return $classes;

FWIW: I feel the same way ;-)

share|improve this answer

The easiest solution is to add a specific class to the menu items that you want to hide. And then hide them through CSS.

custom nav menu classes
^ click to enlarge ^

The CSS classes are not visible by default, you have to enable it in the Screen Options upper tab.

If your theme does not print the relevant classes in the <body> tag, this will do:

add_filter( 'body_class', 'wpse_67929_body_class' );

function wpse_67929_body_class( $classes ) 
    if( !is_user_logged_in() )
        $classes[] = 'not-logged-menu';

    return $classes;

Finally, in your style.css, add this rule:

body.not-logged-menu .menu-logged-in {display:none}

Another option, use the following filter:

add_filter('wp_nav_menu_items', 'wpse_67929_nav_menu_items', 10, 2 );

function wpse_67929_nav_menu_items( $nav_menu, $args )
    // Manipulate the output
    // $nav_menu contains a single string with the menu Html structure
    // more or less like this: 
    // <li id class><a href>Menu Item</a></li> <ul><li><a>Submenu Item</li></ul> <etc>
    return $nav_menu;

"Manipulate the output" seems simple but reveals to be tricky:

share|improve this answer

I had a similar problem and here is the best solution I could come up with. The reason (I think) that private or non-published items show up in menus is that the menu items are themselves posts and have their own post_status. That means that in a situation where a page is marked private, the menu item for that page can still be set to publish, and is displayed. I wrote a filter callback for the wp_nav_menu_objects hook which looks at the post_status for the object that the menu item stands for, and removes it from the menu if that object is private. In my case, private pages was the concern, but one can easily adapt this for drafts. I also allow the menu items to be displayed if the user is logged in, whatever his capabilities are, whereas WP by default limits access to a private page to administrators and editors. This also can easily be adapted.

function jj_filter_private_pages_from_menu ($items, $args) {
    foreach ($items as $ix => $obj) {
        if (!is_user_logged_in () && 'private' == get_post_status ($obj->object_id)) {
            unset ($items[$ix]);
    return $items;
add_filter ('wp_nav_menu_objects', 'jj_filter_private_pages_from_menu', 10, 2); 

It seemed to me easier and more appropriate to filter the menu items than to hide them with CSS, your milage may vary.

To limit menu items for private pages to administrators and editors you should be able to substitute current_user_can ('read_private_pages') for !is_user_logged_in ().

share|improve this answer
Many thanks for this. This is such a better answer than the css hacks. This logic already exists in the core of wordpress when actually viewing the pages, so I am unsure why they left it out of the menu logic. – Phil_1984_ Sep 18 '15 at 9:27

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.