Sortix
Sortix Download Manual Development Source Code News Blog More

Sortix NGINX with HTTPS and PHP via Let's Encrypt

Jonas 'Sortie' Termansen on 2025-06-27

In this tutorial, we will install a Sortix server running a website using NGINX with PHP and HTTPS TLS certificates provided by Let's Encrypt. In fact, that's exactly how this blog is hosted on a Sortix server.

It's assumed you already have an IP available for your server, which has a DNS record registered such as example.com.

Installation

First download and install the latest Sortix build.

It's recommended to seed your downloaded .iso with secure randomness from your source machine, which prewarms the entropy for the live environment and installation. Doing so provides extra security that the cryptographic keys are secure:

wget https://pub.sortix.org/sortix/release/nightly/builds/sortix-nightly-x86_64.iso -O sortix.iso &&
wget https://pub.sortix.org/sortix/release/nightly/scripts/tix-iso-add -O tix-iso-add &&
wget https://pub.sortix.org/sortix/release/nightly/scripts/tix-iso-bootconfig -O tix-iso-bootconfig &&
./tix-iso-bootconfig --random-seed bootconfig &&
./tix-iso-add sortix.iso bootconfig
# install sortix here
rm -rf bootconfig sortix.iso # When no longer useful to avoid leaks.

Secure first time ssh

Alternatively, if you are going to ssh into your new installation, this is an excellent time to also seed your .iso with your ssh keys. The tix-iso-liveconfig(8) script makes it easy to add your keys. You can add your public key to the root user's authorized_keys, so the server automatically trusts your client. You can also pregenerate the sshd keys, which lets you generate your known_hosts entries ahead of time, so your client will trust your server's fingerprint on the first connection. If ssh ever asks you whether to trust a fingerprint, you should have used this approach instead.

wget https://pub.sortix.org/sortix/release/nightly/builds/sortix-nightly-x86_64.iso -O sortix.iso &&
wget https://pub.sortix.org/sortix/release/nightly/scripts/tix-iso-add -O tix-iso-add &&
wget https://pub.sortix.org/sortix/release/nightly/scripts/tix-iso-bootconfig -O tix-iso-bootconfig &&
wget https://pub.sortix.org/sortix/release/nightly/scripts/tix-iso-liveconfig -O tix-iso-liveconfig &&
chmod +x tix-iso-add tix-iso-bootconfig tix-iso-liveconfig &&
./tix-iso-liveconfig \
  liveconfig \
  --hostname=example.com \
  --sshd-keygen \
  --sshd-key-known-hosts-file=new_known_hosts \
  --sshd-key-known-hosts-hosts="example.com,192.0.2.1 example.com 192.0.2.1" \
  --root-ssh-authorized-keys="$HOME/.ssh/id_ecdsa.pub" &&
./tix-iso-bootconfig bootconfig --liveconfig=liveconfig --random-seed &&
./tix-iso-add sortix.iso bootconfig &&
cat new_known_hosts >> ~/.ssh/known_hosts
# install sortix here
rm -rf liveconfig bootconfig sortix.iso # When no longer useful to avoid leaks.

You can add the IP address of your new server here if you already know it ahead of time. You can pass --enable-sshd to tix-iso-bootconfig(8) if you wish to automatically start sshd during the live environment. The installer will offer you to copy the ssh keys from the live environment into the new installation.

NGINX with HTTP

The first step is to set up nginx with HTTP only, and prepare the configuration so you can obtain a TLS certicate via the ACME protocol

Login as root to begin the nginx setup.

Create the nginx configuration to begin customizing your installation:

mkdir -p /etc/nginx
mkdir -p /etc/nginx/sites-available
mkdir -p /etc/nginx/sites-enabled

nginx(8) will search for configuration files/directories in the /etc/nginx and /etc/default/nginx directories. The files in /etc belong to the system administrator, but the default fallback files in /etc/default belong to the operating system and must not be edited (and will be reset on upgrades). The operating system ships the default nginx configuration in /etc/default/nginx. You can override the default files in there by creating their counterparts in /etc/nginx.

The -enabled directores are searched for configuration files. It's useful to put the actual files in the -available directories, and symlink them into the enabled directories, so one can easily enable/disable configuration.

Copy and enable the acme site which listens on port 80, answers ACME challenges, and redirects other all traffic to HTTPS:

cp /etc/default/nginx/sites-available/acme /etc/nginx/sites-available/acme
ln -s ../sites-available/acme /etc/nginx/sites-enabled/acme

The acme configuration file looks like this:

server {
    listen 80;

    location /.well-known/acme-challenge/ {
        root /var/www/acme;
        rewrite ^/\.well-known/acme-challenge/(.*)$ /$1 break;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

Test the nginx configuration is valid:

nginx -t

The nginx server can now be enabled with service(8) and tested:

service nginx enable
service nginx status
curl --head http://example.com

You should see a Location: header that redirects to https://example.com. Inspect the /var/log/nginx.log and /var/log/init.log files if nginx isn't running correctly.

CAA record

It's recommended to create a CAA DNS record for your domain that states which certificate authorities are allowed to issue TLS certificates for your domain. This is an important security hardening, which ensures certicates are not issued by certificate authorities that you are not using.

See the Let's Encrypt CAA Guide.

Let's Encrypt via acme-client

The second step is to obtain a TLS certificate. You can do this using any Certificate Authority on the market.

This example uses the Let's Encrypt certificate authority, as it is free to use, easy and automated, and has a good reputation. The acme-client(8) port obtains TLS certificates via the standard ACME protocol supported by certificate authorities such as Let's Encrypt.

Copy the example acme-client.conf(5) configuration:

cp /etc/examples/acme-client.conf /etc/acme-client.conf

Edit the /etc/acme-client.conf configuration appropriately for your domain:

authority letsencrypt {
    api url "https://acme-v02.api.letsencrypt.org/directory"
    account key "/etc/acme/letsencrypt-privkey.pem"
    #contact "mailto:me@example.com"
}

authority letsencrypt-staging {
    api url "https://acme-staging-v02.api.letsencrypt.org/directory"
    account key "/etc/acme/letsencrypt-staging-privkey.pem"
    #contact "mailto:me@example.com"
}

domain example.com {
    alternative names { www.example.com }
    domain key "/etc/letsencrypt/live/example.com/privkey.pem" ecdsa
    domain chain certificate "/etc/letsencrypt/live/example.com/chain.pem"
    domain full chain certificate "/etc/letsencrypt/live/example.com/fullchain.pem"
    # Test with the staging server to avoid aggressive rate-limiting.
    #sign with letsencrypt-staging
    sign with letsencrypt
}

The above example configures acme-client to obtain a TLS certificate with an EDRSA key for the example.com domain with the www.example.com alternate name. Provide your contact information to receive notifications about your certificate and expiration alerts from your provider per their policies. acme-client(8) will by default place ACME challenges in the /var/www/acme directory, which nginx has been configured to serve. The staging server is useful to test your setup is correct without hitting the rate limits, but it will not issue trusted certificates.

Create the required directories:

mkdir -p /etc/acme
mkdir -p /var/www/acme
mkdir -p /etc/letsencrypt/live/example.com

You can now obtain the TLS certificates for your domain:

acme-client -v example.com

You can now find your fresh certificates in the /etc/letsencrypt/live/example.com directory. If the command failed, you will need to find your misconfiguration and fix it.

NGINX with HTTPS

The third step is to configure the nginx https server.

Copy the https template site and rename it to example.com and enable it:

cp /etc/default/nginx/sites-available/https /etc/nginx/sites-available/example.com
ln -s ../sites-available/example.com /etc/nginx/sites-enabled/example.com

Edit the /etc/nginx/sites-available/example.com configuration file appropriately for your web server per the nginx documentation:

server {
    server_name example.com www.example.com;

    listen 443 ssl;
    http2 on;
    gzip off;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    include ssl-config-mozilla-intermediate;
    #include ssl-config-mozilla-modern;

    charset UTF-8;
    add_header Strict-Transport-Security "max-age=63072000";
    #add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";

    root /var/www/example.com;

    location / {
        index index.html;
    }

    location ~ \.php$ {
        fastcgi_pass   unix:/var/run/php-fpm;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

The above example:

  • Listens on port 443 with TLS and HTTP2.
  • Loads the TLS certificates.
  • Includes Mozilla's intermediate strength cryptography configuration as packaged with the operating system in /etc/default/nginx/ssl-config-mozilla-intermediate. These settings are general purpose and work on a reasonably wide range of devices.
    • You can also include ssl-config-mozilla-modern instead for the modern strong cryptography configuration.
    • Alternatively you can generate your own configuration using Mozilla's ssl-config service with the latest recommendations.
  • Enables Strict-Transport-Security which tells browsers to only connect to your site using HTTPS for the specified period (63072000 seconds is two years).
    • Optionally, you can apply it to all subdomains as well.
    • Optionally, you can also allow browsers to add your domain to their preloaded list of domains that always use HTTPS. If you're ready for the commitment, you can request your domain to be preloaded on hstspreload.org when it's operational.
  • Serves files from the /var/www/example.com directory.
  • Serves .php files using the /var/run/php-fpm FastCGI unix socket server. You should remove this location block if you're not going to use PHP.

Create the /var/www/example.com directory:

mkdir -p /var/www/example.com

nginx must be reloaded or restarted to serve HTTPS traffic using the certificate:

service nginx reload

You should now be able to securely connect to your webserver:

curl https://example.com

You can verify everything is set up correctly by checking your score on Qualys' SSL Test. The intermediate configuration presented here should yield a A+ grade at the time of writing. The modern configuration will only give a A grade the time of writing, since it is somewhat less compatible with common clients.

Certificate Renewal

The fourth step is to automatically renew the certificate before it expires 90 days later. acme-client will renew the certificate when invoked if it expires in 30 days or less.

Create a certificate renewal init(5) daemon by creating /etc/init/local-cert-example.com:

require network optional
require nginx optional
exec sh -c 'while true; do acme-client -v example.com && service nginx reload; if [ -n "$READYFD" ]; then eval "echo >&$READYFD"; unset READYFD; fi; sleep 24h; done'

This daemon will renew the certificate on boot if needed, reload nginx if successful, and repeat every 24 hours. Once the certificate has been renewed, the daemon will become ready, allowing other deamons to depend on valid certificates being in use.

Enable your new local-cert-example.com daemon:

service local-cert-example.com enable

You can find the renewal logs in /var/log/local-cert-example.com.log.

PHP

Finally PHP can be enabled by simply enabling the php-fpm(8) daemon:

service php-fpm enable

nginx was configured above to talk to the /var/run/php-fpm FastCGI unix socket server, which php-fpm listens on by default.

Depending on your setup, you may wish to set up permissions and ownership of some of the website files to the _php_fpm user and group depending on your setup. nginx will run as the _nginx user and will also need access to serve the files.

chown -R _php_fpm:_php_fpm /var/www/example.com

Create a PHP test page /var/www/example.com/index.php:

<?php phpinfo(); ?>

Test whether PHP is working:

curl https://example.com/index.php

If successful, you can now start installing PHP web applications of your choice.

Follow up blog posts will walk you through installing mediawiki, phpbb, and more on Sortix. Mediawiki has been successfully installed and works, although the email registration has not been turned on and tested yet. phbBB can be installed and works, although the the user registration crashes because a DNS lookup function is missing in the standard library. Stay tuned as we bring all of these web applications online on Sortix in the near future.

Congratulations on your new Sortix web server!

Copyright 2011-2025 Jonas 'Sortie' Termansen and contributors.
Sortix's source code is free software under the ISC license.
#sortix on irc.sortix.org
@sortix_org