文档
一个 项目

php_fastcgi

一个专门用于代理请求到 PHP FastCGI 服务器(如 php-fpm)的指令。

虽然 Caddy 的 reverse_proxy 能够为任何 FastCGI 应用程序提供服务,但此指令专门针对 PHP 应用程序进行了定制。这个指令是一个便捷的快捷方式,替代了更长的配置

它假定网站根目录下的任何 index.php 都作为路由器使用。如果这不是您想要的,您可以通过重新配置 try_files 子指令 来修改默认的重写行为,或者以展开形式为基础,根据您的需求进行自定义。

除了下面列出的子指令外,此指令还支持 reverse_proxy 的所有子指令。例如,您可以启用负载均衡和健康检查。

大多数现代 PHP 应用程序无需额外的子指令或自定义即可正常工作。 子指令通常仅在某些特殊情况下或与旧版 PHP 应用程序一起使用。

语法

php_fastcgi [<匹配器>] <php-fpm网关...> {
	root <路径>
	split <子字符串...>
	index <文件名>|off
	try_files <文件...>
	env [<键> <值>]
	resolve_root_symlink
	capture_stderr
	dial_timeout  <持续时间>
	read_timeout  <持续时间>
	write_timeout <持续时间>

	<任何其他 reverse_proxy 子指令...>
}
  • <php-fpm网关...> 是 FastCGI 服务器的地址。通常是 TCP 套接字或 Unix 套接字文件。

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

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

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

  • try_files 指定默认 try-files 重写的覆盖。有关详细信息,请参阅 try_files 指令。默认值:{path} {path}/index.php index.php

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

  • resolve_root_symlink root 目录是符号链接(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 {
	# 为目录请求添加尾部斜杠
	# 如果 "{http.request.uri.path}/index.php" 不在 try_files 列表中,
	# 此重定向将自动禁用
	@canonicalPath {
		file {path}/index.php
		not path */
	}
	redir @canonicalPath {http.request.orig_uri.path}/ 308

	# 如果请求的文件不存在,尝试索引文件并假设 index.php 始终存在
	@indexFiles file {
		try_files {path} {path}/index.php index.php
		try_policy first_exist_fallback
		split_path .php
	}
	rewrite @indexFiles {file_match.relative}

	# 将 PHP 文件代理到 FastCGI 响应器
	@phpFiles path *.php
	reverse_proxy @phpFiles <php-fpm网关> {
		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),如果它存在,则重写请求到该路径。如果您在网站根目录的子目录中运行另一个 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

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

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 应用程序时,每个应用程序的网站根目录必须不同,这样 Caddy 才能分别读取和服务您的静态文件,并检测 PHP 文件是否存在。

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

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}"
	}
}