Laravel 5 - 重定向到 HTTPS

Posted

技术标签:

【中文标题】Laravel 5 - 重定向到 HTTPS【英文标题】:Laravel 5 - redirect to HTTPS 【发布时间】:2015-04-08 18:38:30 【问题描述】:

我正在处理我的第一个 Laravel 5 项目,但不确定在何处或如何放置逻辑以在我的应用程序上强制使用 HTTPS。这里的关键是有许多指向应用程序的域,并且只有三分之二的使用 SSL(第三个是后备域,说来话长)。所以我想在我的应用程序逻辑而不是 .htaccess 中处理这个问题。

在 Laravel 4.2 中,我使用此代码完成了重定向,位于 filters.php:

App::before(function($request)

    if( ! Request::secure())
    
        return Redirect::secure(Request::path());
    
);

我在想中间件是应该实现这样的东西的地方,但我不能完全弄清楚使用它。

谢谢!

更新

如果您像我一样使用 Cloudflare,这可以通过在控制面板中添加新的页面规则来完成。

【问题讨论】:

那么第三个域会发生什么?如果您在所有路由上强制使用 https - 第三个域会继续工作吗? $_SERVER['HTTP_HOST']检测到 cloudflare页面规则多久生效 哦,我必须在 DNS 设置中打开代理哈哈! 【参考方案1】:

您可以使其与中间件类一起使用。让我给你一个想法。

namespace MyApp\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\App;

class HttpsProtocol 

    public function handle($request, Closure $next)
    
            if (!$request->secure() && App::environment() === 'production') 
                return redirect()->secure($request->getRequestUri());
            

            return $next($request); 
    

然后,将此中间件应用于每个请求,在Kernel.php 文件中添加设置规则,如下所示:

protected $middleware = [
    'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
    'Illuminate\Cookie\Middleware\EncryptCookies',
    'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
    'Illuminate\Session\Middleware\StartSession',
    'Illuminate\View\Middleware\ShareErrorsFromSession',

    // appending custom middleware 
    'MyApp\Http\Middleware\HttpsProtocol'       

];

在上面的示例中,如果出现以下情况,中间件会将每个请求重定向到 https:

    当前请求未附带安全协议 (http) 如果您的环境等于production。因此,只需根据您的喜好调整设置即可。

Cloudflare

我在带有 WildCard SSL 的生产环境中使用此代码,并且代码可以正常工作。如果我删除 && App::environment() === 'production' 并在 localhost 中对其进行测试,则重定向也有效。所以,有没有安装 SSL 都不是问题。看起来您需要非常注意您的 Cloudflare 层才能重定向到 Https 协议。

2015 年 3 月 23 日编辑

感谢@Adam Link的建议:很可能是Cloudflare传递的headers造成的。 CloudFlare 可能通过 HTTP 访问您的服务器并传递一个 X-Forwarded-Proto 标头,该标头声明它正在转发 HTTPS 请求。您需要在中间件中添加另一行,说明...

$request->setTrustedProxies( [ $request->getClientIp() ] ); 

...信任 CloudFlare 发送的标头。这将停止重定向循环

编辑 27/09/2016 - Laravel v5.3

只需要将中间件类添加到kernel.php file中的web组中即可:

protected $middlewareGroups = [
    'web' => [
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,

        // here
        \MyApp\Http\Middleware\HttpsProtocol::class

    ],
];

请记住,web 组默认应用于每个路由,因此您无需在路由或控制器中显式设置web

编辑 23/08/2018 - Laravel v5.7

要根据环境重定向请求,您可以使用App::environment() === 'production'。对于以前的版本是 env('APP_ENV') === 'production'。 使用\URL::forceScheme('https');实际上不会重定向。它只是在网站呈现后与https:// 建立链接。

【讨论】:

这似乎给了我一个重定向循环......不过看起来它应该可以工作。我不知道这是否有任何区别,但我们使用的是 Cloudflare SSL。但我认为这不会改变简单的重定向。 @NightMICU 我不确定您是否已经解决了重定向问题,但这可能是由 Cloudflare 传递的标头引起的。 CloudFlare 可能通过 HTTP 访问您的服务器并传递一个 X-Forwarded-Proto 标头,该标头声明它正在转发 HTTPS 请求。您需要在中间件中添加另一行 $request->setTrustedProxies( [ $request->getClientIp() ] ); 以信任 CloudFlare 发送的标头。这将停止重定向循环。 @manix 太棒了。这个周末我自己的项目刚刚解决了这个 HTTPS 问题——那些小东西会让你沮丧几个小时! 优秀的答案!只有一个细节:最好使用 301 重定向向 Google 表明这是一个永久性的移动。喜欢:return redirect()->secure($request->getRequestUri(), 301); 对于那些在负载均衡器或代理下的人可以将 secure() 更改为 $request->server('HTTP_X_FORWARDED_PROTO') != 'https' 这对我有用【参考方案2】:

类似于 manix 的答案,但在一个地方。强制HTTPS的中间件

namespace App\Http\Middleware;

use Closure;

use Illuminate\Http\Request;

class ForceHttps

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    
        if (!app()->environment('local')) 
            // for Proxies
            Request::setTrustedProxies([$request->getClientIp()], 
                Request::HEADER_X_FORWARDED_ALL);

            if (!$request->isSecure()) 
                return redirect()->secure($request->getRequestUri());
            
        

        return $next($request);
    

【讨论】:

请求必须是静态的吗? @jRhesk 可能没有,但请尝试随时修改答案 启动 Laravel 5.6 Request::setTrustedProxies 需要一个秒参数Request::setTrustedProxies([$request->getClientIp()], Request::HEADER_X_FORWARDED_ALL);【参考方案3】:

这适用于 Larave 5.2.x 及更高版本。如果您想选择通过 HTTPS 提供某些内容,而通过 HTTP 提供其他内容,这里有一个对我有用的解决方案。您可能想知道,为什么有人只想通过 HTTPS 提供一些内容?为什么不通过 HTTPS 提供所有服务?

尽管通过 HTTPS 为整个站点提供服务是完全可以的,但是通过 HTTPS 切断所有内容会在您的服务器上产生额外的开销。请记住,加密并不便宜。轻微的开销也会影响您的应用程序响应时间。您可能会争辩说商品硬件很便宜,而且影响可以忽略不计,但我离题了 :) 我不喜欢通过 https 提供带有图像等的营销内容大页面的想法。就这样吧。它类似于上面其他人建议使用中间件的方法,但它是一个完整的解决方案,允许您在 HTTP/HTTPS 之间来回切换。

首先创建一个中间件。

php artisan make:middleware ForceSSL

这就是您的中间件应有的样子。

<?php

namespace App\Http\Middleware;

use Closure;

class ForceSSL


    public function handle($request, Closure $next)
    

        if (!$request->secure()) 
            return redirect()->secure($request->getRequestUri());
        

        return $next($request);
    

请注意,我没有根据环境进行过滤,因为我为本地开发和生产设置了 HTTPS,因此不需要。

将以下内容添加到您的 routeMiddleware \App\Http\Kernel.php 以便您可以选择应该强制使用 SSL 的路由组。

    protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'can' => \Illuminate\Foundation\Http\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    'forceSSL' => \App\Http\Middleware\ForceSSL::class,
];

接下来,我想保护两个基本组登录/注册等以及 Auth 中间件背后的所有其他内容。

Route::group(array('middleware' => 'forceSSL'), function() 
/*user auth*/
Route::get('login', 'AuthController@showLogin');
Route::post('login', 'AuthController@doLogin');

// Password reset routes...
Route::get('password/reset/token', 'Auth\PasswordController@getReset');
Route::post('password/reset', 'Auth\PasswordController@postReset');

//other routes like signup etc

);


Route::group(['middleware' => ['auth','forceSSL']], function()
 
Route::get('dashboard', function()
    return view('app.dashboard');
);
Route::get('logout', 'AuthController@doLogout');

//other routes for your application
);

确认您的中间件已从控制台正确应用于您的路由。

php artisan route:list

现在您已经保护了应用程序的所有表单或敏感区域,现在的关键是使用您的视图模板来定义您的安全和公共(非 https)链接。

根据上面的示例,您将按如下方式呈现您的安全链接 -

<a href="secure_url('/login')">Login</a>
<a href="secure_url('/signup')">SignUp</a>

非安全链接可以呈现为

<a href="url('/aboutus',[],false)">About US</a></li>
<a href="url('/promotion',[],false)">Get the deal now!</a></li>

它的作用是呈现一个完全限定的 URL,例如 https://yourhost/login 和 http://yourhost/aboutus

如果您没有使用 http 呈现完全限定的 URL 并使用相对链接 url('/aboutus'),那么 https 将在用户访问安全站点后持续存在。

希望这会有所帮助!

【讨论】:

【参考方案4】:

另一个对我有用的选项,在 AppServiceProvider 中将此代码放在启动方法中:

\URL::forceScheme('https');

forceSchema('https')之前写的函数错误,它的forceScheme

【讨论】:

嘿,刚刚通过谷歌搜索找到了这个 - 请注意,在 5.4 中它是 \URL::forceScheme('https'); 在同一个文件中,也可以做if($this-&gt;app-&gt;environment() === 'production') $this-&gt;app['request']-&gt;server-&gt;set('HTTPS', true); 你是说\URL::forceScheme('https') 我很确定这只是为了建立链接。这不会强制用户使用 https,它只会提供以 https:// 开头的链接 是的,它发生在@WestonWatson。如果找到请分享解决方案【参考方案5】:

在IndexController.php中放

public function getIndex(Request $request)

    if ($request->server('HTTP_X_FORWARDED_PROTO') == 'http') 

        return redirect('/');
    

    return view('index');

在 AppServiceProvider.php 中放

public function boot()

    \URL::forceSchema('https');

在 AppServiceProvider.php 中,每个重定向都将转到 url https,对于 http 请求,我们需要一次重定向 所以在 IndexController.php 中我们只需要做一次重定向

【讨论】:

你能解释一下你的答案是如何解决这个问题的吗? 请将此解释添加到您的答案中。【参考方案6】:

仅仅使用.htaccess文件来实现https重定向呢?这应该放在项目根目录中(而不是公共文件夹)。您的服务器需要配置为指向项目根目录。

<IfModule mod_rewrite.c>
   RewriteEngine On
   # Force SSL
   RewriteCond %HTTPS !=on
   RewriteRule ^ https://%HTTP_HOST%REQUEST_URI [L,R=301]
   # Remove public folder form URL
   RewriteRule ^(.*)$ public/$1 [L]
</IfModule>

我将它用于 laravel 5.4(撰写此答案时的最新版本),但即使 laravel 更改或删除某些功能,它也应该继续适用于功能版本。

【讨论】:

Chrome 给我一个错误:重定向太多.. 好像这会造成循环 嗨,我已经更新了答案。确保将此 .htaccess 放在项目根目录中,并将服务器(apache 配置)指向项目根目录。 @MladenJanjetovic 您可以为这些环境使用不同的 htaccess 文件 @MladenJanjetovic 在应用程序中使用它肯定有其优势,但从效率和速度的角度来看,我认为在服务器配置中使用它是有利的,这样您就不必加载Laravel 只是为了重定向。可以通过使用重写条件检查域来实现单个版本化 .htaccess 中的环境特定配置,例如 RewriteCond %HTTP_HOST productiondomain\.com$ [NC] 我也更喜欢将它放在根目录的 .htaccess 中,正如 Crhis 所说,可以在这里完成特定于环境的设置,尽管不如 Mladen 的解决方案那么优雅。【参考方案7】:

或者,如果您使用的是 Apache,那么您可以使用 .htaccess 文件来强制您的 URL 使用 https 前缀。在 Laravel 5.4 上,我将以下几行添加到我的 .htaccess 文件中,它对我有用。

RewriteEngine On

RewriteCond %HTTPS !on
RewriteRule ^.*$ https://%HTTP_HOST%REQUEST_URI [L,R=301]

【讨论】:

如果您有多个环境(开发、阶段、生产)而不需要在所有环境上设置 SSL,这并不好。 @MladenJanjetovic 您可以在 dev 上使用 RewriteCond %HTTP_HOST !=localhost 来解决这个问题。 @Dan - 是的,但是您仍然需要为舞台、本地设置它(如果开发人员在本地开发中使用不同的 URL,即 .dev、.local、子域,这会更加复杂,...等等)。我更希望在应用程序中有这种逻辑。 这应该是正确的答案。如果该重写规则对 TLS 代理有影响,则无法对其进行测试,但这应该由 Web 服务器完成,而不是 php 代码【参考方案8】:

对于 laravel 5.4 使用这种格式来获取 https 重定向而不是 .htaccess

namespace App\Providers;

use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider

    public function boot()
    
        URL::forceScheme('https');
    

【讨论】:

只是为了澄清:1)这些更改应该在 app/Providers/AppServiceProvider.php 中完成; 2)这只是将应用程序内部生成的链接设置为使用SSL,并不强制您使用SSL 嗨,如果我点击一个将我发送到下一条路线的按钮,则不会通过此方法生成路线,它不会给我错误 404 这不是 https 重定向。但它允许提供 https:// 站点。如果您将其更改为 http://,它也会起作用。【参考方案9】:

上面的答案对我不起作用,但似乎 Deniz Turan 重写了 .htaccess 以与 Heroku 的负载均衡器一起工作: https://www.jcore.com/2017/01/29/force-https-on-heroku-using-htaccess/

RewriteEngine On
RewriteCond %HTTPS !=on
RewriteRule ^ https://%HTTP_HOST%REQUEST_URI [L,R=301]

【讨论】:

【参考方案10】:

这是在 Heroku 上的操作方法

要在您的测功机上强制 SSL 但不在本地强制 SSL,请在 public/ 中添加到 .htaccess 的末尾:

# Force https on heroku...
# Important fact: X-forwarded-Proto will exist at your heroku dyno but wont locally.
# Hence we want: "if x-forwarded exists && if its not https, then rewrite it":
RewriteCond %HTTP:X-Forwarded-Proto .
RewriteCond %HTTP:X-Forwarded-Proto !https
RewriteRule ^ https://%HTTP_HOST%REQUEST_URI [L,R=301]

您可以在本地机器上进行测试:

curl -H"X-Forwarded-Proto: http" http://your-local-sitename-here

这会将标头 X-forwarded 设置为将在 heroku 上采用的形式。

即它模拟 heroku dyno 如何看到请求。

您将在本地机器上收到此响应:

<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="https://tm3.localhost:8080/">here</a>.</p>
</body></html>

这是一个重定向。如果您如上所述设置 .htaccess,这就是 heroku 将回馈给客户的内容。但它不会在您的本地机器上发生,因为不会设置 X-forwarded(我们用上面的 curl 伪造了它以查看发生了什么)。

【讨论】:

【参考方案11】:

对于 Laravel 5.6,我必须稍微更改条件才能使其正常工作。

来自:

if (!$request->secure() && env('APP_ENV') === 'prod') 
return redirect()->secure($request->getRequestUri());

收件人:

if (empty($_SERVER['HTTPS']) && env('APP_ENV') === 'prod') 
return redirect()->secure($request->getRequestUri());

【讨论】:

【参考方案12】:

您可以使用 RewriteRule 强制 ssl 在 .htaccess 与您的 index.php 相同的文件夹中 请添加为图片附件,添加它在所有规则其他人之前

【讨论】:

【参考方案13】:

这对我有用。 我制作了一个自定义 php 代码以强制将其重定向到 https。 只需在 header.php 中包含此代码

<?php
if (isset($_SERVER['HTTPS']) &&
    ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
    isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
    $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') 
  $protocol = 'https://';

else 
  $protocol = 'http://';

$notssl = 'http://';
if($protocol==$notssl)
    $url = "https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";?>
    <script> 
    window.location.href ='<?php echo $url?>';
    </script> 
 <?php  ?>

【讨论】:

【参考方案14】:

如果您使用 CloudFlare,您只需创建一个页面规则以始终使用 HTTPS: 这会将每个 http:// 请求重定向到 https://

除此之外,您还必须在您的 \app\Providers\AppServiceProvider.php boot() 函数中添加类似这样的内容:

if (env('APP_ENV') === 'production' || env('APP_ENV') === 'dev') 
     \URL::forceScheme('https');

这将确保您应用中的每个链接/路径都使用 https:// 而不是 http://。

【讨论】:

【参考方案15】:

我正在添加这个替代方案,因为我在这个问题上遭受了很多痛苦。我尝试了所有不同的方法,但没有任何效果。所以,我想出了一个解决方法。这可能不是最好的解决方案,但它确实有效 -

仅供参考,我使用的是 Laravel 5.6

if (App::environment('production')) 
    URL::forceScheme('https');

生产

【讨论】:

同样,这不会将客户端重定向到 HTTPS,而是使用 HTTPS 提供文件。【参考方案16】:

我在 Laravel 5.6.28 中使用下一个中间件:

namespace App\Http\Middleware;

use App\Models\Unit;
use Closure;
use Illuminate\Http\Request;

class HttpsProtocol

    public function handle($request, Closure $next)
    
        $request->setTrustedProxies([$request->getClientIp()], Request::HEADER_X_FORWARDED_ALL);

        if (!$request->secure() && env('APP_ENV') === 'prod') 
            return redirect()->secure($request->getRequestUri());
        

        return $next($request);
    

【讨论】:

【参考方案17】:

最简单的方法是在应用程序级别。在文件中

app/Providers/AppServiceProvider.php

添加以下内容:

use Illuminate\Support\Facades\URL;

并在 boot() 方法中添加以下内容:

$this->app['request']->server->set('HTTPS', true);
URL::forceScheme('https');

这应该将所有请求重定向到应用程序级别的 https。

(注意:这个已经用 laravel 5.5 LTS 测试过了)

【讨论】:

【参考方案18】:

有点不同的方法,在 Laravel 5.7 中测试过

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Str;

class ForceHttps

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
        
        if ( !$request->secure() && Str::startsWith(config('app.url'), 'https://') ) 
            return redirect()->secure($request->getRequestUri());
        
        return $next($request);
    

PS。代码根据@matthias-lill 的 cmets 更新。

【讨论】:

也适用于 Laravel 6。 使用 env() 函数将不适用于缓存的配置文件。这应该指向config('app.url')。另外,Laravel 自带了一个非常方便的字符串函数Str::startsWith(config('app.url'), 'https://')【参考方案19】:

这对我来说在 Laravel 7.x 中使用 3 个简单步骤 使用中间件:

1) 使用命令php artisan make:middleware ForceSSL生成中间件

中间件

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\App;

class ForceSSL

    public function handle($request, Closure $next)
    
        if (!$request->secure() && App::environment() === 'production') 
            return redirect()->secure($request->getRequestUri());
        

        return $next($request);
    

2) 在内核文件routeMiddleware中注册中间件

内核

protected $routeMiddleware = [
    //...
    'ssl' => \App\Http\Middleware\ForceSSL::class,
];

3) 在你的路线中使用它

路线

Route::middleware('ssl')->group(function() 
    // All your routes here

);

这里是关于middlewares的完整文档

==========================

.HTACCESS 方法

如果您更喜欢使用.htaccess 文件,可以使用以下代码:

<IfModule mod_rewrite.c>
    RewriteEngine On 
    RewriteCond %SERVER_PORT 80 
    RewriteRule ^(.*)$ https://yourdomain.com/$1 [R,L]
</IfModule>

问候!

【讨论】:

【参考方案20】:

您可以简单地转到 app -> Providers -> AppServiceProvider.php

添加两行

使用 Illuminate\Support\Facades\URL;

URL::forceScheme('https');

如下代码所示:

use Illuminate\Support\Facades\URL;

class AppServiceProvider extends ServiceProvider

   public function boot()
    
        URL::forceScheme('https');

       // any other codes here, does not matter.
    

【讨论】:

【参考方案21】:

在 Laravel 5.1 中,我使用了: 文件:app\Providers\AppServiceProvider.php

public function boot()

    if ($this->isSecure()) 
        \URL::forceSchema('https');
    


public function isSecure()

    $isSecure = false;
    if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') 
        $isSecure = true;
     elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') 
        $isSecure = true;
    

    return $isSecure;

注意:使用forceSchema,而不是forceScheme

【讨论】:

【参考方案22】:

使用 Laravel 重定向到 HTTPS 的最简单方法是使用 .htaccess

所以您只需将以下行添加到您的 .htaccess 文件中即可。

RewriteEngine On
RewriteCond %HTTPS !=on
RewriteRule ^ https://%HTTP_HOST%REQUEST_URI [L,R=301]

确保在 .htaccess 文件中找到的现有(*default) 代码之前添加它,否则 HTTPS 将不起作用。 这是因为现有(默认)代码已经处理了一个重定向,该重定向将所有流量重定向到主页,然后路由根据您的 URL 接管该主页

所以把代码放在首位意味着 .htaccess 将在路由接管之前首先将所有流量重定向到 https

【讨论】:

【参考方案23】:

我发现这对我有用。首先将这段代码复制到.htaccess 文件中。

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %SERVER_PORT !^443$
RewriteRule ^(.*)$ https://%HTTP_HOST%REQUEST_URI [L,R=301]
</IfModule>

【讨论】:

欢迎来到 DO 并感谢您的贡献。 SO通常不鼓励没有上下文的仅代码答案。大多数质量答案都包括一个解释,以突出解决 OP 问题的解决方案的特定部分以及它如何/为什么这样做。随着未来的访问者从您的帖子中学到一些可以应用于他们自己的编码问题的东西,大多数支持都会随着时间的推移而累积。请考虑编辑以通过一些解释,甚至是文档链接来提高您帖子的长期价值。

以上是关于Laravel 5 - 重定向到 HTTPS的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5 - 发布路由上的 htaccess HTTPS 重定向不起作用。

Laravel 5 中间件 https 上的 aws ec2 重定向循环

重定向进入空白页面而不重定向到路由 Laravel 5.8

如何将客人重定向到登录页面,在laravel 5.2登录后重定向回来[关闭]

Laravel 将 Http 重定向到 Https

如何在 Laravel 4 中将一个路由重定向到 HTTPS,将所有其他路由重定向到 HTTP?