Zum Inhalt

Nginx

Inhalt

  • Nginx 1.28.2
  • Webserver für statische Inhalte, Redirects und TLS
  • PHP-FPM-Anbindung per Unix-Socket
  • HTTP/2
  • optional HTTP/3
  • optional Brotli

Einleitung

Dieses HowTo beschreibt die Installation und Konfiguration von Nginx auf FreeBSD 15+ als Webserver für statische Inhalte, Redirects, TLS und PHP-FPM über Unix-Socket.

Dieses HowTo verwendet bewusst den Port www/nginx. Der aktuelle Portstand ist 1.28.2. Der Port bringt bereits die üblichen Beispielkonfigurationen wie nginx.conf-dist, fastcgi_params-dist und mime.types-dist mit. In den Default-Optionen sind HTTPV2 und HTTPV3 enthalten; Brotli gehört dagegen nicht zu den Standardoptionen und muss bewusst zusätzlich gewählt werden. (freshports.org)

Nginx liest seine Hauptkonfiguration aus nginx.conf. Änderungen an der Konfiguration werden erst nach Reload oder Restart wirksam. Für die PHP-Anbindung verwendet Nginx fastcgi_pass; dabei kann statt TCP ausdrücklich auch ein Unix-Domain-Socket verwendet werden. (Nginx)


Voraussetzungen

Zu den Voraussetzungen für dieses HowTo siehe bitte: Hosting System

Zusätzlich gilt für dieses HowTo:

  • Nginx ist der öffentliche Webserver auf Port 80 und 443.
  • PHP-FPM ist bereits installiert.
  • PHP-FPM lauscht tatsächlich auf dem konfigurierten Unix-Socket, zum Beispiel /var/run/fpm_www.sock.
  • Für produktiven TLS-Betrieb liegen die Zertifikate bereits unter /usr/local/etc/letsencrypt/live/....
  • Falls HTTP/3 später aktiviert werden soll, muss zusätzlich QUIC konfiguriert werden. Dafür braucht Nginx einen eigenen listen ... quic-Listener; QUIC verwendet dabei UDP als Transport. (Nginx)

Vorbereitungen

DNS Records

Für dieses HowTo müssen zuvor folgende DNS-Records angelegt werden, sofern sie noch nicht existieren, oder entsprechend geändert werden, sofern sie bereits existieren.

Text Only
example.com.            IN  A       __IPADDR4__
example.com.            IN  AAAA    __IPADDR6__

www.example.com.        IN  A       __IPADDR4__
www.example.com.        IN  AAAA    __IPADDR6__

mail.example.com.       IN  A       __IPADDR4__
mail.example.com.       IN  AAAA    __IPADDR6__

Gruppen / Benutzer / Passwörter

Für dieses HowTo sind keine zusätzlichen Systemgruppen, Systembenutzer oder Passwörter erforderlich.

Verzeichnisse / Dateien

Für dieses HowTo müssen zuvor folgende Verzeichnisse angelegt werden, sofern sie noch nicht existieren, oder entsprechend geändert werden, sofern sie bereits existieren.

Bash
install -d -m 1777 -o www -g www /usr/local/www/cache
install -d -m 1777 -o www -g www /usr/local/www/tmp

mkdir -p /usr/local/www/vhosts/0default0/conf
mkdir -p /usr/local/www/vhosts/0default0/cron
mkdir -p /usr/local/www/vhosts/0default0/logs
mkdir -p /usr/local/www/vhosts/0default0/data/.well-known

mkdir -p /usr/local/www/vhosts/mail.example.com/conf
mkdir -p /usr/local/www/vhosts/mail.example.com/cron
mkdir -p /usr/local/www/vhosts/mail.example.com/logs
mkdir -p /usr/local/www/vhosts/mail.example.com/data/.well-known

mkdir -p /usr/local/www/vhosts/www.example.com/conf
mkdir -p /usr/local/www/vhosts/www.example.com/cron
mkdir -p /usr/local/www/vhosts/www.example.com/logs
mkdir -p /usr/local/www/vhosts/www.example.com/data/.well-known

mkdir -p /usr/local/etc/newsyslog.conf.d

Für diese HowTos müssen zuvor folgende Dateien angelegt werden, sofern sie noch nicht existieren, oder entsprechend geändert werden, sofern sie bereits existieren.

Bash


Installation

Wir installieren www/nginx und dessen Abhängigkeiten.

Bash
mkdir -p /var/db/ports/devel_google-perftools
cat <<'EOF' > /var/db/ports/devel_google-perftools/options
_OPTIONS_READ=google-perftools-2.17.2
_FILE_COMPLETE_OPTIONS_LIST=DOCS PROFILER PAGE8K PAGE32K PAGE64K ALIGN8 ALIGN16
OPTIONS_FILE_UNSET+=DOCS
OPTIONS_FILE_SET+=PROFILER
OPTIONS_FILE_UNSET+=PAGE8K
OPTIONS_FILE_SET+=PAGE32K
OPTIONS_FILE_UNSET+=PAGE64K
OPTIONS_FILE_UNSET+=ALIGN8
OPTIONS_FILE_SET+=ALIGN16

EOF

mkdir -p /var/db/ports/www_nginx
cat <<'EOF' > /var/db/ports/www_nginx/options
_OPTIONS_READ=nginx-1.28.2
_FILE_COMPLETE_OPTIONS_LIST=DEBUG DEBUGLOG DSO FILE_AIO IPV6 NJS NJS_XML OTEL THREADS WWW GSSAPI_HEIMDAL GSSAPI_MIT GOOGLE_PERFTOOLS HTTP HTTP_ADDITION HTTP_AUTH_REQ  HTTP_CACHE HTTP_DAV HTTP_DEGRADATION HTTP_FLV HTTP_GUNZIP_FILTER  HTTP_GZIP_STATIC HTTP_IMAGE_FILTER HTTP_MP4 HTTP_PERL  HTTP_RANDOM_INDEX HTTP_REALIP HTTP_SECURE_LINK HTTP_SLICE HTTP_SSL  HTTP_STATUS HTTP_SUB HTTP_XSLT HTTPV2 HTTPV3 HTTPV3_BORING HTTPV3_LSSL  HTTPV3_QTLS MAIL MAIL_IMAP MAIL_POP3 MAIL_SMTP MAIL_SSL STREAM STREAM_REALIP STREAM_SSL  STREAM_SSL_PREREAD AJP AWS_AUTH BROTLI CACHE_PURGE  DEVEL_KIT ARRAYVAR DRIZZLE DYNAMIC_UPSTREAM ECHO ENCRYPTSESSION  FIPS_CHECK FORMINPUT GRIDFS HEADERS_MORE HTTP_ACCEPT_LANGUAGE HTTP_AUTH_DIGEST  HTTP_AUTH_KRB5 HTTP_AUTH_LDAP HTTP_AUTH_PAM HTTP_DAV_EXT HTTP_EVAL  HTTP_FANCYINDEX HTTP_FOOTER HTTP_GEOIP2 HTTP_IP2LOCATION HTTP_IP2PROXY  HTTP_JSON_STATUS HTTP_MOGILEFS HTTP_NOTICE HTTP_PUSH  HTTP_PUSH_STREAM HTTP_REDIS HTTP_SLICE_AHEAD HTTP_SUBS_FILTER HTTP_TARANTOOL  HTTP_UPLOAD HTTP_UPLOAD_PROGRESS HTTP_UPSTREAM_CHECK HTTP_UPSTREAM_FAIR  HTTP_UPSTREAM_STICKY HTTP_VIDEO_THUMBEXTRACTOR HTTP_ZIP ICONV LET LINK LUA LUASTREAM  MEMC MODSECURITY3 NAXSI PASSENGER POSTGRES RDS_CSV RDS_JSON  REDIS2 RTMP SET_MISC SFLOW SHIBBOLETH SLOWFS_CACHE SRCACHE STS  VOD VTS XSS WEBSOCKIFY ZSTD
OPTIONS_FILE_UNSET+=DEBUG
OPTIONS_FILE_UNSET+=DEBUGLOG
OPTIONS_FILE_SET+=DSO
OPTIONS_FILE_SET+=FILE_AIO
OPTIONS_FILE_SET+=IPV6
OPTIONS_FILE_UNSET+=NJS
OPTIONS_FILE_UNSET+=NJS_XML
OPTIONS_FILE_UNSET+=OTEL
OPTIONS_FILE_SET+=THREADS
OPTIONS_FILE_SET+=WWW
OPTIONS_FILE_UNSET+=GSSAPI_HEIMDAL
OPTIONS_FILE_UNSET+=GSSAPI_MIT
OPTIONS_FILE_SET+=GOOGLE_PERFTOOLS
OPTIONS_FILE_SET+=HTTP
OPTIONS_FILE_SET+=HTTP_ADDITION
OPTIONS_FILE_SET+=HTTP_AUTH_REQ
OPTIONS_FILE_SET+=HTTP_CACHE
OPTIONS_FILE_SET+=HTTP_DAV
OPTIONS_FILE_UNSET+=HTTP_DEGRADATION
OPTIONS_FILE_SET+=HTTP_FLV
OPTIONS_FILE_SET+=HTTP_GUNZIP_FILTER
OPTIONS_FILE_SET+=HTTP_GZIP_STATIC
OPTIONS_FILE_UNSET+=HTTP_IMAGE_FILTER
OPTIONS_FILE_SET+=HTTP_MP4
OPTIONS_FILE_UNSET+=HTTP_PERL
OPTIONS_FILE_SET+=HTTP_RANDOM_INDEX
OPTIONS_FILE_SET+=HTTP_REALIP
OPTIONS_FILE_SET+=HTTP_SECURE_LINK
OPTIONS_FILE_SET+=HTTP_SLICE
OPTIONS_FILE_SET+=HTTP_SSL
OPTIONS_FILE_SET+=HTTP_STATUS
OPTIONS_FILE_SET+=HTTP_SUB
OPTIONS_FILE_SET+=HTTP_XSLT
OPTIONS_FILE_SET+=HTTPV2
OPTIONS_FILE_SET+=HTTPV3
OPTIONS_FILE_UNSET+=HTTPV3_BORING
OPTIONS_FILE_UNSET+=HTTPV3_LSSL
OPTIONS_FILE_UNSET+=HTTPV3_QTLS
OPTIONS_FILE_UNSET+=MAIL
OPTIONS_FILE_UNSET+=MAIL_IMAP
OPTIONS_FILE_UNSET+=MAIL_POP3
OPTIONS_FILE_UNSET+=MAIL_SMTP
OPTIONS_FILE_UNSET+=MAIL_SSL
OPTIONS_FILE_SET+=STREAM
OPTIONS_FILE_SET+=STREAM_REALIP
OPTIONS_FILE_SET+=STREAM_SSL
OPTIONS_FILE_SET+=STREAM_SSL_PREREAD
OPTIONS_FILE_UNSET+=AJP
OPTIONS_FILE_UNSET+=AWS_AUTH
OPTIONS_FILE_SET+=BROTLI
OPTIONS_FILE_UNSET+=CACHE_PURGE
OPTIONS_FILE_SET+=DEVEL_KIT
OPTIONS_FILE_UNSET+=ARRAYVAR
OPTIONS_FILE_UNSET+=DRIZZLE
OPTIONS_FILE_UNSET+=DYNAMIC_UPSTREAM
OPTIONS_FILE_UNSET+=ECHO
OPTIONS_FILE_UNSET+=ENCRYPTSESSION
OPTIONS_FILE_UNSET+=FIPS_CHECK
OPTIONS_FILE_UNSET+=FORMINPUT
OPTIONS_FILE_UNSET+=GRIDFS
OPTIONS_FILE_UNSET+=HEADERS_MORE
OPTIONS_FILE_UNSET+=HTTP_ACCEPT_LANGUAGE
OPTIONS_FILE_UNSET+=HTTP_AUTH_DIGEST
OPTIONS_FILE_UNSET+=HTTP_AUTH_KRB5
OPTIONS_FILE_UNSET+=HTTP_AUTH_LDAP
OPTIONS_FILE_UNSET+=HTTP_AUTH_PAM
OPTIONS_FILE_UNSET+=HTTP_DAV_EXT
OPTIONS_FILE_UNSET+=HTTP_EVAL
OPTIONS_FILE_UNSET+=HTTP_FANCYINDEX
OPTIONS_FILE_UNSET+=HTTP_FOOTER
OPTIONS_FILE_UNSET+=HTTP_GEOIP2
OPTIONS_FILE_UNSET+=HTTP_IP2LOCATION
OPTIONS_FILE_UNSET+=HTTP_IP2PROXY
OPTIONS_FILE_UNSET+=HTTP_JSON_STATUS
OPTIONS_FILE_UNSET+=HTTP_MOGILEFS
OPTIONS_FILE_UNSET+=HTTP_NOTICE
OPTIONS_FILE_UNSET+=HTTP_PUSH
OPTIONS_FILE_UNSET+=HTTP_PUSH_STREAM
OPTIONS_FILE_UNSET+=HTTP_REDIS
OPTIONS_FILE_UNSET+=HTTP_SLICE_AHEAD
OPTIONS_FILE_UNSET+=HTTP_SUBS_FILTER
OPTIONS_FILE_UNSET+=HTTP_TARANTOOL
OPTIONS_FILE_UNSET+=HTTP_UPLOAD
OPTIONS_FILE_UNSET+=HTTP_UPLOAD_PROGRESS
OPTIONS_FILE_UNSET+=HTTP_UPSTREAM_CHECK
OPTIONS_FILE_UNSET+=HTTP_UPSTREAM_FAIR
OPTIONS_FILE_UNSET+=HTTP_UPSTREAM_STICKY
OPTIONS_FILE_UNSET+=HTTP_VIDEO_THUMBEXTRACTOR
OPTIONS_FILE_UNSET+=HTTP_ZIP
OPTIONS_FILE_SET+=ICONV
OPTIONS_FILE_UNSET+=LET
OPTIONS_FILE_UNSET+=LINK
OPTIONS_FILE_UNSET+=LUA
OPTIONS_FILE_UNSET+=LUASTREAM
OPTIONS_FILE_UNSET+=MEMC
OPTIONS_FILE_UNSET+=MODSECURITY3
OPTIONS_FILE_UNSET+=NAXSI
OPTIONS_FILE_UNSET+=PASSENGER
OPTIONS_FILE_UNSET+=POSTGRES
OPTIONS_FILE_UNSET+=RDS_CSV
OPTIONS_FILE_UNSET+=RDS_JSON
OPTIONS_FILE_UNSET+=REDIS2
OPTIONS_FILE_UNSET+=RTMP
OPTIONS_FILE_UNSET+=SET_MISC
OPTIONS_FILE_UNSET+=SFLOW
OPTIONS_FILE_UNSET+=SHIBBOLETH
OPTIONS_FILE_UNSET+=SLOWFS_CACHE
OPTIONS_FILE_UNSET+=SRCACHE
OPTIONS_FILE_UNSET+=STS
OPTIONS_FILE_UNSET+=VOD
OPTIONS_FILE_UNSET+=VTS
OPTIONS_FILE_UNSET+=XSS
OPTIONS_FILE_UNSET+=WEBSOCKIFY
OPTIONS_FILE_SET+=ZSTD

EOF

portmaster -w -B -g -U --force-config www/nginx -n

Dienst in rc.conf eintragen

Der Dienst wird mittels sysrc in der rc.conf eingetragen und dadurch beim Systemstart automatisch gestartet.

Bash
sysrc nginx_enable=YES
sysrc nginxlimits_enable=YES

Logrotation einrichten

Bash
cat <<'EOF' > /usr/local/etc/newsyslog.conf.d/nginx.conf
EOF

Konfiguration

Konfigurationsdateien

Der Port liefert bereits Beispielkonfigurationen mit. Die zentrale Konfiguration liegt unter /usr/local/etc/nginx/nginx.conf; zusätzliche Dateien wie vhosts.conf und vhosts-ssl.conf können über include eingebunden werden. Änderungen werden erst nach reload oder restart wirksam. (freshports.org)

Bash
cat <<'EOF' > /usr/local/etc/nginx/nginx.conf
user  www;
worker_processes  auto;
pid   /var/run/nginx.pid;
error_log  /var/log/nginx/error.log;

events {
    worker_connections 4096;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format combined
        '$host $remote_addr - $remote_user [$time_local] "$request" '
        '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
        '"$http_x_forwarded_for"';

    log_format combinedssl
        '$host $remote_addr - $remote_user [$time_local] "$request" '
        '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
        '"$http_x_forwarded_for" "$ssl_protocol" "$ssl_cipher"';

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;

    keepalive_timeout   2s;
    keepalive_requests  500;

    server_tokens off;
    charset utf-8;

    gzip on;
    gzip_vary on;
    gzip_http_version 1.1;
    gzip_proxied any;
    gzip_min_length 1024;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/json
        application/ld+json
        application/manifest+json
        application/xml
        application/xhtml+xml
        application/rss+xml
        application/atom+xml
        image/svg+xml
        image/vnd.microsoft.icon;

    brotli on;
    brotli_comp_level 5;
    brotli_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/json
        application/ld+json
        application/manifest+json
        application/xml
        application/xhtml+xml
        application/rss+xml
        application/atom+xml
        image/svg+xml
        image/vnd.microsoft.icon;

    include /usr/local/etc/nginx/vhosts.conf;
    include /usr/local/etc/nginx/vhosts-ssl.conf;
}
EOF

cat <<'EOF' > /usr/local/etc/nginx/vhosts.conf
server {
    listen 127.0.0.1:80;
    server_name localhost;
    access_log /usr/local/www/vhosts/0localhost0/logs/nginx_access_log combined;
    error_log  /usr/local/www/vhosts/0localhost0/logs/nginx_error_log warn;
    root  /usr/local/www/vhosts/0localhost0/data;
    index index.html index.htm index.php;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    location = /.well-known/server-status {
        stub_status;
        allow 127.0.0.1;
        allow ::1;
        deny all;
    }
    error_page  404              /404.html;
    location = /404.html {
        root   /usr/local/www/nginx-dist;
    }
    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/www/nginx-dist;
    }

    location ^~ /.well-known/acme-challenge/ {
        root /usr/local/www;
        default_type text/plain;
        try_files $uri =404;
    }
    location ~ /\.(?!well-known/acme-challenge/) {
        return 404;
    }
    location / {
        try_files $uri $uri/ =404;
    }

    location ~ /\.ht {
        deny  all;
    }
}
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name devnull.example.com _;
    access_log /usr/local/www/vhosts/0default0/logs/nginx_access_log combined;
    error_log  /usr/local/www/vhosts/0default0/logs/nginx_error_log warn;
    root /usr/local/www/vhosts/0default0/data;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    location = /.well-known/server-status {
        stub_status;
        allow 127.0.0.1;
        allow ::1;
        deny all;
    }
    error_page  404              /404.html;
    location = /404.html {
        root   /usr/local/www/nginx-dist;
    }
    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/www/nginx-dist;
    }

    location ^~ /.well-known/acme-challenge/ {
        root /usr/local/www;
        default_type text/plain;
        try_files $uri =404;
    }
    location ~ /\.(?!well-known/acme-challenge/) {
        return 404;
    }
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ /\.ht {
        deny  all;
    }

    return 308 https://devnull.example.com$request_uri;
}
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    access_log /usr/local/www/vhosts/www.example.com/logs/nginx_access_log combined;
    error_log  /usr/local/www/vhosts/www.example.com/logs/nginx_error_log warn;
    root /usr/local/www/vhosts/www.example.com/data;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    location = /.well-known/server-status {
        stub_status;
        allow 127.0.0.1;
        allow ::1;
        deny all;
    }
    error_page  404              /404.html;
    location = /404.html {
        root   /usr/local/www/nginx-dist;
    }
    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/www/nginx-dist;
    }

    location ^~ /.well-known/acme-challenge/ {
        root /usr/local/www;
        default_type text/plain;
        try_files $uri =404;
    }
    location ~ /\.(?!well-known/acme-challenge/) {
        return 404;
    }
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ /\.ht {
        deny  all;
    }

    return 308 https://www.example.com$request_uri;
}
server {
    listen 80;
    listen [::]:80;
    server_name mail.example.com;
    access_log /usr/local/www/vhosts/mail.example.com/logs/nginx_access_log combined;
    error_log  /usr/local/www/vhosts/mail.example.com/logs/nginx_error_log warn;
    root /usr/local/www/vhosts/mail.example.com/data;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    error_page  404              /404.html;
    location = /404.html {
        root   /usr/local/www/nginx-dist;
    }
    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/www/nginx-dist;
    }

    location = /.well-known/server-status {
        stub_status;
        allow 127.0.0.1;
        allow ::1;
        deny all;
    }
    location ^~ /.well-known/acme-challenge/ {
        root /usr/local/www;
        default_type text/plain;
        try_files $uri =404;
    }
    location ~ /\.(?!well-known/acme-challenge/) {
        return 404;
    }
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ /\.ht {
        deny  all;
    }

    return 308 https://mail.example.com$request_uri;
}
EOF

cat <<'EOF' > /usr/local/etc/nginx/vhosts-ssl.conf
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    http2 on;
    server_name devnull.example.com _;
    access_log /usr/local/www/vhosts/0default0/logs/nginx_ssl_access_log combinedssl;
    error_log  /usr/local/www/vhosts/0default0/logs/nginx_ssl_error_log warn;
    root  /usr/local/www/vhosts/0default0/data;
    index index.html index.htm index.php;
    ssl_certificate         /usr/local/etc/letsencrypt/live/devnull.example.com/fullchain.pem;
    ssl_certificate_key     /usr/local/etc/letsencrypt/live/devnull.example.com/privkey.pem;
    ssl_trusted_certificate /usr/local/etc/letsencrypt/live/devnull.example.com/chain.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers on;
    ssl_ecdh_curve X448:X25519:secp384r1:prime256v1:secp521r1;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 127.0.0.1 [::1] valid=300s;
    resolver_timeout 2s;
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    location = /.well-known/server-status {
        stub_status;
        allow 127.0.0.1;
        allow ::1;
        deny all;
    }
    error_page  404              /404.html;
    location = /404.html {
        root   /usr/local/www/nginx-dist;
    }
    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/www/nginx-dist;
    }

    location ^~ /.well-known/acme-challenge/ {
        root /usr/local/www;
        default_type text/plain;
        try_files $uri =404;
    }
    location ~ /\.(?!well-known/acme-challenge/) {
        return 404;
    }
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
        fastcgi_param HTTPS on;
        fastcgi_pass unix:/var/run/fpm_www.sock;
    }

    location ~ /\.ht {
        deny  all;
    }
}
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name example.com;
    ssl_certificate     /usr/local/etc/letsencrypt/live/www.example.com/fullchain.pem;
    ssl_certificate_key /usr/local/etc/letsencrypt/live/www.example.com/privkey.pem;
    return 308 https://www.example.com$request_uri;
}
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name www.example.com;
    access_log /usr/local/www/vhosts/www.example.com/logs/nginx_ssl_access_log combinedssl;
    error_log  /usr/local/www/vhosts/www.example.com/logs/nginx_ssl_error_log warn;
    root  /usr/local/www/vhosts/www.example.com/data;
    index index.html index.htm index.php;
    ssl_certificate         /usr/local/etc/letsencrypt/live/www.example.com/fullchain.pem;
    ssl_certificate_key     /usr/local/etc/letsencrypt/live/www.example.com/privkey.pem;
    ssl_trusted_certificate /usr/local/etc/letsencrypt/live/www.example.com/chain.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers on;
    ssl_ecdh_curve X448:X25519:secp384r1:prime256v1:secp521r1;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 127.0.0.1 [::1] valid=300s;
    resolver_timeout 2s;
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    location = /.well-known/server-status {
        stub_status;
        allow 127.0.0.1;
        allow ::1;
        deny all;
    }
    error_page  404              /404.html;
    location = /404.html {
        root   /usr/local/www/nginx-dist;
    }
    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/www/nginx-dist;
    }

    location ^~ /.well-known/acme-challenge/ {
        root /usr/local/www;
        default_type text/plain;
        try_files $uri =404;
    }
    location ~ /\.(?!well-known/acme-challenge/) {
        return 404;
    }
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
        fastcgi_param HTTPS on;
        fastcgi_pass unix:/var/run/fpm_www.sock;
    }

    location ~ /\.ht {
        deny  all;
    }
}
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name mail.example.com;
    access_log /usr/local/www/vhosts/mail.example.com/logs/nginx_ssl_access_log combinedssl;
    error_log  /usr/local/www/vhosts/mail.example.com/logs/nginx_ssl_error_log warn;
    root  /usr/local/www/vhosts/mail.example.com/data;
    index index.html index.htm index.php;
    ssl_certificate         /usr/local/etc/letsencrypt/live/mail.example.com/fullchain.pem;
    ssl_certificate_key     /usr/local/etc/letsencrypt/live/mail.example.com/privkey.pem;
    ssl_trusted_certificate /usr/local/etc/letsencrypt/live/mail.example.com/chain.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers on;
    ssl_ecdh_curve X448:X25519:secp384r1:prime256v1:secp521r1;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 127.0.0.1 [::1] valid=300s;
    resolver_timeout 2s;
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    location = /.well-known/server-status {
        stub_status;
        allow 127.0.0.1;
        allow ::1;
        deny all;
    }
    error_page  404              /404.html;
    location = /404.html {
        root   /usr/local/www/nginx-dist;
    }
    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/www/nginx-dist;
    }

    location ^~ /.well-known/acme-challenge/ {
        root /usr/local/www;
        default_type text/plain;
        try_files $uri =404;
    }
    location ~ /\.(?!well-known/acme-challenge/) {
        return 404;
    }
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
        fastcgi_param HTTPS on;
        fastcgi_pass unix:/var/run/fpm_www.sock;
    }

    location ~ /\.ht {
        deny  all;
    }
}
EOF

HTTP/2

Für aktuelles Nginx wird HTTP/2 über die Direktive http2 on; aktiviert. Seit Nginx 1.25.1 ist der alte http2-Parameter an listen deprecated. Das passt zu deinem ursprünglichen Hinweis und sollte in den VHosts genauso umgesetzt werden. (Nginx)

HTTP/3 nur bei Bedarf

HTTP/3 ist in Nginx ein eigener Modulpfad. Das offizielle ngx_http_v3_module wird weiterhin als experimental dokumentiert. Zusätzlich reicht ein Build mit HTTP/3-Unterstützung allein nicht aus: du brauchst einen separaten QUIC-Listener wie listen 443 quic reuseport;. Die offizielle Dokumentation empfiehlt außerdem, für HTTP/3 und HTTPS denselben Port zu verwenden. (Nginx)

Wenn du HTTP/3 nicht gezielt brauchst, aktiviere zunächst nur HTTP/2 und nimm QUIC später separat in Betrieb.

PHP-FPM über Unix-Socket

Für PHP-FPM ist die FastCGI-Anbindung per Unix-Socket fachlich korrekt. Nginx unterstützt bei fastcgi_pass ausdrücklich Socket-Pfade im Format unix:/pfad/zur.sock. Genau deshalb muss der in deiner Nginx-Konfiguration eingetragene Socket-Pfad wirklich zum laufenden PHP-FPM-Pool passen. (Nginx)

fixperms.sh einrichten

Bash
cat <<'EOF' > /usr/local/www/vhosts/0default0/cron/fixperms.sh
#!/bin/sh

DIR="$(dirname $0)"

chown -R root:wheel $DIR/../conf
chown -R root:wheel $DIR/../logs

chown -R www:www    $DIR/../cron
chown -R www:www    $DIR/../data

find $DIR/../conf/ -type d -print0 | xargs -0 chmod 0755
find $DIR/../conf/ -type f -print0 | xargs -0 chmod 0664

find $DIR/../logs/ -type d -print0 | xargs -0 chmod 0755
find $DIR/../logs/ -type f -print0 | xargs -0 chmod 0664

find $DIR/../cron/ -type d -print0 | xargs -0 chmod 0755
find $DIR/../cron/ -type f -print0 | xargs -0 chmod 0755

find $DIR/../data/ -type d -print0 | xargs -0 chmod 6750
find $DIR/../data/ -type f -print0 | xargs -0 chmod 0640

exit 0
EOF
chmod 750 /usr/local/www/vhosts/0default0/cron/fixperms.sh

cat <<'EOF' > /usr/local/www/vhosts/mail.example.com/cron/fixperms.sh
#!/bin/sh

DIR="$(dirname $0)"

chown -R root:wheel $DIR/../conf
chown -R root:wheel $DIR/../logs

chown -R www:www    $DIR/../cron
chown -R www:www    $DIR/../data

find $DIR/../conf/ -type d -print0 | xargs -0 chmod 0755
find $DIR/../conf/ -type f -print0 | xargs -0 chmod 0664

find $DIR/../logs/ -type d -print0 | xargs -0 chmod 0755
find $DIR/../logs/ -type f -print0 | xargs -0 chmod 0664

find $DIR/../cron/ -type d -print0 | xargs -0 chmod 0755
find $DIR/../cron/ -type f -print0 | xargs -0 chmod 0755

find $DIR/../data/ -type d -print0 | xargs -0 chmod 6750
find $DIR/../data/ -type f -print0 | xargs -0 chmod 0640

exit 0
EOF
chmod 750 /usr/local/www/vhosts/mail.example.com/cron/fixperms.sh

cat <<'EOF' > /usr/local/www/vhosts/www.example.com/cron/fixperms.sh
#!/bin/sh

DIR="$(dirname $0)"

chown -R root:wheel $DIR/../conf
chown -R root:wheel $DIR/../logs

chown -R www:www    $DIR/../cron
chown -R www:www    $DIR/../data

find $DIR/../conf/ -type d -print0 | xargs -0 chmod 0755
find $DIR/../conf/ -type f -print0 | xargs -0 chmod 0664

find $DIR/../logs/ -type d -print0 | xargs -0 chmod 0755
find $DIR/../logs/ -type f -print0 | xargs -0 chmod 0664

find $DIR/../cron/ -type d -print0 | xargs -0 chmod 0755
find $DIR/../cron/ -type f -print0 | xargs -0 chmod 0755

find $DIR/../data/ -type d -print0 | xargs -0 chmod 6750
find $DIR/../data/ -type f -print0 | xargs -0 chmod 0640

exit 0
EOF
chmod 750 /usr/local/www/vhosts/www.example.com/cron/fixperms.sh

Konfiguration prüfen

Vor dem ersten Start sollte die Konfiguration immer geprüft werden. nginx -t prüft Syntax und grundlegende Konsistenz. Änderungen an der Konfiguration werden von Nginx erst nach Reload oder Restart übernommen; beim Reload validiert der Master-Prozess die neue Konfiguration zuerst und rollt bei Fehlern auf die alte zurück. (Nginx)

Bash
nginx -t
service nginx restart
sockstat -4 -6 -l | egrep 'nginx|php-fpm'

Für HTTP/2 und optionale HTTP/3-/Brotli-Nutzung solltest du lokal zusätzlich prüfen, ob der Port wirklich mit den gewünschten Modulen gebaut wurde.


Datenbanken

Für dieses HowTo sind keine Datenbanken erforderlich.


Zusatzsoftware

Mögliche Zusatzsoftware wird hier installiert und konfiguriert.

Für dieses HowTo ist keine zusätzliche Software erforderlich.

Brotli bleibt eine bewusste Zusatzentscheidung. Im Standard-Port www/nginx ist es nicht Teil der Default-Optionen. (GitHub)


Aufräumen

Überflüssige oder temporäre Verzeichnisse und Dateien entsorgen.

Zusatzsoftware Installation

Nicht erforderlich.

Zusatzsoftware Konfiguration

Nicht erforderlich.


Abschluss

Nginx kann nun gestartet werden.

Bash
service nginx start

Für spätere Änderungen:

Bash
service nginx reload
service nginx restart

Nginx übernimmt Änderungen an Konfigurationsdateien erst nach einem Reload oder Restart. Beim Reload werden neue Worker nur dann gestartet, wenn die neue Konfiguration gültig ist. (Nginx)


Referenzen

  • FreeBSD FreshPorts: www/nginx (freshports.org)
  • Nginx Beginner’s Guide (Nginx)
  • Nginx Runtime Control / Reload-Verhalten (Nginx)
  • Nginx ngx_http_fastcgi_module (Nginx)
  • Nginx ngx_http_v2_module (Nginx)
  • Nginx CHANGES 1.25.1 (http2-Direktive, Deprecation von listen ... http2) (Nginx)
  • Nginx QUIC / HTTP/3 (Nginx)