Hidden / Preview posts


#1

Hi there, I’d like to publish some posts “hidden”; in other blogs it’s in “preview” mode. The goal for me, would be to avoid those posts from being indexed or in the list in the front page; whereas I can share a URL to specific people who can see the post.

It’s a “security” issue in real life: I go on holidays and write posts in my blog, but I don’t want to share this with the public, so anyone can know I’m far from home, only with my people.

I saw this post (How to publish and “hide” drafts?) but it I didn’t find the “preview” feature. Is there any way to achieve this?

Thanks for your time!


#2

Not with Hugo only. You would need a password protected area of your site to have those previews published for the selected few.


#3

Hi @onedrawingperday, thanks for your answer. Could you be more specific? I have control of the server and the caddy serving the Hugo, but I don’t really understand the logic behind your proposal.

You mean putting all the blog behind a password? I’d like to exclude those “preview” posts from the front page, but having the rest of the blog public (as it’s currently).

I’m trying overwriting _defaults/list.html with a custom whereclause but I’m afraid this interferes with pagination.


#4

Hi!

If password word protection is not crucial you can use a param hidden in front matter.

In layouts/_default/baseof.html you can use something like this:

<meta content="{{ if .Params.hidden }}noindex{{ else }}index{{ end }}, follow" name="robots">

… in layouts/sitemap, layouts/_default/list.html etc. something like this:

{{ range .Data.Pages }}
    {{ if not .Params.hidden }}
        …
    {{ end }}
{{ end }}

As in your case it is a security issue it can be tricky—think of robots.txt, multilingual sites, RSS feeds etc.

I would indeed prefer a Hugo option for hidden pages.


#5

juts add some sugestion…

There’s some workaround to achive your goal

  1. You can create new section in your content eg private_post or something else that will be not indexed in your front page or other list page (I assume your theme only indexing the blog section)

  2. Make sure the private post is not appears in the

  • sitemap
  • RSS
  • other list template (taxonomy and related content)
  1. Make sure to tell Search Engine not to index the post in SERP

Just like @Grob suggest to You, it can be achieve it by using some param in the front matter then create a conditional function.

The cons: You have to redesign the theme and someone still can see the post once they get the URL


#6

See here for a simple tip:

As for the other tips posted here if these “preview” posts are supposed to be private you really need to have them behind a password protected area, because otherwise there is no guarantee that these pages will not show up online elsewhere.

Also please search the Forum about adding password protection, there are also other topics here that may be of use in your particular case.


#7

@Grob @Yudy_Ananda @onedrawingperday thank you for your help and your advice. My approach finally will consist on:

  1. not rendering the hidden posts (in frontpage, in archives, tags, trips, rss, etc)
 {{ $paginator := .Paginate (where (.Data.Pages "Type" "post") ".Params.hidden" "!=" "true") }}
  1. adding some random uuid to the URL so it’s more difficult to guess
https://myblog.com/800c3ab6-1027-42ae-a2c4-a41ebe0d4f4d-my-slug
  1. avoiding robots to index those posts
<meta content="{{ if .Params.hidden }}noindex{{ else }}index{{ end }}, follow" name="robots">
  1. adding password protection through the web server. This is secondary cause knowing they exist it’s the real problem; the content is not private, the private thing is knowing I’m out of home; as those posts exist it’s easy to assume, even not knowing what are the specifics of my trip. Anyway, it’s a good idea and a opportunity to improve my infrastructure.

Those measures will prevent some of the potencial problems; of course, I cannot think of a 100% secure scenario involving digital and analogic life, but this is a promising start.

Again, thank you very much!


#8

I’ve implemented this myself (was very surprised that default sitemap/rss templates do not check for draft status). My two cents in addition to the above:

Add visual cues to draft pages:

  <title>{{ block "title" . }}{{ if .Draft }}[DRAFT] {{ end }}{{ with .Title }}{{ . }} | {{ end }}{{ .Site.Title }}{{ end }}</title>

  <h1>{{ if .Draft }}[DRAFT] {{ end }}{{ .Title | default .Site.Title }}</h1>

If you use GroupBy, and group has only draft page(s), this is the way to avoid empty groups:

  {{ range (where .Pages "Draft" "==" false).GroupByDate "2006" }}

#9

What we do is we have a test domain where we publish all draft pages using the command hugo server -D when publishing to the test domain if it all looks good we remove the draft param from the toml and then we promote our build to production.

Might not be what you are looking for but its one way of doing it if you have CI/CD setup and version control.


#10

I have a feature suggestion here, because I too would like to see an easy way to create “preview” content where:

  • It’s published
  • It doesn’t show up in the nav tree
  • It can optionally be set to noindex and/or nofollow in robots.txt

An approach I’ve seen to this problem is marking any preview content with an underscore (or other special character) prior to the name in the file system. So page bundle stuff-i-want-to-preview becomes _stuff-i-want-to-preview.


#11

Hugo uses Afero as a file system. Don’t know if what you suggest is currently possible. I suggest you ask here:


#12

My approach to this is to have a separate section (“on-deck”) which I keep draft articles, often with a bit of nonsense in the url (for example, content/on-deck/draft-article-8dx71/index.md) and then make sure that section is excluded from any enumerated indexes, like so:

      {{ range ( first 10 ( where .Data.Pages "Section" "not in" (slice "page" "on-deck") ) ) }}
        {{ .Render "li" }}
      {{ end }}

It might not be the most idiomatic way to do something like this in Hugo, but it works.