首页 文章

Laravel 5 - 重定向到HTTPS

提问于
浏览
79

在我的第一个Laravel 5项目上工作,不确定在我的应用程序中强制使用HTTPS的逻辑位置和方式 . 这里的关键是有许多域指向应用程序,只有三分之二使用SSL(第三个是后备域,长篇故事) . 所以我想在我的应用程序逻辑而不是.htaccess中处理这个问题 .

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

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

我认为中间件应该在这样的地方实现,但我不能完全理解这一点 .

谢谢!

UPDATE

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

18 回答

  • 42

    如何使用 .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更改或删除某些功能,它仍应继续用于功能版本 .

  • 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协议 .

    编辑23/03/2015

    感谢 @Adam Link 的建议:它可能是由Cloudflare传递的标头引起的 . 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:// 构建链接 .

  • 1

    另一个适合我的选项,在AppServiceProvider中将此代码放在boot方法中:

    \URL::forceScheme('https');
    

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

  • 9

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

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

    对于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');
        }
    }
    
  • 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()]);
    
                if (!$request->isSecure()) {
                    return redirect()->secure($request->getRequestUri());
                }
            }
    
            return $next($request);
        }
    }
    
  • 5

    这适用于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并使用相对链接网址('/ aboutus'),那么https将在用户访问安全网站后保留 .

    希望这可以帮助!

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

  • 1

    上面的答案在这里没有't work for me, but it appears that Deniz Turan rewrote the .htaccess in a way that works with 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]
    
  • 11

    您可以使用RewriteRule强制ssl在.htaccess与index.php相同的文件夹中
    请添加为图片附加,添加 before all rule others
    setting ssl .htaccess

  • 1

    Here's how to do it on Heroku

    要在您的dynos上强制使用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
    

    这将 Headers X转发到它将在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伪造它以查看发生了什么) .

  • 3

    对于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 } ?>
    
  • 0

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

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

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

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

  • 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);
        }
    }
    
  • 2

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

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

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

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

  • 0

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

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

  • 0

    在Laravel 5.7中测试的一种不同的方法

    <?php
    
    namespace App\Http\Middleware;
    
    use Closure;
    
    class ForceHttps
    {
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            $app_url = env('APP_URL');
    
            if ( !$request->secure() && substr($app_url, 0, 8) === 'https://' ) {
                return redirect()->secure($request->getRequestUri());
            }
            return $next($request);
        }
    }
    

相关问题