The security configuration of my static blog

Even though I’m using Hugo to generate the source code of my blog, I decided to host it on a self-managed VPS. You could say this is a lot of overkill for a static site that can be hosted on any platform, and you will be right. But being in charge of my own turf gives me more control over the security settings than what I could have on a shared hosting.

The breaking point for me is that setting up, deploying, and maintaining web servers is always fun!

Setting up expectations

With this blog’s current security configuration I’m trying to:

  • Protect my blog from targeted/opportunistic attacks.
  • Detect security issues/incidents at an early stage.
  • Be ready to respond to successful security incidents.

Every piece of software on my server, or service around my website contributes to those goals.

I’m aware that there’s nothing 100% secure. In fact, affirming that a product/service/server is unhackable will likely raise the same reactions in the infosec community than questioning the speed of light in a room full of physicists.

However, if you want to have an online presence, you need to go as far as you can to protect your visitors, as this is the ultimate goal.

Protecting my web server

I’m using Ubuntu 18 as the OS of my web server. In my opinion, when it comes to security, new is always better.

Users, sessions & privileges

In a *nix system, the root user has the highest set of privileges. This user can run # rm -rf / and get away with it. The sudoers are users with privileges to perform actions outside their role.

If an attacker logs in to the server as a root user or a sudoer with enough privileges to modify the server’s configuration or the source code of my blog, things are going to get messy. That’s why protecting how these users log in, and what they can do once logged in is important.

Here’s what I did:

  • Created sudoers with specific privileges, according to the Principle of Least Privilege.
  • Installed SSH keys for the sudoers, and protected the keys with long, complex and unique passphrases.
  • Disabled SSH connections on port 22, and set a custom one.
  • In my /etc/ssh/sshd_config file I:
    • Set PermitRootLogin to no to block the root user from log in to the server.
    • Set PasswordAuthenticationChallengeResponseAuthenticationPermitEmptyPasswords and UsePAM to no, disabling log in to the server with a password, for all users.

These settings will complicate an attacker’s attempt to brute force his/her way to my server.

Web server: Nginx

I wanted to dedicate some time to my web server because it is what interacts with my visitors. My default choice is Nginx and I always follow OWASP’s hardening recommendations to configure it for security.

To summarise, these recommendations help me configure Nginx to:

  • Avoid buffer overflow attacks
  • Mitigate slow DoS attacks
  • Restrict access to specific domain names and IP addresses
  • Configure ssl/tls
  • Limit http methods and set http secure headers
  • Avoid unnecessary display of data and to delete unnecessary files

And in addition to OWASP’s security recommendations, I also configured Nginx to:

  • Only accept requests to and 301 redirect other addresses to this one.
  • Don’t serve any content when reaching out to the server’s IP address.

Server-side firewall: UFW

I personally love security solutions that require a lower learning curve and that are quickly to deploy. So when it comes to firewalls, I think UFW live up to its name: uncomplicated firewall. And that’s why I chose it.

My requirements for UFW were simple:

  • Block all by default
  • Allow requests from specific IP addresses (the WAF’s IP addresses)
  • Only allow https requests
  • Block ssh connections on port 22
  • Allow ssh connections on custom port

Some of this settings are redundant as per my Nginx’s configuration but think of this as a lettuce. It’s a layer on top of a layer on top of another layer.

Web application firewall (WAF/CDN)

I use Sucuri’s website security platform which includes a WAF/CDN. Why? Because even though my website is completely static, the backend still needs protection. My operating system and all of the software installed on it, including Nginx, could contain software vulnerabilities or misconfigurations.

Deploying a web application firewall allows me to protect my website from the attack vectors I wasn’t able to foresee.

There are other benefits of using a WAF. I’ll name the two most important to me.

Pain free migrations

Even though my website is static at the time of writing this post, it doesn’t mean it’ll stay that way.

WAFs are deployed through DNS changes, for this reason, they are great to deal with all sorts of migrations.

For example, if I want to move this blog to a CMS, all I’d have to do is set up a new server and point the WAF to the new IP address.

PS. I migrated to WordPress.


Serving my blog’s assets through a WAF/CDN will also help me improve the load time of my site, as a consequence of serving my content from servers closest to the location of my visitors.

And many other benefits that could fit on their own post.

SSL certificate

Even though SSL certificates do nothing to protect a website from the attack of cyber-criminals, they’re still a website security best practice.

Here’s why SSL is important for my blog:

  • SSL certificates validate domain names: meaning that when my visitors see on their browser’s address bar, they can be certain they are in the right place.
  • SSL certificates encrypt data in transit: which means that what you’re reading wasn’t read/modified by a third party while the data was in transit (man-in-the-middle attack).
  • Mixed content warnings: Browsers will panic if a website using an SSL certificate tries to load an asset over http. Why is this important? These mixed content warnings can signal attackers linking http assets from https websites (poor executed attacks).

Website monitoring

Despite all my efforts to secure this blog, things can still go south for many reasons. One of the best precautions I can take is to set a website monitoring service, such as the one Sucuri has, as part of its website security platform.

This monitoring service will verify the integrity of files and will load my website frequently using different IP addresses and user-agents to try to discover conditional malware or any other sign of compromise on my blog.

Here’s what I monitor for:

  • Source code changes: A server-side scanner will verify if there are changes in the source code of my website every 24 hours.
  • Malware & blacklists: An external scanner will verify if my website is serving malicious pop-ups/pop-unders, hosting malware or if it’s in any major blacklist every 30 minutes.
  • DNS changes: To verify any change on the DNS for the domain If attackers break into my configuration panel and change the DNS for this domain, I will know within 30 minutes.
  • SSL changes: Because you can’t never be too precautious… I look for any change in my SSL certificate every 30 minutes.
  • Uptime: If my blog’s server goes down for any reason, I’ll be notified within 5 minutes.

Response plan

My friend and colleague Val Vesa recently published a great blog post about creating a response plan you can trust. In my case, if the worst happens and my server is compromised, I have a couple of options.

  • Just like in real life, I travel light. I can always destroy this server, create a new one and deploy my blog again. Making sure to understand how the compromise happened in order to block it in the future of course! This is an option because I’m a technical person and I know my way around terminals and configuration and log files.
  • Submit a ticket to the response team at Sucuri so they can figure it out for me.

What am I lacking?

Automatic server deployments

At the time of writing this blog post I don’t have an automatic server deployment process and this need to change. Gio Delgado posted an amazing blog post talking about his over-engineer blog setup on I’ll give it a few reads to take on inspiration!

An important note

This blog post doesn’t get to have a conclusion because securing online assets is a never ending process. Expect this to be a living blog post, updated regularly.

I hope you enjoyed the reading time!

Did you find a bug in my blog? Do you have any question about my security configuration? Do you want to share a gif or a meme? I’m on Twitter!