Configuring Write Freely as a single-user, multiple blog instance


Write Freely is the self-hosted version of Write.as. I stumbled across write.as on the Fediverse and immediately took a liking to it. It's spartan, clean, and has literally no distracting features to play with; it's perfect for people that put content first.

One of the features I liked as a user of write.as was the ability to have multiple blogs. Write Freely has that feature as well, but not when you run it in single-user mode. Putting it into multi-user mode does indeed give you multiple blogs, but it also activates a landing page for users to log in, pay for services, etc. I was looking for a single-user setup with multiple blogs. That combination is not available out of the box so I documented what I did.

Set single_user mode to false

This puts your instance into multiple user mode so you can now get multiple blogs. Before you enable multi-user mode, ensure the open_registration is also set to false or you may end up with surprise customers. You can change this setting in your writefreely_directory/config.ini file. Here's the app part of my config.ini

[app]
site_name         = jonw
site_description  = broadcasting to the fediverse from jonw@jonwatson.ca
host              = https://write.jonwatson.ca
theme             = write
disable_js        = false
webfonts          = true
single_user       = false
open_registration = false
min_username_len  = 3
max_blogs         = 10
federation        = true
public_stats      = false
private           = true
local_timeline    = false
user_invites      = 

But now you're stuck with a landing page full of “join the Fediverse” and “start blogging” stuff. That is not what I wanted my visitors to see when they landed on my domain.

Modify the landing page template

I found two different directories with template files. The syntax reminds me of smarty, but I haven't dug deeply into it yet. The landing page template is at writefreely_directory/pages/landing.tmpl

Do whatever you want in there, but leave the two define/end statements. It seems that removing those causes the template engine to fail and your instance won't load. This is all I did but I will probably spruce it up later:

{{define "head"}}
<title>{{.SiteName}}</title>

{{end}}
{{define "content"}}
<div style="width:905">
	<div>
		<h1 style="margin-top:0;max-width:8em;">write.jonwatson.ca</h1>
                <h3>navigation</h3>
                <p><a href="https://write.jonwatson.ca/blog">Blog (mostly technical)</a>
                <p><a href="https://write.jonwatson.ca/stories">Stories (mostly whatever)</a>

	</div>
</div>
{{end}}

That gave me a landing page that is about this sexy:

write.jonwatson.ca Landing Page

Deal with caching issues

I use a firewall/CDN to protect my sites. It works in such a way that my entire site may get cached, unlike typical CDNs which only cache known static content like images and css files. Normally, this does not present a problem because mature apps like Wordpress, Drupal, etc, are aware of potential caching issues and send 'no cache' headers to ensure that logged-in user data is never cached. Write Freely does not send any cache headers that I can see, so it leaks considerable private user data when cached.

One option is to try to modify the Write Freely engine to send no-cache headers, but an even easier solution is to just get your web server to do it. I use Nginx so I just added the add_header Cache-Control private to the Write Freely site configuration that activates when my blog or stories site is called:

    location ~^/(blog|stories) {
      add_header Cache-Control private;
    }

The CDN I use will disregard that header on static content as part of its firewall protection, so this configuration will allow all the static content of my site to be cached, but none of the pages. This ensures that anonymous users won't be able to load logged-in user pages.

We can test it using curl and see the new header at the end:

$ curl -IL https://write.jonwatson.ca/blog/
HTTP/2 200 
server: nginx
date: Thu, 07 Feb 2019 19:48:30 GMT
content-type: text/html; charset=utf-8
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
content-security-policy: upgrade-insecure-requests;
vary: Accept-Encoding
cache-control: private

If you're using a more typical CDN then you will probably want to put that add_header line within a new location section that only targets php pages. you'll want to test that pretty well.

#writefreely #nginx #caching #infosec