Setting up NGINX

assuming NGINX is installed or built from scratch already

Introduction

It is usually assumed that everybody wants HTTPS, right? This is untrue esp. if your website is read-only or providing content without any authentication scheme. You can have a strong SSL setup if you like, while still allowing plain HTTP at the same time. For those out there who are stuck in the past, and sometimes don’t even have SSL support on their ante- diluvian systems, why not serve anything that’s already public without crypto?

Of course HSTS comes in da game and any modern browser might forget about HTTP. However it is NOT a problem to serve HSTS while talking HTTP, as it does not make any difference to e.g. Lynx which did NOT implement HSTS as of Jan 2021.

Setup

Setup your HTTP/HTTPS daemon

cd /var/www/html/
rm -f index.html 50x.html

grep --color=always aes /proc/cpuinfo
grep --color=always avx /proc/cpuinfo

openssl dhparam 2048 > /etc/ssl/dhparam.pem
cat /etc/ssl/dhparam.pem
#-rand /dev/urandom

mv -i /etc/nginx/nginx.conf /etc/nginx/nginx.conf.dist
grep -vE '^[[:space:]]*(#|$)' /etc/nginx/nginx.conf.dist > /etc/nginx/nginx.conf.clean
vi /etc/nginx/nginx.conf #new file

user www www;
worker_processes auto;

events {
        worker_connections 1024;
}

http {
        include mime.types;
    default_type text/html;
    #default_type application/octet-stream;
        sendfile on;
        keepalive_timeout 65;
    server_tokens off;

    # adding default_server here
        # http2 pops-up a download window on FF
    # curl: (1) Received HTTP/0.9 when not allowed
        server {
                listen 80 default_server;
                listen [::]:80 default_server;
                server_name _;

                location / {
                        return 301 https://nethence.com/;
                }

            # deal with http-01 challenges (no http2 there)
        location ^~ /.well-known/acme-challenge/ {
            default_type "text/plain";
            #trailing slash matters
            alias /var/www/dehydrated/;
        }

        #ssi on;
        #autoindex on;
        #autoindex_exact_size off;
        location = /robots.txt          { access_log off; log_not_found off; }
        location = /favicon.ico         { access_log off; log_not_found off; }
        location ~ /apple-touch-icon    { access_log off; log_not_found off; }
        }

            # without includeSubDomains - here 180 days - otherwise 31536000 seconds
            add_header Strict-Transport-Security "max-age=15552000" always;

    # https://pub.nethence.com/security/ciphers
    ssl_protocols TLSv1.3 TLSv1.2;
    ssl_prefer_server_ciphers off;
    ssl_ciphers ECDHE:DHE:kGOST:AEAD:!aNULL:!eNULL:!RC4:!MD5:!3DES:!AES256-GCM-SHA384:!ECDHE-RSA-AES256-SHA:!ECDHE-ECDSA-AES256-SHA;
    ssl_dhparam /etc/ssl/dhparam.pem;
    # no restriction on ssl_ecdh_curve
    ssl_session_cache shared:SSL:40m;
    ssl_session_timeout 4h;
    ssl_session_tickets on;

    add_header X-Frame-Options DENY;

        include conf.d/*.conf;
}

…and eventually tune your ciphers more.

Note HSTS of 180 days is enough, no need for 365 days.

Moar options

Unconditional redirect that keeps the requested URI

                location / {
            # taking over some FQDN at once
                        return 301 https://pub.nethence.com$request_uri;
                }

Serve static pages

                location / {
            root /var/www/html;
            index index.html index.htm;
                    try_files $uri $uri/ =404;
            }

Reverse-proxy something

                location / {
                    proxy_pass http://x.x.x.x/;
            }

Virtual host example

Put your certs in place and define SNIs.

Here’s the joined HTTP+HTTPS vhost sample when there is no authentication.

vi conf.d/HOST.conf

server {
        listen 80;
        listen [::]:80;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
        server_name HOST.nethence.com;

    # http-01
        ssl_certificate     /etc/dehydrated/certs/HOST.nethence.com/fullchain.pem;
        ssl_certificate_key /etc/dehydrated/certs/HOST.nethence.com/privkey.pem;

    # dns-01
    #ssl_certificate     /etc/dehydrated/certs/nethence_com/fullchain.pem;
    #ssl_certificate_key /etc/dehydrated/certs/nethence_com/privkey.pem;

        location / {
                root /var/www/html;
                index index index.html index.htm;
                try_files $uri $uri/ =404;
        }

            # deal with http-01 challenges (no http2 there)
            location ^~ /.well-known/acme-challenge/ {
                    default_type "text/plain";
                    #trailing slash matters
                    alias /var/www/dehydrated/;
            }

        #ssi on;
        autoindex on;
        autoindex_exact_size off;
    location = /robots.txt          { access_log off; log_not_found off; }
    location = /favicon.ico         { access_log off; log_not_found off; }
    location ~ /apple-touch-icon    { access_log off; log_not_found off; }
}

More options

    location ~ /\.                 { access_log off; log_not_found off; deny all; }
    location ~ ~$                  { access_log off; log_not_found off; deny all; }

And of course, if you really need a redirect instead of serving HTTP, here’s the sample when there is truly and mandatory need for SSL.

server {
    listen 80;
    listen [::]:80;
    server_name moodle.nethence.com;

    # redirect to HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name moodle.nethence.com;

    # dns-01
    ssl_certificate     /etc/dehydrated/certs/nethence_com/fullchain.pem;
    ssl_certificate_key /etc/dehydrated/certs/nethence_com/privkey.pem;

    location / {
        proxy_pass http://x.x.x.x/;
    }
}

Also when you’re migrating a whole web site to another sub-domain, you can eventually pack it within the default_server stanza as such

            if ($host = doc.nethence.com) {
                    return 301 https://pub.nethence.com$request_uri;
            }

            return 301 https://pub.nethence.com/;

Ready to go

check configuration

nginx -t

enable at boot time

vi /etc/rc.d/rc.local

echo -n nginx...
/usr/local/sbin/nginx && echo done || echo FAIL

on Ubuntu 16+, make sure the System D service for rc-local is enabled and don’t forget to make the script executable

systemctl status rc-local.service
chmod +x /etc/rc.local

status

ps auxfww | grep nginx | grep -v grep
cat /var/log/nginx.pid
cat /var/lock/nginx.lock
cat /var/db/nginx/nginx.lock

reload

nginx -s reload

shutdown gracefully

nginx -s quit

exit brutally

nginx -s stop

Acceptance

remotely

redirect to HTTS or serve HTTP?

curl -i http://nethence.com/
curl -i http://HOST.nethence.com/

HTTPS just works

curl -i https://nethence.com/
curl -i https://HOST.nethence.com/

what happens if you’re talking SSL to a non-existing vhost?

curl -i https://ipsec.nethence.com/

check 301 on 404

curl -i https://nethence.com/lala
curl -i https://HOST.nethence.com/lala

HTTP additions vs fancy specific setups

prepare headers and footers

cd /var/www/html/
mkdir -p css/
echo '<p>header' > css/header.html
echo '<p>footer' > css/footer.html
touch check-file1
mkdir check-folder/
touch check-folder/check-file2

then enable fancy as http or server context

    location / {
        ...
        fancyindex on;
        fancyindex_exact_size off;
        #fancyindex_css_href /css/kult.custom.css;
        fancyindex_header /header.html;
        fancyindex_footer /footer.html;
        fancyindex_ignore favicon.ico robots.txt css/header.html css/footer.html css;
        #fancyindex_localtime off;
    }
    autoindex on;

or just DIY

                        #add_before_body /css/header.html;
            sub_filter '<head><title>Index of $uri</title></head>' '<head><title>TITLE-HERE - $uri</title></head>';
            sub_filter '<h1>Index of $uri</h1>' '<h1 style="font-family:Courier;font-style:italic;text-transform:uppercase;">TITLE-HERE - $uri</h1>';
                        sub_filter_once on;

                        #add_after_body  /css/footer.html;
            sub_filter '</body>' '<div>SOME FOOTER HERE</div></body>';

Note: not adding a footer as some /body and /html would remain – the filter would not differenciate those I added and those from the directory listing.

Resources

Pitfalls and Common Mistakes https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/

Getting Started https://www.nginx.com/resources/wiki/start/index.html

http://wiki.nginx.org/QuickStart

http://wiki.nginx.org/Configuration

Default NGINX Configuration https://gist.github.com/ArunMichaelDsouza/471395af64fb52943bf1

NGINXConfig https://nginxconfig.io/

Debugging Nginx Errors https://blog.martinfjordvald.com/debugging-nginx-errors/ https://blog.martinfjordvald.com/optimizing-nginx-for-high-traffic-loads/ https://blog.martinfjordvald.com/?s=nginx

setup

Syntax: server { … } https://nginx.org/en/docs/http/ngx_http_core_module.html#server

How to do an Nginx redirect https://www.bjornjohansen.no/nginx-redirect

How do I force redirect all 404’s (or every page, whether invalid or not) to the homepage? https://stackoverflow.com/questions/19487365/how-do-i-force-redirect-all-404s-or-every-page-whether-invalid-or-not-to-the

Module ngx_http_sub_module http://nginx.org/en/docs/http/ngx_http_sub_module.html

http additions vs fancy

Module ngx_http_sub_module https://nginx.org/en/docs/http/ngx_http_sub_module.html

Module ngx_http_addition_module https://nginx.org/en/docs/http/ngx_http_addition_module.html

Beautiful listing of files and directories in nginx https://weekly-geekly.github.io/articles/353478/index.html

Directory Theme https://github.com/jessfraz/directory-theme/blob/master/README.md

dual cert

Integration Guide https://letsencrypt.org/docs/integration-guide/

Hybrid RSA and ECDSA certificates with NginX https://scotthelme.co.uk/hybrid-rsa-and-ecdsa-certificates-with-nginx/

operations

Controlling NGINX Processes at Runtime https://docs.nginx.com/nginx/admin-guide/basic-functionality/runtime-control/

nginx -s stop and -s quit what is the difference? https://serverfault.com/questions/271810/nginx-s-stop-and-s-quit-what-is-the-difference

hsts

HTTP Strict Transport Security (HSTS) and NGINX https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/

The Importance of a Proper HTTP Strict Transport Security Implementation on Your Web Server https://blog.qualys.com/vulnerabilities-research/2016/03/28/the-importance-of-a-proper-http-strict-transport-security-implementation-on-your-web-server

selective redirect within catch-all default_server

Nginx: Matching server host name in location directive https://serverfault.com/questions/286828/nginx-matching-server-host-name-in-location-directive

If is Evil… when used in location context https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/