使用 PHP 实现伪强制 HTTPS
使用 PHP 实现伪强制 HTTPS

使用 PHP 实现伪强制 HTTPS

阅读时长 ≈2 分, 3 秒

最近建站时有这样一个需求,网站使用了 Cloudflare 的 CDN 加速服务,要实现强制 HTTPS 访问

我用的是 nginx,理论上来说在 80 端口的配置里写好 HTTP 301 或 302 转发就可以了

server {
	listen 80;
	listen [::]:80;
	server_name <domain>;
	return 301 https://<domain>$request_uri; # HTTP 301 永久重定向至 HTTPS
	# return 302 https://<domain>$request_uri; # HTTP 302 临时重定向也可以,看具体需求选用
}

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	server_name <domain>;
	...

但在实际测试过程中,HTTP 可以跳转到 HTTPS,但 HTTPS 一直出现 ERR_TOO_MANY_REDIRECTS 错误

排查了一通之后,发现锅出在 Cloudflare 上面:如图,在默认情况下,Cloudflare 与源服务器之间的流量通讯是不走 SSL/TLS 加密的,也就是直接走了上面的 80 端口,会被 301/302 回去,如此循环多次之后,浏览器便会报出 ERR_TOO_MANY_REDIRECTS 错误。

所以不能只在 nginx 中使用 301 转发,要在 Cloudflare 的 SSL/TLS 设置页中将 加密模式 修改为 完全;或者直接使用 灵活 模式,只加密浏览器到 Cloudflare 之间的流量,也算是变相实现了强制 HTTPS

那么问题又来了,我的域名不是通过正常的 NS 手段接入 Cloudflare 的,而是通过 Cloudflare Partner 提供的 CNAME 方式接入,Cloudflare Partner 并未提供 SSL/TLS 设置页面,上面的方案也就无法实施

转念一想,后端是可以获取到浏览器访问地址的,我们可以借助后端处理,变相实现伪强制 HTTPS

网站采用了 PHP 作为网站后端,那么也就可以利用 PHP 的 auto_prepend_file 特性,在每一个经过 PHP 处理的页面前插入跳转代码

<?php
	# Force SSL redirect
	function isSSL() {
		return (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on") || (isset($_SERVER["HTTP_X_FORWARDED_PROTO"]) && $_SERVER["HTTP_X_FORWARDED_PROTO"] == "https") || ((int) $_SERVER["SERVER_PORT"] == 443);
	}
	if (!isSSL()) {
		header("HTTP/1.1 301 Moved Permanently"); # 若要使用 HTTP 302,删去此行
		header("Location: https://" . $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"]);
	}
	# auto_prepend_file 只能插入一个文件,你也可以在这个文件中写点其他内容
?>

在服务器上随便找个地方,将上面的代码保存成 php 文件,之后在 php.ini 中插入以下配置

auto_prepend_file = "/path/to/file.php" ; 文件的相对/绝对路径

或在 nginx 引入 FastCGI 的部分插入

location ~ [^/]\.php(/|$) {
	...
        fastcgi_param PHP_VALUE "auto_prepend_file=/path/to/file.php"; # 文件的相对/绝对路径
	...
}

.htaccess 则是这么写

php_value auto_prepend_file "/path/to/file.php"

保存,重载配置,测试,正常从 HTTP 跳转到 HTTPS,大功告成

这种实现也有它的缺陷:

  1. 非全局强制 HTTPS,不走 FastCGI 处理的文件(如 html、png 等)不会自动跳转,不够安全(不怕性能损耗和 CGI 爆炸导致全站暴毙的话,可以将所有文件都走 FastCGI 处理
  2. 可能会造成性能损耗,给服务器带来更大的压力
  3. 好像没了?

2条评论

发表回复