My understanding of private posts/pages is that they only work when you are logged into the WP admin system as an administrator or editor.

I have a site where I need to occasionally share custom posts to users via links and I don't want them to appear elsewhere on the site. Setting posts to "private" almost does what I want as it instantly removes the posts from the site homepage and other areas that it would normally be included.

The only problem is that the private post feature assumes I want to look at the post when logged in to the admin which is not the case as I get a 404 error when not logged in. I want to share this post with strangers manually as and when I need to without a password and for them to appear as normal via their standard permalink.

This may well be plugin territory but to my surprise I haven't found one that does that.

To clarify, the post type I need to implement this on is a custom post type defined by a plugin that the site needs in order to run.

  • I'm not sure I understand entirely but this is what I'm getting; You only want a post to be accessible if the link is known to the person you've provided it to (whether that person has an account or not)?
    – Jami Gibbs
    Commented Dec 30, 2016 at 13:11
  • Beware of the fact that those pages, judging from your description, will never be really private, but merely not that easily accessible. So if it's somewhat sensible information of any kind, just don't do it. Commented Dec 30, 2016 at 19:34
  • You mentioned you want to do this with an existing custom post type, do you plan on making the whole post type private or just certain posts?
    – Tunji
    Commented Jan 3, 2017 at 8:42

5 Answers 5


If you don't want to use a plugin (or can't find one that does what you're needing), you might want to approach it this way:

  1. Add a custom meta box that allows you to mark the post as hidden.
  2. Modifying the query with pre_get_posts to remove the posts you've labeled as hidden from your site (but will be available with a direct link).


Following the suggestion above, here is a possible solution.

Create a custom meta box

First, create the custom meta box by registering one:

function yourtextdomain_add_custom_meta_box() {
  add_meta_box("demo-meta-box", "Custom Meta Box", "yourtextdomain_custom_meta_box_markup", "post", "side", "high", null);
add_action("add_meta_boxes", "yourtextdomain_add_custom_meta_box");

Add the markup to the metabox (a checkbox in the case):

function yourtextdomain_custom_meta_box_markup($object) {
  wp_nonce_field(basename(__FILE__), "meta-box-nonce"); ?>
      <br />
      <label for="meta-box-checkbox">Hidden</label>

      <?php $checkbox_value = get_post_meta($object->ID, "meta-box-checkbox", true);
          if($checkbox_value == "") { ?>

          <input name="meta-box-checkbox" type="checkbox" value="true">

      <?php } else if($checkbox_value == "true") { ?>

          <input name="meta-box-checkbox" type="checkbox" value="true" checked>

      <?php } ?>

      <p style="color: #cccccc"><i>When selected, the post will be removed from the WP loop but still accessible from a direct link.</i></p>

This will give you a meta box for each post that looks like this:

And finally save the meta box value:

function yourtextdomain_save_custom_meta_box($post_id, $post, $update) {
    if (!isset($_POST["meta-box-nonce"]) || !wp_verify_nonce($_POST["meta-box-nonce"], basename(__FILE__)))
        return $post_id;

    if(!current_user_can("edit_post", $post_id))
        return $post_id;

        return $post_id;

    $slug = "post";

    if($slug != $post->post_type)
        return $post_id;

    $meta_box_checkbox_value = "";

    if(isset($_POST["meta-box-checkbox"])) {
      $meta_box_checkbox_value = $_POST["meta-box-checkbox"];
    update_post_meta($post_id, "meta-box-checkbox", $meta_box_checkbox_value);
add_action("save_post", "yourtextdomain_save_custom_meta_box", 10, 3);

In the wp_postmeta table, you should now see the meta value 'true' assigned to the posts you've checked as hidden and saved:

Post Meta Table

Modifying the query with pre_get_posts

Now it's just a matter of filtering out those posts that are marked as hidden from the main query. We can do this with pre_get_posts:

add_action( 'pre_get_posts', 'yourtextdomain_pre_get_posts_hidden', 9999 );
function yourtextdomain_pre_get_posts_hidden( $query ){

  // Check if on frontend and main query.
    if( ! is_admin() && $query->is_main_query() ) {

    // For the posts we want to exclude.
    $exclude = array();

    // Locate our posts marked as hidden.
    $hidden = get_posts(array(
      'post_type' => 'post',
      'meta_query' => array(
          'key' => 'meta-box-checkbox',
          'value' => 'true',
          'compare' => '==',

    // Create an array of hidden posts.
    foreach($hidden as $hide) {
      $exclude[] = $hide->ID;

    // Exclude the hidden posts.
    $query->set('post__not_in', $exclude);

  • 3
    The only drawback to this is that depending on how many posts you have, it can make things quite slow. Meta queries are not fast, and doing it on every request may grind things to a halt.
    – Milo
    Commented Dec 31, 2016 at 16:23
  • It may be easier and faster to delete_post_meta() when a value is not checked so you can run a meta_query on your meta-box-checkbox key using NOT EXISTS instead of using the post__no_in parameter. Meta Queries
    – Howdy_McGee
    Commented Jan 2, 2017 at 15:55

In WordPress core, private posts are probably the closest you can get without using a plugin. Since you mentioned that you need to send links to private posts to others, I recommend you to not re-invent the wheel and use one of the plugins out there that does this.

For example, I can highly recommend Public Post Preview by WordPress Core Committer Dominik Schilling (ocean90). I think it does exactly what you want:

Enables you to give a link to anonymous users for public preview of a post before it is published.

It handles all the link expiration stuff, capability checks, etc. for you so you don't have to worry about accidentally exposing too much of your content when using a custom built solution.

  • Was going to recommend a solution in concept somewhat similar to what this plugin promises to accomplish. Good suggestion!
    – CK MacLeod
    Commented Jan 1, 2017 at 17:19
  • @swissspidy you are right,that plugin looks great. I will check this as well, thanks for the link.
    – AdamJones
    Commented Jan 5, 2017 at 16:40

Probably plugin territory, because as you mention the way private pages work, they are only visible to logged in admin or editor level users.

See https://codex.wordpress.org/Content_Visibility

Not totally clear on your use case, but you may be able to create a custom post type that is not visible on index pages (set public to false, don't add an archive for it, etc.) so only someone with the permalink could see it. Someone could guess the permalink though, so it wouldn't be totally 'private'.


  • Thanks for the ideas @jetyet47. The use case is actually with a custom post type that already exists that was defined by the theme this website uses (out of the box customised - not my choice but I'm having to maintain something built by others).
    – AdamJones
    Commented Dec 28, 2016 at 18:24
  • 1
    @AdamJones You can use the register_post_type_args filter and change whatever you need to.
    – Milo
    Commented Dec 30, 2016 at 16:22

1) Create a custom post type that is NOT public (e.g "hidden_posts").

2) Create a custom template in the current them and run a custom $wpdb query passing the post_type parameter to "hidden_posts" in the template

3) Create a regular page using the template you just created.

4) Add the page in (3) to your robots text and deny access to make sure it is not indexed!

5) Vote my answer as the best :)

NB: If you need a detailed code of this, let me know !


You can make a custom post type for this type of posts that you want to display only to non-logged in user .

in the you can customize the single.php file for your custom posttype by overriding it in custom-postype.php in this you can check if the user is not logged in you can display it else not.

Let me know your views in below by comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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