Site Setup



It’s not difficult to install and configure NGINX and certbot, but I want to remember a handful of things that I prefer. (My current server runs Debian 11, but much of this would be the same on other distros.)

To begin with, here are the basic steps.

sudo apt install nginx python3-certbot-nginx
# See below for discussion of the site configuration.
sudo vim /etc/nginx/sites-available/site-name
sudo ln -s /etc/nginx/sites-available/site-name \
sudo nginx -t
sudo systemctl restart nginx
sudo ufw allow 'Nginx Full'
# Run sudo ufw status to check ufs.
# Test a page on the site before the next step.
sudo certbot --nginx -d domain -d www.domain
# Test renewal with sudo certbot renew --dry-run.

NGINX Site Configuration

For my site, I wanted three things.

  1. All traffic to www.domain-name should redirect to domain-name.
  2. All http requests should redirect to https.
  3. If I write some-cool-post.html, the URL is some-cool-post. Visitors don’t need to see or type the suffix.

If you search online, you find a lot of different advice about how to do these things. Here’s what I settled on.

# In /etc/nginx/sites-available/sitename
server {
    server_name www.sitename sitename;
    listen 80;
    listen [::]:80;
    return 301 https://sitename$request_uri;

server {
    listen 443 ssl;
    listen [::]:443 ssl ipv6only=on;
    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;
    include /path/to/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /path/to/letsencrypt/ssl-dhparams.pem;
    server_name www.sitename;
    return 301 https://sitename$request_uri;

server {
    listen 443 ssl;
    # Do NOT add ipv6only=on in the next line.
    listen [::]:443 ssl;
    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;
    include /path/to/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /path/to/letsencrypt/ssl-dhparams.pem;
    server_name sitename;

    charset utf-8;
    root /path/to/site/html;
    error_page 404 /404;
    try_files $uri $uri.html $uri/ =404;

The first block redirects all http traffic to https. The second block redrects all https://www.sitename traffic to https://sitename, and the final block actually serves pages. The try_files line enables pretty URLs since it tries both $uri and $uri.html.

By default, certbot will add listen [::]:443 ssl ipv6only=on; to the second and third server blocks. However, some versions of NGINX wet the bed if you set ipv6only=on twice. I didn’t investigate very deeply, but everything works well if I remove that bit from the third block.

Webroot Permissions

Again, you find different advice about what user should own the webroot and what permissions you should set on that directory. I doubt that there is a single best answer, but here’s what I decided.

# On the server
sudo chown $USER:www-data /path/to/webroot
sudo chmod 750 /path/to/webroot
sudo chmod g+s /path/to/webroot

By setting the group id (g+s) of the webroot, I make sure that all new files and folders inherit their group from the webroot itself rather than from the person who shares the files. This means that I don’t have to make $USER a member of the www-data group, and I don’t have to share the files as a privileged user.

# On a remote machine
find ./build -type f -exec chmod 640 {} \;
find ./build -type d -exec chmod 750 {} \;
rsync -rzP --delete ./build/ user@host:/path/to/webroot

On the remote side, I set restrictive permissions, and I make sure not to overwrite the webroot itself when I sync. To simplify things, the three commands on the remote side run as a single task from a Makefile. make sync updates the website.

Warning and References

These are notes for myself, and I’m an amateur. Don’t rely on anything here without reading more on the subjects. Here are some of the things I read while deciding how to do all this.