My NGINX configuration

Posted on 14 May 2025 by Mino 3 min

My NGINX configuration

After deploying my own certificate authority (previous post) I updated all my NGINX configurations to be almost exactly the same, with a few exceptions. Detailed config along with explanation of required changes please find in the post.

To use this template just replace the following lines and enjoy (hopefully) secure NGINX configuration:

  • <SERVERNAME>, f.e. test.com
  • <HOSTNAME>, f.e. test
  • <PROXYPASS>, f.e. 127.0.0.1:3000

In order to create dhparam file, you can run following command to make your secure connection even more secure ;).

openssl dhparam -out static-dhparam.pem 4096
map $http_upgrade $connection_upgrade {
    default         upgrade;
    ''              '';
}

server {
    listen          80;
    server_name     <SERVERNAME>;

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

server {
    listen          443 ssl;
    listen          443 quic reuseport;
    server_name     <SERVERNAME>;

    http2           on;
    http3           on;
    quic_gso        on;
    quic_retry      on;
    #quic_bpf        on;

    add_header      X-Frame-Options DENY;
    add_header      Alt-Svc 'h3=":443"; ma=86400';
    add_header      Strict-Transport-Security max-age=63072000;
    add_header      X-XSS-Protection "0";
    add_header      X-Content-Type-Options "nosniff";
    add_header      Permissions-Policy "accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), camera=(), clipboard-read=(), display-capture=(), document-domain=(), encrypted-media=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), interest-cohort=(), keyboard-map=(), local-fonts=(), magnetometer=(), microphone=(), payment=(), publickey-credentials-get=(), serial=(), sync-xhr=(), usb=(), xr-spatial-tracking=()" always;
    add_header      Content-Security-Policy "default-src https: data: blob: ; img-src 'self' https://* ; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.gstatic.com https://www.youtube.com blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'";

    server_tokens               off;
    proxy_buffering             off;
    proxy_request_buffering     off;

    client_max_body_size        0;
    client_body_buffer_size     512k;
    http3_stream_buffer_size    512k;
    proxy_read_timeout          86400s;

    ssl_dhparam                 /etc/nginx/ssl/<HOSTNAME>-dhparam.pem;
    ssl_certificate             /etc/nginx/ssl/<HOSTNAME>-fullchain.pem;
    ssl_certificate_key         /etc/nginx/ssl/<HOSTNAME>-privkey.pem;
    ssl_early_data              on;
    ssl_session_timeout         1d;
    ssl_session_cache           shared:SSL:10m;
    ssl_protocols               TLSv1.2 TLSv1.3;
    ssl_ecdh_curve              x25519:x448:secp521r1:secp384r1:secp256r1;
    ssl_prefer_server_ciphers   on;
    ssl_conf_command            Options PrioritizeChaCha;
    ssl_ciphers                 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256;

    location / {
        proxy_pass              http://<PROXYPASS>;

        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-Proto $scheme;
        proxy_set_header        X-Forwarded-Scheme $scheme;
        proxy_set_header        Early-Data $ssl_early_data;
        proxy_set_header        X-Forwarded-Port $server_port;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_http_version      1.1;
        proxy_set_header        Upgrade $http_upgrade;
        proxy_set_header        Connection $connection_upgrade;
    }
}

Variants

Static server

I have a static webserver, which only purpose is to give me icons for my vaultwarden server and to store public key for certificate authority in case new device is added to my collection. For this I only modify the http server and add autoindex on; to https/location.

...
server {
    listen          80;
    server_name     static.test.com;
    root        /var/www/static;
    location / {
        autoindex on;
    }
}
...
    root            /var/www/static;
    location / {
        autoindex on;
    }
...

Useful script

Script for editing multiple configurations at once. There is one small issue with the script below, such as I have to manually edit / add port if the service behind the reverse proxy uses nonstandard port.

#!/bin/bash

sites=("static" "vaultwarden")

mkdir -p ./updated

for site in "${sites[@]}"
do
    rm ./updated/$site.conf &>/dev/null
    cp ./template.conf ./updated/$site.conf

    sed -i "s/<SERVERNAME>/$site.test.com/g" ./updated/$site.conf
    sed -i "s/<HOSTNAME>/$site/g" ./updated/$site.conf
    sed -i "s/<PROXYPASS>/$site/g" ./updated/$site.conf
done

This post was written without the help of AI.