My Server Setup

Sep 2, 2019

Site Setup

This site is powered by Hugo, served by Apache. I moved away from WordPress because it’s too heavy for a personal site. It’s also slow and I could not find a theme that suits the purpose of the site. So, eventually I’m with Hugo and using markdown for everything.

The setup is very simple:

  • Download Hugo from GitHub. Note that we need the extended version.
$ wget
  • Extract the downloaded pacakge.
$ sudo dpkg -i hugo_extended_0.55.5_Linux-64bit.deb

The theme I use is hugo-book. It’s built for documentation but I find it also works great for me.

SSL is enabled through Let’s Encrypt.

AWS EC2 Setup

The graph below describes the overall structure of my EC2, running an Ubuntu 16.04 Bitnami WordPress stack. I still have WordPress running for some other domains, but is static and directly served by Apache. I use Apache to redirect to as well.


Since some apps (e.g. LeGao - Make LEGO Pixel Art) need to communicate with my server, I re-write the backend as shown above.

In the past, I had separate Flask apps listening on ports 5000, 5001, etc., and opened these ports on the AWS Security Group. It worked but apparently was not ideal. First, it is risky that additional ports were open. Second, sometimes results were blocked by certain browser as they were not returned through usual ports.

Reverse Proxy

Therefore, I’ve moved everything behind a unified gateway I use Apache to reverse proxy all requests to that redistributes them according to the request url. Itself is a very simple script, the core logic is just to use requests.request to forward the request to the responsible app according to the port mapping dictionary.

import requests
from flask import Flask, request, Response, redirect,

class Server:

    def __init__(self): = Flask(__name__)
        self.mapping_app_port = {
            'legao': '5000',
            'optimization': '5001'

    def run(self, *args, **kwargs):*args, **kwargs)

    def add_endpoints(self):'/', None, self.handle_default_request)'/<app>/<path:path>', None,
                              self.handle_requests, methods=['GET', 'POST'])

    def handle_default_request(self):
        return redirect('')

    def handle_requests(self, app, path):
        resp = requests.request(
      , f'localhost:{self.mapping_app_port.get(app)}'),
        excluded_headers = []
        headers = [(name, value) for (name, value) in resp.headers.items()
                   if name.lower() not in excluded_headers]
        response = Response(resp.content, resp.status_code, headers)
        return response

As such, each app is completely separated from others. All requests starting with /legao go to /legao/ and those starting with /optimization go to /optimization/

Cross-Origin Headers

One problem with this setup is that the headers from /legao/ and /optimization/, etc. will be overwritten by Apache. So I need to set Access-Control-Allow-Origin and Access-Control-Allow-Headers in the Apache config file, which looks like this.

# Reverse Proxy "" to localhost
<VirtualHost *:80>
  Redirect permanent /
<VirtualHost *:443>
  SSLEngine on
  SSLProxyEngine on
  ProxyRequests off
  SSLCertificateFile "/etc/letsencrypt/live/"
  SSLCertificateKeyFile "/etc/letsencrypt/live/"
  SSLCertificateChainFile "/etc/letsencrypt/live/"
  Header set Access-Control-Allow-Origin ""
  Header set Access-Control-Allow-Headers "*"
  ProxyPass / http://localhost:8888/
  ProxyPassReverse / http://localhost:8888/

Then everything is good to go 😎

comments powered by Disqus