This series of articles loosely documents design choices and implementation issues I met during the making of the tln website. I learnt a few lessons along my improvised 9-week schedule, and I thought I could share them with fellow first-timers. I guess some technical points might also be of interest to more experimented developers.
Hosting the website
Now that the website is complete and runs on most browsers from most devices, it's time to make it public. Actually this doesn't really qualify as web development, it's more about building a web server infrastructure. Because once again I wanted to retain control where I could, instead of getting a virtual server from a company like Gandi (which I trust and like otherwise), I went with a friend's offer to host my website. Thus the tasks could be broken down as follows:
- register a domain name from Gandi, and associate it with my friend's IP address;
- define the resources to be allocated by the ESXi hypervisor to my guest virtual machine;
- install an nginx server for serving Django-managed content with production features;
- configure my friend's pfSense firewall for allowing proper web and mail traffic on his LAN.
Serving Django-managed content with nginx
There would be much to tell on each of the previous subjects, but I'll focus on the nginx part. The Django development server is fine for testing, but it's not meant for serving static content, it cannot answer parallel requests from distinct users, it won't handle HTTPS for securing communications, etc. That's why a production server like nginx or Apache should stand in front of Django.
The interface between Django and nginx can be covered by softwares such as uWSGI and Gunicorn. The uWSGI documentation comes with an excellent tutorial which covers basic deployment. One option I'd strongly recommend to add to the uwsgi command would be --touch-reload, which provides a quick way to refresh the uWSGI service whenever the Django project was modified.
The tutorial includes a sample nginx configuration file, with static content to be served independently from Django-generated content. Several static files are used across different pages of the website, and most of them are left unchanged between visits. Downloading identical files multiple times is a waste of bandwidth and it slows down page retrieval, so it should be avoided as much as possible. One way to do this comes from leveraging client browser caching through the right HTTP codes; this can be achieved in the nginx configuration file with the expires command.
Enabling HTTPS also involves the nginx configuration file. Because plain HTTP does not protect communications, anyone on the network route between a client and the server could easily eavesdrop passwords —that's why any website asking users to sign in should definitely be served over HTTPS. Even though I'm the only person with an account on this website, it still applies.
At any rate, in 2018 it is poor practice to serve web content without HTTPS. Anyone can get a free, verified certificate from Let's Encrypt. Certificate renewal can be automated easily, at least on Debian 9 running nginx. Even the nginx configuration file can be updated seamlessly for using HTTPS (although I'd recommend removing some ciphersuites from the Certbot-generated configuration file, but this is too broad a topic to be addressed here).
On March 12th, I made a public announcement about launching my website, on Facebook and Twitter. Except I kind of forgot about setting the way links should be shared social media, so both Facebook and Twitter displayed a poorly cropped logo along with random text extracted from the homepage... Fixing this required adding Open Graph and Twitter tags to the HTML page metadata. Additionally, because both social media cache this metadata, I had to refresh the caches from here and here in order to display the proper logo and preview text.
On several occasions over the following days, the server froze for no apparent reason, interrupting the service completely and requiring a manual reboot from the hypervisor web interface. This happened at infrequent intervals, and it seemed the risk of crashing was worsened by user traffic. It looked like a kernel panic and no logs could capture the event, so it was really a downer. Eventually I was able to track it down to a nasty, failure-prone network interface interaction between Debian 9 and ESXi 6.5. Although debugging involved finding one barely suspicious line inside the kernel logs, fixing this only required updating ESXi. Yay!
Regarding backup, obviously I'd feel better knowing the whole website could be easily restored in case of heavy server corruption. Fortunately, I was offered some disk space on a distant, different server. From this, I just had to add a cronjob for rsync-ing the website contents every night.
Since launching the website a month ago, I've been fixing details and making small improvements. The one thing I'm really bothered that I couldn't find the energy to do just yet, is improving accessibility. While I paid attention to following proper HTML syntax, I know that the website remains far from being keyboard-friendly nor screenreader-friendly. I already took some time to learn about the features I'd have to add for this; hopefully I can implement it soon.
A less important feature I'd like to provide is a search bar for the Critique section. Back when I built it, I had to settle for so-so workarounds: either use Ctrl-F on big pages which load all œuvre objects at once, or try writing a slugified œuvre title to the URL. Now that the Haystack application supports Django v2.0, I should investigate whether it's compatible with a MongoDB database.