全站强制HTTPS跳转应优先在Web服务器层实现:Apache用.htaccess配301跳转,Nginx在80端口server块中用return 301指令;禁用CI4的force_https()(不存在),框架层中间件仅作兜底。

全站强制 HTTPS 跳转,优先走 Web 服务器层(.htaccess 或 Nginx 配置),而不是框架层。CI4 的 force_https() 不可用,且 CI3 的版本默认发 302、不可控、不推荐用于生产环境。
Apache 下用 .htaccess 实现 301 全站跳转
这是最稳定、最早生效、对性能无影响的方式。确保服务器已启用 mod_rewrite,且站点根目录的 .htaccess 文件可被读取。
在 .htaccess 开头(RewriteEngine On 后)插入:
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
注意点:
-
RewriteCond %{HTTPS} !=on比检查%{SERVER_PORT} !^443$更可靠,能兼容反向代理场景 - 不要把这条规则放在 CI 的重写规则之后,否则可能被跳过
- 若同时要规范 www/non-www,需额外加条件,避免产生两次跳转
- 本地测试务必用无痕窗口,301 会被浏览器强缓存,改错后清不掉就卡死
CI4 中禁用 HTTP 请求入口(中间件方式)
仅当无法修改服务器配置时才考虑此方案。它不替代 HTTPS 跳转,而是作为兜底:一旦请求进到 PHP 层且非 HTTPS,立刻重定向。
新建 app/Middlewares/ForceHttps.php:
namespace App\Middlewares;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\HTTP\RedirectResponse;
class ForceHttps implements \CodeIgniter\Middleware\FilterInterface
{
public function before(RequestInterface $request, $arguments = null): RequestInterface|ResponseInterface
{
if ($request->getUri()->getScheme() !== 'https') {
return redirect()->to($request->getUri(), 'location', 301);
}
return $request;
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null): void
{
}
}
然后在 app/Config/Filters.php 中注册:
public $globals = [
'before' => [
'forcehttps',
],
];
并添加别名:
public $aliases = [
'forcehttps' => \App\Middlewares\ForceHttps::class,
];
关键限制:
- 该中间件只对通过 index.php 进入的请求有效;静态资源(如
/public/css/app.css)绕过 PHP,不会触发 -
redirect()->to()默认是 302,但 CI4 的redirect()支持第三个参数传状态码,所以这里显式写301 - 不能依赖
$this->request->isSecure()—— 它在反向代理下可能返回错误结果,必须用getUri()->getScheme()
为什么不要在控制器里调 force_https()
CI4 完全不加载 CI3 的辅助函数,force_https() 函数根本不存在。如果你在 CI4 项目里写了这行代码,运行时会直接报 Fatal error: Uncaught Error: Call to undefined function force_https()。
即使你手动引入了 CI3 的 url_helper.php,也会遇到问题:
- CI4 的
redirect()和 CI3 的行为不一致,混用易出错 -
force_https()内部硬编码为 302,且没有参数可改,不符合 SEO 和安全最佳实践 - 它在构造函数或
initController()中调用,意味着每个请求都多一次判断,而服务器层跳转在路由解析前就完成了
Nginx 环境下必须用 rewrite,不能靠 PHP
Nginx 不识别 .htaccess,所有重定向逻辑必须写在 server 块中。80 端口配置示例:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
比 rewrite 更推荐用 return,因为:
- 性能更高:不触发正则匹配,直接响应
- 语义更清晰:明确表示“这不是重写,就是跳转”
- 避免循环:某些 rewrite 写法(如未加
break或条件缺失)会导致 301 → 301 → ... 死循环
最后提醒:无论 Apache 还是 Nginx,证书必须已正确部署并能被浏览器信任,否则跳转后页面会显示“您的连接不是私密连接”,用户直接离开。跳转本身成功,不代表 HTTPS 生效。


















