Laravel 5 - 重定向到 HTTPS

Laravel 5 - redirect to HTTPS

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

在 Laravel 4.2 中,我使用位于 filters.php:

中的代码完成了重定向
App::before(function($request)
{
    if( ! Request::secure())
    {
        return Redirect::secure(Request::path());
    }
});

我在想中间件是应该实现这样的东西的地方,但我不太明白如何使用它。

谢谢!

更新

如果您像我一样使用 Cloudflare,可以通过在控制面板中添加新的 Page Rule 来实现。

您可以让它与中间件一起工作 class。让我给你一个想法。

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:

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

Cloudflare

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

编辑 23/03/2015

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

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

...相信 headers CloudFlare 正在发送。这将停止重定向循环

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

只需要将中间件class添加到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

    ],
];

Remember that web group is applied to every route by default, so you do not need to set web explicitly in routes nor controllers.

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

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

与 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);
    }
}

这适用于 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,
];

接下来,我想保护两个基本组 login/signup 等以及 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

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

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

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

非安全 link 可以呈现为

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

它所做的是呈现完全限定的 URL,例如 https://yourhost/login and http://yourhost/aboutus

如果您没有使用 http 呈现完全合格的 URL 并使用相对 link url('/aboutus') 那么 https 将在用户访问安全站点后持续存在.

希望对您有所帮助!

另一个对我有用的选项,在 AppServiceProvider 中将这段代码放在引导方法中:

\URL::forceScheme('https');

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

在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 我们只需要做一次重定向

只用.htaccess文件实现https重定向怎么样?这应该放在项目根目录中(而不是 public 文件夹中)。您的服务器需要配置为指向项目根目录。

<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/ [L]
</IfModule>

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

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

RewriteEngine On

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

对于 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');
    }
}

上面的答案对我不起作用,但 Deniz Turan 似乎以一种适用于 Heroku 负载均衡器的方式重写了 .htaccess: 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]

这是在 Heroku 上的操作方法

要在您的测功机上而非本地强制使用 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

这会将 header 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 伪造它以查看发生了什么)。

对于 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());
}

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

这对我很有效。 我制作了自定义 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 } ?>

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

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

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

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

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

FYI, I am using Laravel 5.6

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

production <- 它应该替换为 .env 文件中的 APP_ENV 值

我正在 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);
    }
}

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

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 测试)

略有不同的方法,已在 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 的评论更新。

这对我有用 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/ [R,L]
</IfModule>

此致!

您可以简单地转到 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.
    }

在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

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

所以你所要做的就是将以下行添加到你的 .htaccess 文件中,你就可以开始了。

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

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

接管

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

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

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