v2中文文档
项目

php_fastcgi

一个固执己见的指令,将请求代理到 PHP FastCGI 服务器,例如 php-fpm。

Caddy 的reverse_proxy能够为任何 FastCGI 应用程序提供服务,但该指令是专门为 PHP 应用程序量身定制的。该指令是一个方便的快捷方式,取代了更长的配置

它期望站点根目录中的任何index.php都充当路由器。如果不希望这样,可以重新配置try_files 子指令来修改默认的重写行为,或者以展开形式为基础并根据您的需要进行自定义。

除了下面列出的子指令之外,该指令还支持reverse_proxy的所有子指令。例如,您可以启用负载均衡和运行状况检查。

Most modern PHP apps work fine without extra subdirectives or customization. 子指令通常仅在某些边缘情况或旧版 PHP 应用程序中使用。

语法

php_fastcgi [<matcher>] <php-fpm_gateways...> {
	root <path>
	split <substrings...>
	index <filename>|off
	try_files <files...>
	env [<key> <value>]
	resolve_root_symlink
	capture_stderr
	dial_timeout  <duration>
	read_timeout  <duration>
	write_timeout <duration>

	<any other reverse_proxy subdirectives...>
}
  • <php-fpm_gateways...> 是 FastCGI 服务器的addresses。通常是 TCP 套接字或 unix 套接字文件。

  • root将根文件夹设置为站点。建议始终将root 指令php_fastcgi结合使用,但是当您的 PHP-FPM 上游使用与 Caddy 不同的根目录时,覆盖此设置可能会很有用(请参阅示例)。如果使用,则默认为root 指令的值,否则默认为 Caddy 的当前工作目录。

  • split设置将 URI 分成两部分的子字符串。第一个匹配的子字符串将用于从路径中分割“路径信息”。第一部分以匹配的子字符串为后缀,并将被假定为实际资源(CGI 脚本)名称。第二部分将设置为 PATH_INFO 供 CGI 脚本使用。默认:.php

  • index指定视为目录索引文件的文件名。这会影响展开形式中的文件匹配器。默认:index.php。可以设置为off,以在找不到匹配文件时禁用重写回退到index.php

  • try_files指定默认尝试文件重写的覆盖。详情请见try_files 指令。默认:{path} {path}/index.php index.php

  • env将额外的环境变量设置为给定值。可以为多个环境变量指定多次。默认情况下,所有相关的 FastCGI 环境变量都已设置(包括 HTTP 标头),但您可以根据需要添加或覆盖变量。

  • resolve_root_symlinkroot目录是符号链接(symlink)时,这可以将其解析为其实际值。有时,通过简单地交换符号链接以指向另一个目录中的新版本,这可以用作部署策略。默认情况下禁用以避免重复的系统调用。

  • capture_stderr可以捕获并记录上游 fastcgi 服务器在stderr上发送的任何消息。默认情况下,日志记录在WARN级别完成。如果响应具有4xx5xx状态,则将使用ERROR级别。默认情况下,stderr被忽略。

  • dial_timeout持续时间值,设置连接到上游套接字时等待多长时间。默认:3s

  • read_timeout持续时间值,设置从 FastCGI 上游读取时等待的时间。默认:无超时。

  • write_timeout持续时间值,设置发送到 FastCGI 上游时等待多长时间。默认:无超时。

由于该指令是反向代理上的固执己见的包装器,因此您可以使用reverse_proxy的任何子指令来自定义它。

展开形式

php_fastcgi指令(无子指令)与以下配置相同。大多数现代 PHP 应用程序都可以很好地使用此预设。如果您没有,请随意借鉴此内容并根据需要进行自定义,而不是使用php_fastcgi快捷方式。

route {
	# Add trailing slash for directory requests
	# This redirection is automatically disabled if "{http.request.uri.path}/index.php"
	# doesn't appear in the try_files list
	@canonicalPath {
		file {path}/index.php
		not path */
	}
	redir @canonicalPath {http.request.orig_uri.path}/ 308

	# If the requested file does not exist, try index files and assume index.php always exists
	@indexFiles file {
		try_files {path} {path}/index.php index.php
		try_policy first_exist_fallback
		split_path .php
	}
	rewrite @indexFiles {file_match.relative}

	# Proxy PHP files to the FastCGI responder
	@phpFiles path *.php
	reverse_proxy @phpFiles <php-fpm_gateway> {
		transport fastcgi {
			split .php
		}
	}
}

解释

  • 第一部分涉及规范化请求路径。目标是确保针对磁盘上目录的请求实际上将尾部斜杠/添加到请求路径中,以便只有单个 URL 对于对该目录的请求有效。

    仅当try_files子指令包含{path}/index.php(默认值)时,才会发生此规范化。

    这是通过使用请求匹配器来执行的,该请求匹配器仅匹配不以斜杠结尾的请求,并且映射到磁盘上包含index.php文件的目录,如果匹配,则执行附加尾部斜杠的 HTTP 308 重定向。例如,如果磁盘上存在/foo/index.php,它会将路径/foo的请求重定向到/foo/(附加/,以规范化目录路径)。

  • 下一节将讨论根据磁盘上是否存在匹配文件来执行路径重写。这还有一个副作用,就是记住.php之后的路径部分(如果请求路径中有.php)。这对于 Caddy 正确设置 FastCGI 环境变量非常重要。

    • 首先,它检查{path}是否是磁盘上存在的文件。如果是这样,它将重写到该路径。这本质上使其余部分短路,并确保对磁盘上_确实存在_的文件的请求不会被重写(请参阅下面的后续步骤)。因此,如果您的磁盘上有一个/js/app.js文件,那么对该路径的请求将保持不变。

    • 其次,它检查{path}/index.php是否是磁盘上存在的文件。如果是这样,它将重写到该路径。对于像/foo/这样的目录的请求,它会查找/foo//index.php(它被规范化为/foo/index.php),并将请求重写到该路径(如果存在)。如果您在 Webroot 的子目录中运行另一个 PHP 应用程序,此行为有时会很有用。

    • 最后,它总是会重写为index.php(它几乎总是存在于现代 PHP 应用程序中)。这允许您的 PHP 应用程序通过使用index.php脚本作为其入口点来处理对_不_映射到磁盘上的文件的路径的任何请求。

  • 最后,最后一部分是实际代理对 PHP FastCGI(或 PHP-FPM)服务的请求以实际运行 PHP 代码。请求匹配器只会匹配以.php结尾的请求,因此,任何不是 PHP 脚本且存在于磁盘上的文件都不会被该指令处理,并且会失败。

php_fastcgi指令本身通常是不够的。它几乎应该总是与root 指令配对来设置文件在磁盘上的位置(对于现代 PHP 应用程序,这可能是/var/www/html/public,其中public目录包含您的index.php),并与file_server 指令一起提供您的静态文件(您的 JS、CSS、图像等),这些文件不会由该指令处理并失败。

示例

将所有 PHP 请求代理到在127.0.0.1:9000监听的 FastCGI 响应程序:

php_fastcgi 127.0.0.1:9000

相同,但仅适用于/blog/下的请求:

php_fastcgi /blog/* localhost:9000

当使用 PHP-FPM 通过 unix 套接字监听时:

php_fastcgi unix//run/php/php8.2-fpm.sock

root 指令几乎总是用于指定包含 PHP 脚本的目录,而file_server 指令则用于提供静态文件:

example.com {
	root /var/www/html/public
	php_fastcgi 127.0.0.1:9000
	file_server
}

当使用 Caddy 提供多个 PHP 应用程序时,每个应用程序的 Webroot 必须不同,以便 Caddy 可以单独读取和提供静态文件并检测 PHP 文件是否存在。

如果您使用 Docker,通常您的 PHP-FPM 容器会将文件安装在同一根目录下。在这种情况下,解决方案是将文件挂载到 Caddy 容器的不同目录中,然后使用root subdirective设置每个容器的根目录:

app1.example.com {
	root /srv/app1/public
	php_fastcgi app1:9000 {
		root /var/www/html/public
	}
	file_server
}

app2.example.com {
	root /srv/app2/public
	php_fastcgi app2:9000 {
		root /var/www/html/public
	}
	file_server
}

对于不使用index.php作为入口点的 PHP 站点,您可以回退到发出404错误。可以使用handle_errors 指令捕获并处理该错误:

example.com {
	php_fastcgi localhost:9000 {
		try_files {path} {path}/index.php =404
	}

	handle_errors {
		respond "{err.status_code} {err.status_text}"
	}
}