文档
一个 项目

php_fastcgi

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

Caddy 的 reverse_proxy 能够服务任何 FastCGI 应用程序,但此指令专门针对 PHP 应用程序。此指令是一个方便的快捷方式,它替换了 更长的配置

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

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

大多数现代 PHP 应用程序在没有额外的子指令或自定义的情况下都能正常工作。 子指令通常只在某些边缘情况下或使用旧版 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 服务器的地址。通常是 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 重写的覆盖。有关详细信息,请参阅 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 {
	# Add trailing slash for directory requests
	@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
	@indexFiles file {
		try_files {path} {path}/index.php index.php
		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 对该目录的请求有效。

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

  • 下一部分处理根据磁盘上是否存在匹配文件来执行路径重写。这也具有记住 .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

使用通过 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 应用程序时,每个应用程序的 webroot 必须不同,以便 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}"
	}
}