How I made this blog more privacy friendly. (And how you can too!)

I love and highly respect other peoples privacy. This blog is more just for me and I feel that I don't really need to have any responsibility for my readers. But really, that's an unhealthy way of thinking. So I have fixed some things to make my blog way more privacy friendly(than it already was!).

First of all you need to know that this is a default Ghost-install. So this goes for all other Ghost blogs out there. Hopefully I can help other bloggers running Ghost do the same thing as I did.

Also, Ghost already has built in privacy-setting and I highly encourages you to change this setting. Just add this to your config.js(in the production-section):

  privacy: {
           useUpdateCheck: false,
           useGoogleFonts: false,
           useGravatar: false,
           useRpcPing: false,
           useStructuredData: false

But this blogpost will go through much more than just that. Also, if you choose to not use Google Fonts it will only be applied in the admin-area(and in some other places). The theme will still be using Google Fonts.

Things that need to be fixed

As you can see in the above picture the extension Privacy Badger detected some trackers; fonts from Google and a copy of jQuery from their CDN. That's not so bad, but it can(and will) get better!

I want everything to be served from one place; my server. So what I need to do is download all the used fonts and offer a local jQuery.

I also need to get rid of Cloudflare and use a certificate I bought. More control = better.

Local jQuery

This was the easy part. I just made a grep on the Ghost-folder like this:

grep -ir "jquery-1.11.3.min.js" ./*

And found that it was refferered in one file -- content/themes/casper/default.hbs

So I downloaded the file "" and placed it in content/themes/casper/assets/js and changed the line(in default.hbs)

< script type="text/javascript" src="">
< script type="text/javascript" src="{{asset "js/jquery-1.11.3.min.js"}}">
Restarted node and jQuery was now served locally!

Before: After:

Local fonts

All fonts originated from Google but it was quite easy to also serve them locally because I just needed to download the CSS-file locally and also the fonts, then change in the CSS-file that fonts are served via my domain. The original CSS was this:,700,700italic,300italic|Open+Sans:700,400 So I wget'd that file and saved it in content/themes/casper/assets/css as fonts.css. Then I changed the default.hbs-file again from:
< link rel="stylesheet" type="text/css" href=",700,700italic,300italic|Open+Sans:700,400" /> 
< link rel="stylesheet" type="text/css" href="{{asset "css/fonts.css"}}" />
Now I just needed to download all the fonts from google and store them locally. For this I grepped all the URL's and downloaded them like this:
$ grep -o "*\.woff2" fonts.css >

$ wget -i

All fonts were downloaded to content/themes/casper/assets/fonts

After that I just changed the URL to the fonts from pointing to Google to my domain like this:

$ sed -i 's/https:\/\/\/s\/merriweather\/v8\//https:\/\/\/assets\/fonts\//g' fonts.css

$ sed -i 's/https:\/\/\/s\/opensans\/v13\//https:\/\/\/assets\/fonts\//g' fonts.css

I restarted node and voilĂ !

Add an onion!

This was an easy one! I hope you're running nginx and you just copy the ghost.conf to ghost_hs.conf and add the following:

server {
       listen 80;
       server_name < your onion >;
       location / {
       add_header X-XSS-Protection "1; mode=block";
       add_header Cache-Control "public, max-age=0";
       add_header Content-Security-Policy "script-src 'self' ; font-src 'self' ; connect-src 'self' ; block-all-mixed-content; reflected-xss block; referrer no-referrer";
       add_header X-Content-Type-Options nosniff;
       add_header X-Frame-Options DENY;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header Host $http_host;
       proxy_set_header X-Forwarded-Proto $scheme;

And in /etc/tor/torrc/:

HiddenServiceDir /path/to/ghost
HiddenServicePort 80

Restart nginx and tor and you're done! Yes, it's THAT easy!

Note: The private key and hostname will be saved to the HiddenServiceDir-path! Take backup!

I did this to generate 2000 Onion-URL's:

$ for i in {1..2000}; do ./shallot ^chloe | tee -a keys.txt;done

After this command was done I just looked through the file and looked for an onion that stood out, and it became chloenlpvlemmmmd.onion because it has chloe in the beginning and an easy pattern to remember; lemmmmd in the end. Perfect!

Remove Cloudflare

But why? Well, because Cloudflare is placed in USA and all your traffic is routed through them when you visit my blog. Also, if you use Cloudflare they will set a Cookie(__cfduid) on all visitors.

The absolute best thing would be to host your server on a privacy friendly location but because I'm not that rich DigitalOcean has to do, also, if I can route my visitors through less servers I'll be happy.

A new certificate

Because I used Cloudflare's built-in certificate I needed to buy a one. Let's Encrypt is not open yet so I bought a cheap certificate over at for around 10$. But I will use Let's Encrypt later.

After the purcase I installed it I made sure that it got an A+ from SSLlabs.

Add some sexy headers

I also added some headers that I did not have before. CSP with no-referrer(try it out here) and HSTS. Also Public Key Pinning but I'll fix that later.

X-Powered-By: chloe
Cache-Control: public, max-age=0
Vary: Accept-Encoding
Server: chloe
X-XSS-Protection: 1; mode=block
Cache-Control: public, max-age=0
Content-Security-Policy: default-src 'self' ; script-src 'self' ; font-src 'self' ; connect-src 'self' ; block-all-mixed-content; reflected-xss block; referrer no-referrer
Strict-Transport-Security: max-age=15768000
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

No logging

Add this to your ghost.conf in your Nginx site-enabled folder:

access_log off;
error_log /dev/null;


When adding an onion it's important to also add HTTP-headers. Some people completely forget this. Also, always remove the referer-header. Thanks to @bcrypt for pointing this out.