Laravel 5-重定向到HTTPS


127

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

在Laravel 4.2中,我使用以下代码完成了重定向filters.php

App::before(function($request)
{
    if( ! Request::secure())
    {
        return Redirect::secure(Request::path());
    }
});

我在想应该在中间件上实现类似这样的东西,但是我不能完全理解使用它。

谢谢!

更新

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


那么第三域会发生什么呢?如果您在所有路径上强制使用https-第三域将继续正常工作吗?
劳伦斯2015年

使用$_SERVER['HTTP_HOST']
NightMICU

cloudflare页面规则生效需要多长时间
CodeGuru

哦,我必须在DNS设置中打开代理服务器哈哈!
CodeGuru

Answers:


253

您可以使其与Middleware类一起使用。让我给你个主意。

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。因此,只需根据您的喜好调整设置。

云耀斑

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

编辑23/03/2015

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

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

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

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

只需要将中间件类添加到以下web组中kernel.php file

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://网站建成后,它仅与之建立链接。

5
这似乎给了我一个重定向循环……不过看起来应该可以。我不知道这有什么区别,但是我们正在使用Cloudflare SSL。但是我认为这不会改变简单的重定向。
NightMICU'Feb 9'15

3
@NightMICU我不确定您是否通过重定向解决了该问题,但这很可能是由Cloudflare传递的标头引起的。CloudFlare可能通过HTTP命中了您的服务器,并传递了X-Forwarded-Proto标头,该标头声明其正在转发HTTPS请求。您需要在中间件中添加另一行,$request->setTrustedProxies( [ $request->getClientIp() ] );以信任CloudFlare发送的标头。这将停止重定向循环。
亚当·林克

2
@manix太棒了。刚刚在本周末通过我自己的项目解决了HTTPS问题-那些小东西会让你沮丧几个小时!
亚当·林克

8
很好的答案!仅需一个细节:最好使用301重定向向Google表示这是永久性的动作。像:return redirect()->secure($request->getRequestUri(), 301);
阿德里亚罗卡(Adriaroca)

4
对于那些在负载均衡器或代理服务器下可以更改$request->server('HTTP_X_FORWARDED_PROTO') != 'https'为该方法的secure()的我来说
Shiro

63

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

\URL::forceScheme('https');

在forceSchema('https')之前编写的函数是错误的,其forceScheme


16
嘿,刚通过谷歌搜索发现了这一点-请注意,在5.4中\URL::forceScheme('https');
dev

5
在同一文件中,您也可以执行if($this->app->environment() === 'production'){ $this->app['request']->server->set('HTTPS', true); }
罗里(Rory)

2
您的意思是\URL::forceScheme('https')

17
我很确定这仅用于构建链接。这不会强制用户使用https,则只会以https前缀链接://
韦斯顿沃森

是的,发生了@WestonWatson。如有发现,请与我们分享解决方案
Harat

33

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

RewriteEngine On

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

2
如果您有多个环境(开发,阶段,生产),那么这比在所有环境上设置SSL都不好。
Mladen Janjetovic

您可以RewriteCond %{HTTP_HOST} !=localhost在开发人员上使用@MladenJanjetovic 来解决此问题。

3
@Dan-是的,但是您仍然必须将其设置为本地阶段(如果开发人员在本地开发中使用不同的URL,例如.dev,.local,子域等,则情况会更加复杂)。我希望在应用程序中使用这种逻辑。
Mladen Janjetovic

16

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

15
只是为了澄清一下:1)这些更改应在app / Providers / AppServiceProvider.php中完成;2)这仅是将应用程序内部生成的链接设置为使用SSL,它不会强制您使用SSL
凤凰城

嗨,如果我单击将我发送到下一条路线的按钮,则它没有给我错误404
Saket Sinha

这不是https重定向。但它允许提供https://网站。如果您将其更改为http://,则也可以使用。
法郎

12

与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()]);

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

        return $next($request);
    }
}

Request是否必须是静态的?
GFxJamal

@jRhesk它可能不会,但是请尝试随时修改答案
Mladen Janjetovic

8

这适用于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 / loginhttp:// yourhost / aboutus

如果您未使用http渲染完全限定的URL,而是使用相对链接url('/ aboutus'),则在用户访问安全站点后https仍然存在。

希望这可以帮助!


7

仅使用.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配置)指向项目根目录。
Maulik Gangani

1
@MladenJanjetovic您可以为这些环境使用不同的htaccess文件
Burgi

1
@MladenJanjetovic在应用程序中拥有它绝对有其优势,但是从效率和速度的角度来看,在服务器配置中拥有它是有利的,这样您就不必为重定向而加载Laravel。可以通过使用重写条件来检查域来实现单个版本的.htaccess中特定于环境的配置,例如RewriteCond %{HTTP_HOST} productiondomain\.com$ [NC]
Chris

1
我也更喜欢将其放在根目录中的.htaccess中,正如Crhis所说,可以在此处完成特定于环境的设置,尽管比Mladen的解决方案要优雅一些。
jovan

6

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


3

在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中,我们只需要执行一次重定向


您能解释一下您的答案如何解决问题吗?
听起来像是

请将此说明添加到您的答案中。
听起来像是


3

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

仅供参考,我正在使用Laravel 5.6

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

生产<-应该用.env文件中的APP_ENV值替换


2

这是在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

这会将标头X转发设置为它将在heroku上采用的形式。

即,它模拟heroku测功机如何看到请求。

您将在本地计算机上收到以下响应:

<!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转发(我们用上面的curl伪造了它以查看发生了什么)。


2

如果您使用的是CloudFlare,则可以创建一个页面规则以始终使用HTTPS: 强制SSL Cloudflare 这会将每个http://请求重定向到https://

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

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

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


2

稍有不同的方法,在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的评论进行了更新。


1
同样适用于Laravel 6。
鲁本斯

1
使用env()函数不适用于缓存的配置文件。这应该指向config('app.url')。而且,Laravel具有非常方便的字符串函数Str::startsWith(config('app.url'), 'https://')
Matthias Lill

1

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

1

这为我解决了。我做了一个自定义的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 } ?>

1

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

1

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

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进行了测试)


1

你可以简单的进入应用程序 - > 供应商 - > 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.
    }

0

通过3个简单的步骤即可Laravel 7.x中为我工作使用中间件:

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

});

这里是有关中间件的完整文档

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

.HTACCESS方法

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

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

问候!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.