nginx
nginx(发音为 "engine X")是一个免费、开源、高性能的 HTTP Web 服务器和反向代理,同时也是一个 IMAP/POP3 代理服务器,由 Igor Sysoev 于 2005 年编写。nginx 以其稳定性、丰富的功能集、简单的配置和低资源消耗而闻名。
本文介绍了如何设置 nginx 以及如何通过 #FastCGI 将其与 PHP 集成。
安装
建议使用 mainline 分支。使用 stable(稳定)分支的主要原因是你担心新功能可能带来的影响,例如与第三方模块的不兼容或 新功能中意外引入的 bug。
若要进行基于 chroot 的安装以提高安全性,请参阅 #在 chroot 中安装。
运行
启动/启用 nginx.service,如果使用 Angie 则启动/启用 angie.service。
默认的访问页面 http://127.0.0.1 位于 /usr/share/nginx/html/index.html。
配置
nginx 的入门步骤在 初学者指南中有描述。你可以通过编辑 /etc/nginx/ 下的文件来修改配置。主配置文件位于 /etc/nginx/nginx.conf。
更多详情和示例可在 官方文档中找到。
以下示例涵盖了最常见的用例。假设你使用默认的文档位置(/usr/share/nginx/html)。如果情况并非如此,请替换为你自己的路径。
配置示例
/etc/nginx/nginx.conf
user http;
worker_processes auto;
worker_cpu_affinity auto;
events {
worker_connections 1024;
}
http {
charset utf-8;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
log_not_found off;
types_hash_max_size 4096;
client_max_body_size 16M;
# MIME
include mime.types;
default_type application/octet-stream;
# logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
# load configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
通用配置
进程与连接
你应该为 worker_processes 选择一个合适的值。此设置最终定义了 nginx 将接受多少连接以及它能够利用多少处理器。通常,将其设置为系统中硬件线程的数量是一个不错的开始。另外,从 1.3.8 和 1.2.5 版本开始,worker_processes 接受 auto 值,它将尝试自动检测最佳值(来源)。
nginx 接受的最大连接数由 max_clients = worker_processes * worker_connections 计算得出。
以不同用户身份运行
默认情况下,nginx 以 root 用户运行主进程,以 http 用户运行工作进程。要以其他用户身份运行工作进程,请更改 nginx.conf 中的 user 指令。
/etc/nginx/nginx.conf
user user [group];
如果省略了组名,则使用与 user 同名的组。
服务器块
可以使用 server 块来服务多个域名。它们类似于 Apache HTTP Server 中的“虚拟主机”。另请参阅 上游示例。
在下面的示例中,服务器监听两个域名 domainname1.tld 和 domainname2.tld 的 IPv4 和 IPv6 的 80 端口连接。
/etc/nginx/nginx.conf
...
server {
listen 80;
listen [::]:80;
server_name domainname1.tld;
root /usr/share/nginx/domainname1.tld/html;
location / {
index index.php index.html index.htm;
}
}
server {
listen 80;
listen [::]:80;
server_name domainname2.tld;
root /usr/share/nginx/domainname2.tld/html;
...
}
重启 nginx.service 以应用更改。
管理服务器条目
可以将不同的 server 块放在不同的文件中。这允许你轻松启用或禁用某些站点。
若要使用 sites-enabled 和 sites-available 方法,请创建以下目录:
# mkdir /etc/nginx/sites-available # mkdir /etc/nginx/sites-enabled
在 sites-available 目录中创建一个包含一个或多个 server 块的文件:
/etc/nginx/sites-available/example.conf
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
...
}
在 http 块末尾添加 include sites-enabled/*;:
/etc/nginx/nginx.conf
http {
...
include sites-enabled/*;
}
要启用站点,只需创建符号链接:
# ln -s /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/example.conf
要禁用站点,取消链接(删除)活动的符号链接:
# unlink /etc/nginx/sites-enabled/example.conf
重载/重启 nginx.service 以启用对站点配置的更改。
TLS
OpenSSL 提供 TLS 支持,并且在 Arch 安装中默认安装。
- 在配置 SSL 之前,你可能需要先阅读 ngx_http_ssl_module 文档。
- Let’s Encrypt 是一个免费、自动化和开放的证书颁发机构。有一个插件可用于直接从命令行请求有效 SSL 证书并进行自动配置。
- Mozilla 有一篇有用的 TLS 文章,以及一个 自动化工具 来帮助创建更安全的配置。
创建一个私钥和自签名证书。这对于大多数不需要 CSR 的安装来说已经足够了:
# mkdir /etc/nginx/ssl # cd /etc/nginx/ssl # openssl req -new -x509 -nodes -newkey rsa:4096 -keyout server.key -out server.crt -days 1095 # chmod 400 server.key # chmod 444 server.crt
-days 开关是可选的,RSA 密钥大小可以低至 2048(默认值)。如果你需要创建一个 CSR,请按照这些说明操作,而不是上面的:
# mkdir /etc/nginx/ssl # cd /etc/nginx/ssl # openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out server.key # chmod 400 server.key # openssl req -new -sha256 -key server.key -out server.csr # openssl x509 -req -days 1095 -in server.csr -signkey server.key -out server.crt
带有 TLS 的 /etc/nginx/nginx.conf 的一个起点是 Mozilla 的 SSL 配置生成器。
重启 nginx.service 以应用更改。
用户个人目录
要将 Apache 风格的 ~user URL 复制到用户的 ~/public_html 目录,请尝试以下操作。(注意:如果同时使用了这两条规则,下面的更具体的 PHP 规则必须放在前面。)
/etc/nginx/nginx.conf
...
server {
...
# PHP in user directories, e.g. http://example.com/~user/test.php
location ~ ^/~(.+?)(/.+\.php)$ {
alias /home/$1/public_html$2;
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
# User directories, e.g. http://example.com/~user/
location ~ ^/~(.+?)(/.*)?$ {
alias /home/$1/public_html$2;
index index.html index.htm;
autoindex on;
}
...
}
...
有关使用 nginx 配置 PHP 的更多信息,请参阅 #PHP 实现。
重启 nginx.service 以启用新配置。
FastCGI
FastCGI(也称 FCGI)是一种用于将交互式程序与 Web 服务器接口的协议。FastCGI 是早期 通用网关接口 (CGI) 的变体;FastCGI 的主要目的是减少与接口 Web 服务器和 CGI 程序相关的开销,从而使服务器能够一次处理更多的网页请求。
FastCGI 技术被引入 nginx 以与许多外部工具协同工作,例如 Perl、PHP 和 Python。
PHP 实现
PHP-FPM 是作为 PHP 的 FastCGI 服务器运行的推荐解决方案。
安装 php-fpm 并确保已正确安装和配置 PHP。PHP-FPM 的主要配置文件是 /etc/php/php-fpm.conf。对于基本使用,默认配置应该足够了。
最后,启动/启用 php-fpm.service。
你也可以使用 php-legacy-fpm 代替,请参阅 #使用 php-legacy。
- 如果你 以不同用户身份运行 nginx,请确保 PHP-FPM 套接字文件可被此用户访问,或者使用 TCP 套接字。
- 如果你在 chroot 环境中运行 nginx(chroot 为
/srv/nginx-jail,网页在/srv/nginx-jail/www提供服务),你必须修改/etc/php/php-fpm.conf文件,在池部分(默认是[www])中包含chroot = /srv/nginx-jail和listen = /srv/nginx-jail/run/php-fpm/php-fpm.sock指令。如果缺少套接字文件所在的目录,请创建它。此外,对于动态链接到依赖项的模块,你需要将这些依赖项复制到 chroot 中(例如,对于 php-imagick,你需要将 ImageMagick 库复制到 chroot,但不需要 imagick.so 本身)。
nginx 配置
在服务 PHP Web 应用程序时,每个 服务器块 中都应该包含一个用于 PHP-FPM 的 location [2],例如:
/etc/nginx/sites-available/example.conf
server {
root /usr/share/nginx/html;
location / {
index index.html index.htm index.php;
}
location ~ \.php$ {
# 404
try_files $fastcgi_script_name =404;
# default fastcgi_params
include fastcgi_params;
# fastcgi settings
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
# fastcgi params
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
#fastcgi_param PHP_ADMIN_VALUE "open_basedir=$base/:/usr/lib/php/:/tmp/";
}
}
如果需要使用 PHP 处理其他扩展名(例如 .html 和 .htm):
location ~ [^/]\.(php|html|htm)(/|$) {
...
}
PHP-FPM 中的非 .php 扩展名处理也应在 /etc/php/php-fpm.d/www.conf 中明确添加:
security.limit_extensions = .php .html .htm
fastcgi_pass 参数,因为它必须是所选 FastCGI 服务器在其配置文件中定义的 TCP 或 Unix 套接字。php-fpm 的 默认(Unix)套接字是:fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
你可能会使用公共 TCP 套接字(非默认):
fastcgi_pass 127.0.0.1:9000;不过,Unix 域套接字应该更快。
server 块使用相同的 PHP-FPM 配置,可以使用 php_fastcgi.conf 配置文件来简化管理:/etc/nginx/php_fastcgi.conf
location ~ \.php$ {
# 404
try_files $fastcgi_script_name =404;
# default fastcgi_params
include fastcgi_params;
# fastcgi settings
...
}
要为特定服务器启用 PHP 支持,只需包含 php_fastcgi.conf 配置文件:
/etc/nginx/sites-available/example.conf
server {
server_name example.com;
...
include /etc/nginx/php_fastcgi.conf;
}
测试配置
如果更改了配置,你需要 重启 php-fpm.service 和 nginx.service 单元以应用更改。
要测试 FastCGI 实现,在 root 文件夹中创建一个包含以下内容的新 PHP 文件:
<?php phpinfo(); ?>
在浏览器中访问此文件,你应该会看到带有当前 PHP 配置的信息页面。
CGI 实现
此实现是 CGI 应用程序所必需的。
fcgiwrap
安装 fcgiwrap。配置通过 编辑 fcgiwrap.socket 完成。启用 并 启动 fcgiwrap.socket。
多个工作线程
如果你想生成多个工作线程,建议使用 multiwatchAUR,它将负责重启崩溃的子进程。你需要使用 spawn-fcgi 来创建 Unix 套接字,因为 multiwatch 似乎无法处理 systemd 创建的套接字,即使 fcgiwrap 本身如果在单元文件中直接调用,则没有任何问题。
覆盖单元 fcgiwrap.service(以及 fcgiwrap.socket 单元,如果存在),并根据需要修改 ExecStart 行。这是一个使用 multiwatchAUR 的单元文件。确保 fcgiwrap.socket 没有启动或启用,因为它会与此单元冲突:
/etc/systemd/system/fcgiwrap.service
[Unit] Description=Simple CGI Server After=nss-user-lookup.target [Service] ExecStartPre=/bin/rm -f /run/fcgiwrap.socket ExecStart=/usr/bin/spawn-fcgi -u http -g http -s /run/fcgiwrap.sock -n -- /usr/bin/multiwatch -f 10 -- /usr/sbin/fcgiwrap ExecStartPost=/usr/bin/chmod 660 /run/fcgiwrap.sock PrivateTmp=true Restart=on-failure [Install] WantedBy=multi-user.target
调整 -f 10 以更改生成的子进程数量。
spawn-fcgi 使用 -M 660 选项时看到一些奇怪的行为,所以需要 ExecStartPost 行。设置了错误的模式。这可能是一个 bug?nginx 配置
在 /etc/nginx 中,将 fastcgi_params 文件复制为 fcgiwrap_params。在 fcgiwrap_params 中,注释掉或删除设置 SCRIPT_NAME 和 DOCUMENT_ROOT 的行。
在每个服务 CGI Web 应用程序的 server 块中,应该出现类似于以下的 location 块:
location ~ \.cgi$ {
include fcgiwrap_params;
fastcgi_param DOCUMENT_ROOT /srv/www/cgi-bin/;
fastcgi_param SCRIPT_NAME myscript.cgi;
fastcgi_pass unix:/run/fcgiwrap.sock;
}
fcgiwrap 的默认套接字文件是 /run/fcgiwrap.sock。
使用 fastcgi_param SCRIPT_FILENAME /srv/www/cgi-bin/myscript.cgi 是设置 DOCUMENT_ROOT 和 SCRIPT_NAME 的快捷替代方案。如果使用 SCRIPT_FILENAME,你也不需要将 fastcgi_params 复制到 fcgiwrap_params 并注释掉 DOCUMENT_ROOT 和 SCRIPT_NAME 行。
如果一直收到 502 - bad Gateway 错误,你应该检查你的 CGI 应用程序是否首先声明了以下内容的 mime 类型。对于 HTML,这需要是 Content-type: text/html。
如果收到 403 错误,请确保 CGI 可执行文件可被 http 用户读取和执行,并且每个父文件夹均可被 http 用户读取。
在 chroot 中安装
在 chroot 中安装 nginx 增加了一层额外的安全性。为了获得最大安全性,chroot 应仅包含运行 nginx 服务器所需的文件,并且所有文件应具有尽可能严格的权限,例如,尽可能多的文件应由 root 所有,/usr/bin 等目录应不可读写,等等。
Arch 默认带有一个 http 用户和组,用于运行服务器。chroot 将位于 /srv/http 中。
一个用于创建此 jail 的 Perl 脚本可在 jail.pl gist 获得。你可以使用它,也可以按照本文中的说明进行操作。它期望以 root 身份运行。在进行任何更改之前,你需要取消注释一行。
创建必要的设备
nginx 需要 /dev/null、/dev/random 和 /dev/urandom。要在 chroot 中安装这些,请创建 /dev/ 目录并使用 mknod 添加设备。避免挂载整个 /dev/,以确保即使 chroot 被攻破,攻击者也必须从 chroot 中逃逸才能访问像 /dev/sda1 这样的重要设备。
- 确保
/srv/http没有以 nodev 选项挂载。 - 参阅 mknod(1) 和
ls -l /dev/{null,random,urandom}以更好地理解 mknod 选项。
# export JAIL=/srv/http # mkdir $JAIL/dev # mknod -m 0666 $JAIL/dev/null c 1 3 # mknod -m 0666 $JAIL/dev/random c 1 8 # mknod -m 0444 $JAIL/dev/urandom c 1 9
创建必要的目录
nginx 需要一系列文件才能正常运行。在复制它们之前,请创建存储它们的文件夹。这假设你的 nginx 文档根目录将是 /srv/http/www。
# mkdir -p $JAIL/etc/nginx/logs
# mkdir -p $JAIL/usr/{lib,bin}
# mkdir -p $JAIL/usr/share/nginx
# mkdir -p $JAIL/var/{log,lib}/nginx
# mkdir -p $JAIL/www/cgi-bin
# mkdir -p $JAIL/{run,tmp}
# cd $JAIL; ln -s usr/lib lib
# cd $JAIL; ln -s usr/lib lib64
# cd $JAIL/usr; ln -s lib lib64
然后将 $JAIL/tmp 和 $JAIL/run 挂载为 tmpfs。应限制大小以确保攻击者无法吃掉所有 RAM。
# mount -t tmpfs none $JAIL/run -o 'noexec,size=1M' # mount -t tmpfs none $JAIL/tmp -o 'noexec,size=100M'
为了在重启后保留挂载,应将以下条目添加到 /etc/fstab:
/etc/fstab
tmpfs /srv/http/run tmpfs rw,noexec,relatime,size=1024k 0 0 tmpfs /srv/http/tmp tmpfs rw,noexec,relatime,size=102400k 0 0
填充 chroot 环境
首先复制简单的文件。
# cp -r /usr/share/nginx/* $JAIL/usr/share/nginx # cp -r /usr/share/nginx/html/* $JAIL/www # cp /usr/bin/nginx $JAIL/usr/bin/ # cp -r /var/lib/nginx $JAIL/var/lib/nginx
现在复制所需的库。使用 ldd 列出它们,然后将它们全部复制到正确的位置。相比硬链接,复制更受青睐,以确保即使攻击者获得了文件的写访问权限,他们也无法销毁或更改真正的系统文件。
$ ldd /usr/bin/nginx
linux-vdso.so.1 (0x00007fffc41fe000) libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f57ec3e8000) libcrypt.so.1 => /usr/lib/libcrypt.so.1 (0x00007f57ec1b1000) libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f57ebead000) libm.so.6 => /usr/lib/libm.so.6 (0x00007f57ebbaf000) libpcre.so.1 => /usr/lib/libpcre.so.1 (0x00007f57eb94c000) libssl.so.1.0.0 => /usr/lib/libssl.so.1.0.0 (0x00007f57eb6e0000) libcrypto.so.1.0.0 => /usr/lib/libcrypto.so.1.0.0 (0x00007f57eb2d6000) libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f57eb0d2000) libz.so.1 => /usr/lib/libz.so.1 (0x00007f57eaebc000) libGeoIP.so.1 => /usr/lib/libGeoIP.so.1 (0x00007f57eac8d000) libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f57eaa77000) libc.so.6 => /usr/lib/libc.so.6 (0x00007f57ea6ca000) /lib64/ld-linux-x86-64.so.2 (0x00007f57ec604000)
对于驻留在 /usr/lib 中的文件,你可以尝试以下单行命令:
# cp $(ldd /usr/bin/nginx | grep /usr/lib/ | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
对于 ld-linux-x86-64.so,使用以下命令:
# cp /lib64/ld-linux-x86-64.so.2 $JAIL/lib
linux-vdso.so:它不是一个真实的库,在 /usr/lib 中也不存在。复制一些杂项但必要的库和系统文件。
# cp /usr/lib/libnss_* $JAIL/usr/lib
# cp -rfvL /etc/{services,localtime,nsswitch.conf,nscd.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf,nginx} $JAIL/etc
为 chroot 创建受限的用户/组文件。这样,就 chroot 所知,只有 chroot 正常运行所需的用户存在,并且如果攻击者获得了对 chroot 的访问权限,也不会泄露任何系统用户/组。
$JAIL/etc/group
http:x:33: nobody:x:99:
$JAIL/etc/passwd
http:x:33:33:http:/:/bin/false nobody:x:99:99:nobody:/:/bin/false
$JAIL/etc/shadow
http:x:14871:::::: nobody:x:14871::::::
$JAIL/etc/gshadow
http::: nobody:::
# touch $JAIL/etc/shells # touch $JAIL/run/nginx.pid
最后,设置非常严格的权限。尽可能多的文件应由 root 所有并设置为不可写。
# chown -R root:root $JAIL/
# chown -R http:http $JAIL/www
# chown -R http:http $JAIL/etc/nginx
# chown -R http:http $JAIL/var/{log,lib}/nginx
# chown http:http $JAIL/run/nginx.pid
# find $JAIL/ -gid 0 -uid 0 -type d -print | xargs chmod -rw
# find $JAIL/ -gid 0 -uid 0 -type d -print | xargs chmod +x
# find $JAIL/etc -gid 0 -uid 0 -type f -print | xargs chmod -x
# find $JAIL/usr/bin -type f -print | xargs chmod ug+rx
# find $JAIL/ -group http -user http -print | xargs chmod o-rwx
# chmod +rw $JAIL/tmp
# chmod +rw $JAIL/run
如果你的服务器将绑定 80 端口(或范围 [1-1023] 内的任何其他端口),请赋予 chrooted 可执行文件在没有 root 权限的情况下绑定这些端口的权限。
# setcap 'cap_net_bind_service=+ep' $JAIL/usr/bin/nginx
修改 nginx.service 以启动 chroot
覆盖单元 nginx.service。升级 nginx 不会修改你的自定义 .service 文件。
必须更改 systemd 单元以以 http 用户身份在 chroot 中启动 nginx,并将 PID 文件存储在 chroot 中。
/etc/systemd/system/nginx.service
[Unit] Description=A high performance web server and a reverse proxy server After=network.target [Service] Type=forking PIDFile=/srv/http/run/nginx.pid ExecStartPre=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -t -q -g 'pid /run/nginx.pid; daemon on; master_process on;' ExecStart=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;' ExecReload=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;' -s reload ExecStop=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid;' -s quit [Install] WantedBy=multi-user.target
你现在可以安全地卸载非 chroot 的 nginx 安装了。
# pacman -Rsc nginx
如果你不删除非 chroot 的 nginx 安装,你可能需要确保正在运行的 nginx 进程确实是 chroot 的那个。你可以通过检查 /proc/PID/root 符号链接到哪里来做到这一点。它应该链接到 /srv/http 而不是 /。
# ps -C nginx | awk '{print $1}' | sed 1d | while read -r PID; do ls -l /proc/$PID/root; done
技巧与提示
使用 systemd 以非特权用户运行
使用 nginx.service 的 嵌入式单元文件 (drop-in),并在 [Service] 下设置 User 和可选的 Group 选项:
/etc/systemd/system/nginx.service.d/user.conf
[Service] User=user Group=group
我们可以强化服务,使其永远无法提升权限:
/etc/systemd/system/nginx.service.d/user.conf
[Service] ... NoNewPrivileges=yes
然后我们需要确保 user 有权访问它所需的一切。请按照以下小节操作,然后 启动 nginx。
端口
Linux 默认不允许非 root 进程绑定到 1024 以下的端口。可以使用 1024 以上的端口:
/etc/nginx/nginx.conf
server {
listen 8080;
}
或者,你可以授予 nginx 进程 CAP_NET_BIND_SERVICE 能力,这允许它绑定到 1024 以下的端口:
/etc/systemd/system/nginx.service.d/user.conf
[Service] ... CapabilityBoundingSet= CapabilityBoundingSet=CAP_NET_BIND_SERVICE AmbientCapabilities= AmbientCapabilities=CAP_NET_BIND_SERVICE
或者,你可以使用 systemd 套接字激活。在这种情况下,systemd 将监听端口,并且当连接建立时,生成 nginx 并将套接字作为文件描述符传递。这意味着 nginx 不需要任何特殊能力,因为套接字在启动时已经存在。这依赖于 nginx 用于传递套接字的内部环境变量 [3],因此官方不支持。与其设置 CapabilityBoundingSet 和 AmbientCapabilities,不如编辑服务覆盖以设置 NGINX 环境变量,以告诉 nginx 将套接字作为哪些文件描述符传递:
/etc/systemd/system/nginx.service.d/user.conf
[Service] ... Environment=NGINX=3:4;
每个监听端口将有一个套接字,从文件描述符 3 开始,所以在这个例子中,我们告诉 nginx 预期有两个套接字。现在创建一个 nginx.socket 单元,指定要监听的端口:
/etc/systemd/system/nginx.socket
[Socket] ListenStream=0.0.0.0:80 ListenStream=0.0.0.0:443 After=network.target Requires=network.target [Install] WantedBy=sockets.target
套接字将按照此单元中定义的顺序传递,因此端口 80 将是文件描述符 3,端口 443 将是文件描述符 4。如果你之前启用或启动了该服务,你应该现在 停止 它,改为 启用 nginx.socket。当你的系统启动时,nginx 不会运行,但当你通过浏览器访问网站时,它会被启动。有了这个,你可以进一步强化服务;例如,在许多情况下,你现在可以在服务文件中设置 PrivateNetwork=True,阻塞 nginx 访问外部网络,因为 systemd 创建的套接字足以提供网站服务。请注意,这将在 nginx 服务的日志中打印一条警告:2020/08/29 19:33:20 [notice] 254#254: using inherited sockets from "3:4;"
PID 文件
nginx 编译时默认使用 /run/nginx.pid,但 user 无法写入。我们可以创建一个 user 可以写入的目录,并将 PID 文件放在那里。这可以通过 RuntimeDirectory (systemd.exec(5)) 来完成。
编辑 nginx.service 以配置 PID 文件:
/etc/systemd/system/nginx.service.d/user.conf
[Service] ... RuntimeDirectory=nginx PIDFile=/run/nginx/nginx.pid ExecStart= ExecStart=/usr/bin/nginx -g 'pid /run/nginx/nginx.pid; error_log stderr;' ExecReload= ExecReload=/usr/bin/nginx -s reload -g 'pid /run/nginx/nginx.pid; error_log stderr;'
/var/lib/nginx
nginx 编译时默认将临时文件存储在 /var/lib/nginx 中。
$ nginx -V 查看所有编译时选项。你可以通过例如使用 StateDirectory (systemd.exec(5)) 授予 user 对此目录的写权限。
/etc/systemd/system/nginx.service.d/user.conf
[Service] ... StateDirectory=nginx
/var/log/nginx
nginx 编译时默认将访问日志存储在 /var/log/nginx 中。
你可以通过例如使用 LogsDirectory (systemd.exec(5)) 授予 user 对此目录的写权限。
/etc/systemd/system/nginx.service.d/user.conf
[Service] ... LogsDirectory=nginx
使用 systemd 运行用户服务
如果你想运行一个完全由非特权用户控制和配置的服务器实例,这是一个 systemd 用户服务的示例。
~/.config/systemd/user/nginx.service
[Unit] Description=A high performance web server and a reverse proxy server [Service] Type=forking PIDFile=nginx.pid SyslogLevel=err ExecStart=nginx -g 'pid "%t/nginx.pid"; error_log stderr;' -c '%E/nginx/nginx.conf' -p '%D/nginx' ExecReload=nginx -g 'pid "%t/nginx.pid"; error_log stderr;' -s reload KillMode=mixed [Install] WantedBy=default.target
它从 $XDG_CONFIG_HOME/nginx/nginx.conf 读取配置,并使用 $XDG_DATA_HOME/nginx 作为工作目录。
systemd 的替代脚本
在纯 systemd 上,你可以利用 chroot + systemd 的优势。[4] 基于设置的 用户组 和 pid,使用:
/etc/nginx/nginx.conf
user http; pid /run/nginx.pid;
文件的绝对路径是 /srv/http/etc/nginx/nginx.conf。
/etc/systemd/system/nginx.service
[Unit] Description=nginx (Chroot) After=network.target [Service] Type=forking PIDFile=/srv/http/run/nginx.pid RootDirectory=/srv/http ExecStartPre=/usr/bin/nginx -t -c /etc/nginx/nginx.conf ExecStart=/usr/bin/nginx -c /etc/nginx/nginx.conf ExecReload=/usr/bin/nginx -c /etc/nginx/nginx.conf -s reload ExecStop=/usr/bin/nginx -c /etc/nginx/nginx.conf -s stop [Install] WantedBy=multi-user.target
无需设置默认位置,nginx 默认加载 -c /etc/nginx/nginx.conf,但这是一个好主意。
或者,你可以仅以 chroot 身份运行 ExecStart,并将参数 RootDirectoryStartOnly 设置为 yes(请参阅 systemd.service(5)),或者在挂载点作为有效项或 systemd 路径(请参阅 systemd.path(5))可用之前启动它。
/etc/systemd/system/nginx.path
[Unit] Description=nginx (Chroot) path [Path] PathExists=/srv/http/site/Public_html [Install] WantedBy=default.target
启用 创建的 nginx.path,并将 /etc/systemd/system/nginx.service 中的 WantedBy=default.target 更改为 WantedBy=nginx.path。
单元文件中的 PIDFile 允许 systemd 监视进程(需要绝对路径)。如果这是不需要的,你可以更改为默认的 one-shot 类型,并从单元文件中删除该引用。
Nginx 美化工具
nginxbeautifierAUR 是一个用于美化和格式化 nginx 配置文件的命令行工具。
Gixy 静态配置分析器
gixy-nextAUR 是过时的 Yandex Gixy 的一个分支 —— Nginx 配置的安全和性能问题分析器。默认情况下,它只读取 /etc/nginx/nginx.conf。可以通过以下命令获取并分析完整配置:
# nginx -T > nginx_full.conf $ gixy nginx_full.conf
更好的头部管理
Nginx 有一个相当直观的头部管理系统,其中头部只能在一个上下文中定义,任何其他头部都会被忽略。为了纠正这一点,我们可以安装 headers-more-nginx 模块。
安装 软件包 nginx-mod-headers-more。这将把模块安装到 /usr/lib/nginx/modules 目录。
要加载该模块,请将以下内容添加到你的 nginx 主配置文件的顶部。
/etc/nginx/nginx.conf
load_module "/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so"; ...
基本身份验证
基本身份验证需要创建一个密码文件。密码文件可以使用 apache 软件包提供的 htpasswd 程序进行管理,也可以使用 nginx_passwdAUR(它提供 nginx-passwd —— 详情可在 GitHub 源码上找到)来管理。
使用 php-legacy
安装 php-legacy-fpm 代替 php-fpm,并确保已正确安装和配置 PHP。
PHP-LEGACY-FPM 的主要配置文件是 /etc/php-legacy/php-fpm.conf。对于基本使用,默认配置应该足够了。
fastcgi_pass 参数的 Unix 套接字也需要调整,通常它是:
fastcgi_pass unix:/run/php-fpm-legacy/php-fpm.sock;
然后 启动/启用 php-legacy-fpm.service。
故障排除
配置验证
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
错误:The page you are looking for is temporarily unavailable. Please try again later. (502 Bad Gateway)
这是因为 FastCGI 服务器尚未启动,或者所使用的套接字权限错误。
尝试 这个答案 来修复 502 错误。
在 Arch Linux 中,上述链接中提到的配置文件是 /etc/php/php-fpm.conf。
错误:No input file specified
1. 验证 /etc/php/php.ini 中的 open_basedir 变量是否包含 nginx.conf 中作为 root 参数指定的正确路径(通常是 /usr/share/nginx/)。在使用 PHP-FPM 作为 PHP 的 FastCGI 服务器时,你可以在 location 块中添加 fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/";,该块旨在处理 nginx.conf 中的 PHP 文件。
2. 另一种情况是 nginx.conf 的 location ~ \.php$ 部分中的 root 参数错误。确保 root 指向与同一服务器中 location / 相同的目录。或者你可以直接将 root 设置为全局,不要在任何 location 部分中定义它。
3. 检查权限:例如用户/组使用 http,目录使用 755,文件使用 644。记住整个 html 目录的路径应该具有正确的权限。请参阅 文件权限和属性#批量 chmod 以批量修改目录树。
4. 你的 SCRIPT_FILENAME 没有包含脚本的完整路径。如果 nginx 的配置(fastcgi_param SCRIPT_FILENAME)正确,这种错误意味着 PHP 无法加载请求的脚本。通常这只是一个权限问题,你可以直接以 root 身份运行 php-cgi:
# spawn-fcgi -a 127.0.0.1 -p 9000 -f /usr/bin/php-cgi
或者你应该创建一个组和用户来启动 php-cgi:
# groupadd www # useradd -g www www # chmod +w /srv/www/nginx/html # chown -R www:www /srv/www/nginx/html # spawn-fcgi -a 127.0.0.1 -p 9000 -u www -g www -f /usr/bin/php-cgi
5. 如果你正在 chroot 的 nginx 中运行 php-fpm,请确保 /etc/php-fpm/php-fpm.d/www.conf(或旧版本中的 /etc/php-fpm/php-fpm.conf)中正确设置了 chroot。
警告:Could not build optimal types_hash
当启动 nginx.service 时,进程可能会记录以下消息:
[warn] 18872#18872: could not build optimal types_hash, you should increase either types_hash_max_size: 1024 or types_hash_bucket_size: 64; ignoring types_hash_bucket_size
要修复此警告,请在 http 块中增加这些键的值 [5] [6]:
/etc/nginx/nginx.conf
http {
types_hash_max_size 4096;
server_names_hash_bucket_size 128;
...
}
Cannot assign requested address
来自 nginx.service 单元状态 的完整错误是:
[emerg] 460#460: bind() to A.B.C.D:443 failed (99: Cannot assign requested address)
即使你的 nginx 单元文件配置为在 systemd 的 network.target 之后运行,nginx 也可能尝试在已配置但尚未添加到任何接口的地址上进行监听。通过手动运行 nginx 的 启动(从而显示 IP 地址配置正确)来验证是否属于这种情况。将 nginx 配置为监听任何地址将解决此问题。现在,如果你的用例需要监听特定地址,一种可能的解决方案是重新配置 systemd。
要在所有已配置的网络设备启动并分配 IP 地址后启动 nginx,请在 nginx.service 中的 After= 后面追加 network-online.target,并 启动/启用 systemd-networkd-wait-online.service。