Zum Hauptinhalt springen

Nginx instead of Apache in a manual installation

Preparation

Determine which distribution you are using. The actual configuration file paths and other details depend on it.

DEB (Debian, Ubuntu, etc.)RPM (AlmaLinux, Rocky, CentOS, etc.)
Virtual hostssites-available / sites-enabled by default/etc/nginx/conf.d/*.conf by default
Snippets (include)mkdir -p /etc/nginx/snippets/passworkmkdir -p /etc/nginx/snippets/passwork
PHP-FPM socket/run/php/php8.3-fpm.sock/run/php-fpm/www.sock or /var/run/php-fpm/www.sock
Nginx userwww-datanginx
PHP-FPM poolwww-data by defaultapache or nginx by default — must match the permissions on the site files

Header snippets

Move the directives into separate configuration files in /etc/nginx/snippets/passwork/. This lets you reuse them through include.

/etc/nginx/snippets/passwork/security-headers.conf

add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always;

/etc/nginx/snippets/passwork/cors.conf

add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET,HEAD,OPTIONS,POST,PUT,PATCH,DELETE" always;
add_header Access-Control-Allow-Headers "Authorization, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, X-Browser-Mode, X-Master-Key-Hash, X-CSRF-Token" always;
add_header Access-Control-Max-Age "1728000" always;
add_header Vary "Origin" always;

/etc/nginx/snippets/passwork/csp.conf

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; upgrade-insecure-requests; base-uri 'none'; form-action 'self';" always;

Enabling the site and verifying it

  • DEB: place the configuration file in sites-available and create a symlink in sites-enabled. In case of a conflict, remove default_server from the default site.
  • RPM: if include /etc/nginx/conf.d/*.conf; is enabled in nginx.conf, a configuration file in conf.d/ is enough by default.
nginx -t
systemctl reload nginx

Apache must be stopped before switching to Nginx. The main Nginx log file is /var/log/nginx/error.log

Complete example

Note:

  • The upstream block (uncomment the socket line for your distribution).
  • The HTTP→HTTPS redirect and the server block for HTTPS.
  • Specify server_name and the certificate paths.
Nginx configuration for Passwork
upstream php_passwork {
least_conn;
# DEB (Debian, Ubuntu, etc.):
# server unix:/run/php/php8.3-fpm.sock;
# RPM (AlmaLinux, Rocky, CentOS, etc.):
# server unix:/run/php-fpm/www.sock;
}

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name passwork.example.com;

ssl_certificate /etc/ssl/passwork/fullchain.pem;
ssl_certificate_key /etc/ssl/passwork/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;

root /var/www/public;
index index.html;

charset utf-8;
client_max_body_size 100M;
fastcgi_read_timeout 1800;

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

location = /favicon.ico {
access_log off;
log_not_found off;
}

location = /robots.txt {
access_log off;
log_not_found off;
}

location ~ /\. {
deny all;
}

location / {
include /etc/nginx/snippets/passwork/security-headers.conf;
include /etc/nginx/snippets/passwork/cors.conf;
if ($request_method = 'OPTIONS') {
return 204;
}
include /etc/nginx/snippets/passwork/csp.conf;
try_files $uri /index.html;
}

location ~ ^/api(/|$) {
include /etc/nginx/snippets/passwork/security-headers.conf;
include /etc/nginx/snippets/passwork/cors.conf;
if ($request_method = 'OPTIONS') {
return 204;
}
fastcgi_pass php_passwork;
include fastcgi.conf;
fastcgi_param SCRIPT_FILENAME /var/www/public/index.php;
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
}

location ~* \.(js|css|png|jpg|jpeg|gif|ico|woff|woff2|ttf|svg)$ {
expires 6M;
log_not_found off;
access_log off;
add_header Cache-Control "public, max-age=2592000";
}

location ~ ^/index\.php(/|$) {
try_files $uri =404;
fastcgi_pass php_passwork;
fastcgi_index index.php;
include fastcgi.conf;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
add_header Cache-Control "no-store, max-age=0" always;
}
}