How this works

Yesterday I ended tweaking all the website and adapting it to the new design, and as so, I wanted to share with you all the techniques that I used and how you can apply them to your own site.

Web Server

I'm still using CloudFlare as a DNS cache and service provider in case my entire website goes down. Their service is fantastic, and if you get charged by bandwidth, you should consider adding it to your website. Behind CloudFlare there's a Nginx server that first tries to serve static content from a folder in my VPS. This is where trickery starts:

I wanted to start using GitHub Pages because I don't want to manage webserver issues (I'm lazy), and as so, I needed to proxy content from Nginx to my GitHub page. This is easily done adding this to your server config:

{% highlight nginx %} location / { try_files $uri $uri/ @gh_pages; }

location @gh_pages { proxy_pass https://hugmanrique.github.io; proxy_redirect off; proxy_buffering off;

proxy_set_header Host hugmanrique.github.io;
proxy_set_header X-Host hugmanrique.github.io;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Protocol $scheme;

expires off;

} {% endhighlight %}

This is when I faced another issue, I also need to rewrite URLs to send PHP files from my VPS (/playmanager/buy = /playmanager/buy.php). The try_files directive from Nginx only allows one named location (in this case @gh_pages) as a fallback and I needed two, I finally found a way:

{% highlight nginx %}

Add this to your @gh_pages location

proxy_intercept_errors on;

recursive_error_pages on; error_page 404 = @php_files;

This is the other named location

location @php_files { rewrite ^(.*)$ $1.php last; } {% endhighlight %}

First, it tries to proxy files to GitHub Pages directly (this is where my main traffic usually will go) and if a 404 error occurs in GitHub, proxy_intercept_errors takes care of it and automatically throws a 404 error from Nginx, and as I have rewritten this error to be @php_files, this now tries the extenssionless PHP files. If nothing is found, there's nothing more to do, just throw an standard 404 error (In my case /404).

Security Measures

SecurityHeaders.io This was a tricky one too. I always wanted to serve the most secure experience and I learnt about securityheaders.io, a cool website which shows you what HTTP measures you can do to improve security.

I started by adding Strict-Transport-Security a header which tells the browser to only connect to this domain by using a HTTPS connection. You have to specify a max-age, and I chose 1 year. This one was easy. I then tried to implement X-Content-Type-Options, a header which prevents the MIME-sniffing of a file and stick with the declared one by the web server. Then I went to X-Frame-Options, this one was pretty straightforward too, this header tells the browser to not embed your website in other domains via iframes or objects. I then went to implement X-Xss-Protection which tells the browser what to do in case malware is found in scripts.

And then I went to the worst (but most secure) one, Content-Security-Policy. This header tells the browser what kind of URLs are allowed to be loaded depending on the element (an image, a script, a font)... I will write down what I finally used and explain it:

{% highlight nginx %} default-src 'self' https://hugmanrique.me; img-src 'self' data: https:; object-src 'none'; font-src 'self' https://hugmanrique.me https://fonts.googleapis.com https://fonts.gstatic.com https://maxcdn.bootstrapcdn.com; connect-src 'self' https://api.github.com; form-action 'self' https://hugmanrique.me https://www.paypal.com; style-src 'self' 'unsafe-inline' https://hugmanrique.me https://maxcdn.bootstrapcdn.com https://fonts.googleapis.com; script-src 'self' 'unsafe-eval' https://cdnjs.cloudflare.com https://hugmanrique.me https://code.jquery.com https://www.google-analytics.com https://ajax.googleapis.com https://maxcdn.bootstrapcdn.com https://cdn.socket.io {% endhighlight %}

default-src is the fallback src checker which only allows access to self (for dynamic filepaths) and https://hugmanrique.me. I now only allow images that are secure by using https: and no objects at all. Here is where the trouble starts, I use lots of fonts, CSS and JavaScript frameworks from CDNs and I needed to add one by one. I checked all the websites and hopefully I didn't miss anything. connect-src now only allows jQuery AJAX requests to the GitHub API and my own domain and I only allow form data to be sent to my own domain and paypal.com (donation and buying things).

New Design

I spent a month building my own framework and I'm happy with the result so far. I don't use Bootstrap anymore and jQuery only if needed. The page load times have improved a lot. It isn't 100% mobile-friendly but it will be soon.

This framework is heavily inspired by Bootstrap and Primer, the GitHub's internal CSS open-source framework.

I plan to make HugStrap (yeah, that's how I name things) open-source as soon as all the major bugs are all gone (not forever though :disappointed:).

I also used GitHub's Octicons, a SVG iconset which has all the major icons a simple website needs. This brings a lot of benefits over using a CSS iconset such as Font-Awesome:

  • Icons are retina friendly, not matter the resolution, they will always look good
  • I can embed them directly using a Jekyll's plugin
  • They are much lighter

GitHub Pages

I thought about using GitHub Pages a long time ago. It allows my webpage to be served by GitHub's CDNs and I don't have to worry about downtime, they are almost always online. I had some trouble with plugins and GH Pages, and I will tell you the trick to make it work:

As I said before, I use GitHub's Octicons, which I embed using a plugin. GitHub Pages doesn't allow plugins to be run for security reasons, so the only way to go is render the Jekyll page locally and then commit and push the _site directory, a place where your rendered Jekyll templates are. I now have a repository called hugmanrique.github.io which has two branches, main, the one which stores the rendered content and dev, which stores the root Jekyll page with the _site folder excluded via .gitignore. To render the page I only need to use bundle exec jekyll build --watch and push to the repo as needed.

This is the end...

I hope you learned how not everything in the web is easy to make or fix, and if you are planning on doing something similar on your server, there you have the problems you will face and one way to fix them (I know there are more, and if you feel I could improve something, feel free to tell me via [Twitter]({{ site.data.social.twitter }}) or [Email]({{ site.data.social.email }})).

Hugo :wink:

© Hugmanrique. Made with