The new Obsigna BLog authored with ContentTools and backed by ContentCGI

In December 2012 I set up my BLog with WordPress, and while it is really true, that you can get your WP system up and running without major headaches, it comes also with a lot of things that most people did not ask for and usually do not need. In addition, several WordPress themes load web-fonts and the jQuery library from Swmnbn’s CDN services. I don’t want to be tracked, and I don’t want my visitors to be tracked either. This attitude alone let me maintain a customized CDN-free albeit up to date WordPress for 6 years by the way of the SVN vendor branches method. Finally - the GDPR was lurking already - WordPress 4.9 introduced visual emoticons from yet another CDN. That was the straw to break the camel's back. So, I looked out for another system.

Back to the roots

BLog is the truncation of weBLog, which means it is a kind of a diary published on the web. For publishing a text with some pictures on the web, we don't need WordPress. We would need some writing tools which store our content into the directory where the web server can find it on visitor's requests. Ideally these writing tools would let us author our diary in WYSIWYG style, and it would be really nice if we could edit our text online. Do we need PHP? No. Do we need MySQL? No. Do we need WordPress for this? No, not either.

We need Anthony Blackshaw's ContentTools frontend for online authoring our content in a WYSIWYG manner, and my ContentCGI backend for storing the changes on the server.

Visitors of our BLog see static web pages only. The editing facility shall be accessed by a special URI which is access protected by HTTP Digest Authentication.

LoadModule auth_digest_module libexec/apache24/
LoadModule deflate_module libexec/apache24/
LoadModule ssl_module libexec/apache24/
LoadModule socache_shmcb_module libexec/apache24/
LoadModule proxy_module libexec/apache24/
LoadModule proxy_fcgi_module libexec/apache24/

SetOutputFilter          DEFLATE
SetEnvIfNoCase           Request_URI "\.(?:gif|jpe?g|png)$" no-gzip

Listen                   443
SSLProtocol              All -SSLv2 -SSLv3
SSLCipherSuite           HIGH:!aNULL:!AES128:!SSLv2:!SSLv3
SSLHonorCipherOrder      on

SSLPassPhraseDialog      builtin
SSLSessionCache          "shmcb:/var/run/ssl_scache(512000)"
SSLSessionCacheTimeout   300

<VirtualHost *:80>
   RedirectPermanent     /

<VirtualHost *:443>

   DocumentRoot          "/usr/local/www/ContentCGI/webdocs"

   <Directory "/usr/local/www/ContentCGI/webdocs">
      Require            all granted

   <LocationMatch "^(/_|/.*/_).*$">
      ProxyPass          "unix:/tmp/ContentCGI.sock|fcgi://"
      SSLOptions         +StdEnvVars

   <Location "/edit/">
      AuthType           Digest
      AuthDigestProvider file
      AuthUserFile       "etc/apache24/ContentEditors.passwd"
      AuthName           ContentEditors
      AuthDigestDomain   /
      Require            valid-user
      ProxyPass          "unix:/tmp/ContentCGI.sock|fcgi://"
      SSLOptions         +StdEnvVars

   SSLEngine             on
   SSLCertificateFile    "etc/letsencrypt/live/"
   SSLCertificateKeyFile "etc/letsencrypt/live/"

ContentCGI is a FastCGI daemon which is to be invoked by the Apache web server in case of requests of said special URI's. It responds to edit, save, create, delete, revive and search requests. The search facility is backed by the Zettair search engine.

The ContentTools and ContentCGI are both available on GitHub. ContentCGI runs out of the box on FreeBSD and macOS. It is extensible by the way of plugins. The ContentCGI package comes with the Hello Responder Delegate plugin, named hello-delegate. This one serves as a template for creating new plugins in order to add more functionality. The shell script may be used for cloning the hello plugin.

Those, who need a full featured CMS, would want to stay with WordPress, or Drupal etc. Currently, even a commentary facility is not implemented by ContentCGI (which is problematic anyway with the advent of the GDPR). Who needs a web diary only, may want to explore the new system (s. below).


In order not to disturb users with links to my management utilities, I placed respective menu items into the favorites bar of my web browser.

Let’s say, the base (static) URL of the respective site is, then the URL’s for the editing mode would be:

Enter Editing Mode​

In general, the local links are composed in a way that the editing mode is kept when editors navigate within the site. This means we could enter the editing mode and navigate to the articles which we are going to edit. Saving is done by clicking the check mark in the opaque green circle. Editing can be canceled by clicking the cross in the opaque red circle.​

Create a new Article​

The default language is English = ‘en’. Other languages for the article to be created may be defined by the way of the lang query option with said URL. For example German = ‘de’ would be adjusted by​.
The language option is added to the lang attribute of the page’s HTML tag. This let’s modern browsers do a perfect language based automatic hyphenation and spell checking of the text.
The file names of the articles are composed of the UNIX time stamp of creation and the extension .html. Only time-stamped files are screened for automatic generation of the Index and the TOC files. The latter appears at the right side of all articles.​

Delete an Article

Here the article file having the name 1529528376.html would be deleted and the index and toc files would be regenerated automatically. Removal takes effect immediately, and without any questioning whether we are sure about our actions. Although, the respective article is not deleted from the files system, but moved into /tmp. I got in my /etc/rc.conf:​

Revival of the the Index and TOC​

Usually auto-indexing would be initiated in the course of one of the above commands. Auto-indexing involves as well removal of stale images. Sometimes we want to manipulate the webdocs directory directly by bypassing ContentCGI. Once the changes are done, the revive action would update the Index and the TOC to reflect the actual state.​

Searching is actually a non-editing command, since this can be called by occasional visitors. How this technically works is described my BLog article Using the Zettair Search Engine locally on a Site. Here comes a brief summary:

Searching and refreshing the search index

In case the installation instructions below are followed, everything should be in place for providing the search facility on the site. ContentCGI does not directly update Zettair’s search index. Instead it would place a token into the Zettair’s index directory once something on the site has been changed. The spider script which comes with ContentCGI would be called by a cron job (I do it every minute) and start reindexing only in case it finds the token, otherwise it quits immediately.​
While ContentCGI itself works with UTF-8 throughout, Zettair is quite an old system. The main work was done before UTF-8 became popular. So Zettair works well for languages whose characters may be encoded by one of the ISO-8859-x character sets. I write in English, German and Portuguese, so I use the Western European encoding ISO-8859-1 in the spider script, and in the search-delegate plugin of ContentCGI see:​
Adaption to another ISO-encoding should be straightforward. In the whole source base of ContentCGI/Zettair search for ISO-8859-1 and replace by one of the other ISO-8859 encodings. Then re-compile everything. I fear, Asian languages do not work well with Zettair.​


This tutorial assumes, that you got a clean FreeBSD server connected to the internet up and running, and that it got domain names, let's say content.dom, blog.content.dom and www.content.dom, which resolve to your server. The server must be able to respond on the HTTPS port 443. You should also have chosen already a nice site name, in oder not to waste hours for think about the name in the course of the installation - let's say your site name would be Nice Content.

When following the instructions below, you need to replace
- Nice Content - content.dom - blog.content.dom - www.content.dom -
by the actual names, of course.

  1. Login as User root, and install the requisites by the way of the FreeBSD package+ports system:
    pkg install -y py27-certbot
    pkg install -y apache24
    pkg install -y subversion
    pkg install -y libobjc2
    pkg install -y utf8proc
    pkg install -y iconv
    pkg install -y poppler-utils
    pkg install -y clone
    For image processing we would employ GraphicsMagic. The default options drag-in a lot of unfortunate stuff from the GNU Compiler Collection and from X11. Therefore, add the few really needed dependencies of GraphicsMagic from the FreeBSD package repository, and build+install graphic/GraphicsMagick from the ports with a custom option-set.
    pkg install -y webp
    pkg install -y libwmf-nox11
    cd /usr/ports/graphics/GraphicsMagick
    make config
    Disable Jasper, OpenMP, X11
    Enable SSE
    make install clean
  2. Get SSL certificates for your domains from with the official certbot application:
    certbot certonly --standalone --email admin@content.dom \
    -d content.dom \
    -d blog.content.dom \
    -d www.content.dom
  3. Prepare the installation directory and get the ContentCGI, ContentTools and Zettair sources from GitHub:
    mkdir -p ~/install
    cd ~/install
    svn checkout ContentCGI
    svn checkout ContentCGI/ContentTools
    svn checkout ContentCGI/plugins/search-delegate/zettair-spider
    cd ~/install/ContentCGI
  4. Execute the build and installation script:
    ./ install clean
  5. Copy the virtual host configuration file into the Apache Includes directory. It is wise to name the virtual host config file using the desired domain name:
    cp -p apache-vhost.conf /usr/local/etc/apache24/Includes/blog.content.dom.conf
  6. Use the sed command to set the site's title, and to substitute the virtual host dummy domains and to the desired site title and domain names, e.g.:
    "Nice Content" - content.dom - blog.content.dom:
    sed -i "" -e "s/CONTENT EXAMPLE/Nice Content/"         /usr/local/etc/apache24/Includes/blog.content.dom.conf
    sed -i "" -e "s/" /usr/local/etc/apache24/Includes/blog.content.dom.conf
    sed -i "" -e "s/"              /usr/local/etc/apache24/Includes/blog.content.dom.conf
  7. Create the password digest file of the HTTP Digest authentication for editing the content. Inform your real name, because the system will use this name in the signature of the articles:
    htdigest -c /usr/local/etc/apache24/ContentEditors.passwd ContentEditors "Your Real Name"
  8. We may add more users with the same command but without the -c flag:
    htdigest /usr/local/etc/apache24/ContentEditors.passwd ContentEditors "Author II Real Name"
  9. Use the shell script for populating the site's directory with an initial set of yet empty web documents, derived from the model files of the content-delegate plugin:
    plugins/content-delegate/ "Nice Content" /usr/local/www/ContentCGI/webdocs
    chown -R www:www /usr/local/www/ContentCGI
    chmod -R o-rwx /usr/local/www/ContentCGI
  10. Check the Apache configuration, the output of the following command should be Syntax OK:
    httpd -t
  11. Enable Apache and ContentCGI in /etc/rc.conf by adding the following lines - make sure the CGI daemon runs as the non-privileged user www:
    ContentCGI_flags="-u www:www -w /usr/local/www/ContentCGI/webdocs"
  12. In case it does not exit, create the file /etc/rc.conf and add the following command:
    /bin/rm -rf /tmp; /bin/mkdir -pm1777 /tmp
  13. Start Apache and the ContentCGI Daemon:
    service apache24 start
    service ContentCGI start
  14. Prepare the working directory for the Zettair search engine:
    mkdir -p /var/db/zettair/blog.content.dom
    chown -R www:www /var/db/zettair/blog.content.dom
    chmod -R o-rwx /var/db/zettair/blog.content.dom
  15. Add to /etc/crontab the following lines:
    # call the Zettair spider every minute - it will re-index the articles, if a respective token is present in /var/db/zettair
    *       *       *       *       *       www     /usr/local/bin/spider /usr/local/www/ContentCGI/webdocs/articles blog.content.dom > /dev/null 2>&1
  16. Point your browser to your domain and explore the new system.

Copyright © Dr. Rolf Jansen - 2018-06-20 20:59:36

Discussion on Twitter: 1082820500718583808