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, как и я, это можно сделать, добавив новое правило страницы в панель управления.

NightMICU
источник
Так что же происходит с 3-м доменом? Если вы включите https на всех маршрутах - будет ли работать 3-й домен?
Лоуренс
Обнаружение этого с помощью$_SERVER['HTTP_HOST']
NightMICU
Сколько времени понадобилось, чтобы правило страницы
CodeGuru
О, мне пришлось включить прокси в настройках DNS, ха-ха!
CodeGuru

Ответы:

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. Итак, просто отрегулируйте настройки в соответствии со своими предпочтениями.

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

Просто нужно добавить класс промежуточного программного обеспечения в 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://после отображения веб-сайта.
Manix
источник
5
Кажется, это дает мне цикл перенаправления ... похоже, что он должен работать. Я не знаю, имеет ли это значение, но мы используем SSL Cloudflare. Но я не думаю, что это изменит простой редирект.
NightMICU,
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
для тех, кто под балансировщиком нагрузки или прокси может перейти на secure (), $request->server('HTTP_X_FORWARDED_PROTO') != 'https'для меня это работает
Широ,
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); }
Рори
2
Вы имели в виду\URL::forceScheme('https')
Эрнест Окот
17
Я почти уверен, что это только для построения ссылок. Это не заставит пользователя перейти на https, он будет обслуживать только ссылки с добавлением https: //
Weston Watson
да, это случилось @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
Это нехорошо, если у вас несколько сред (dev, stage, production), чем вам нужно установить SSL на всех из них.
Mladen Janjetovic
@MladenJanjetovic вы можете использовать RewriteCond %{HTTP_HOST} !=localhostна dev, чтобы обойти это.
Дэн
3
@Dan - Да, но вам все равно придется настроить его для stage, local (и это сложнее, если разработчики используют разные URL-адреса при локальной разработке, например .dev, .local, subdomains, ... и т. Д.). Я бы предпочел иметь такую ​​логику в приложении.
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
Сакет Синха
Это не перенаправление 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);
    }
}
Младен Янетович
источник
запрос должен быть статическим?
GFxJamal 04
@jRhesk, вероятно, нет, но, пожалуйста, попробуйте и не стесняйтесь изменить ответ
Младен Джанжетович
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 / login и http: // yourhost / aboutus.

Если вы не отображали полностью определенный URL-адрес с http и использовали относительный URL-адрес ссылки ('/ aboutus'), тогда https будет сохраняться после посещения пользователем защищенного сайта.

Надеюсь это поможет!

на-98
источник
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 выдает ошибку: слишком много перенаправлений. Похоже, это создает цикл
phoenix
Привет, я обновил ответ. Убедитесь, что вы поместили этот .htaccess в корневой каталог проекта и указали свой сервер (конфигурация apache) на корень проекта.
Маулик Гангани 09
1
@MladenJanjetovic, у вас могут быть разные файлы htaccess для этих сред
Берги,
1
@MladenJanjetovic, имея его в приложении, определенно имеет свои преимущества, но с точки зрения эффективности и скорости я бы сказал, что выгодно иметь это в конфигурации сервера, чтобы вам не приходилось загружать Laravel только для перенаправления. Конфигурации, зависящие от среды в одном версионном .htaccess, могут быть достигнуты с помощью условия перезаписи для проверки домена, что-то вродеRewriteCond %{HTTP_HOST} productiondomain\.com$ [NC]
Крис
1
Я тоже предпочитаю помещать это в .htaccess в корневой каталог, и, как сказал Crhis, здесь можно выполнить настройки для конкретной среды, хотя и немного менее элегантно, чем в решении Mladen.
Йован
6

Вы можете использовать RewriteRule для принудительного использования ssl в .htaccess в той же папке, что и ваш index.php.
Пожалуйста, добавьте как прикрепленное изображение, добавьте его перед всеми другими правилами установка ssl .htaccess

Куи Ле
источник
4

Я добавляю эту альтернативу, так как я много страдал от этой проблемы. Я перепробовал все по-разному, но ничего не вышло. Итак, я придумал обходной путь. Возможно, это не лучшее решение, но оно работает -

К вашему сведению, я использую Laravel 5.6

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

production <- его следует заменить значением APP_ENV в вашем файле .env.

thebrownkid
источник
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 нам нужно сделать один раз перенаправление

Артур Карамян
источник
Вы можете объяснить, как ваш ответ решает вопрос?
звучит как неординарно 02
Пожалуйста, добавьте это объяснение к своему ответу.
звучит как необычно, 04
3

Приведенные выше ответы не сработали для меня, но похоже, что Дениз Туран переписал .htaccess таким образом, чтобы он работал с балансировщиком нагрузки Heroku здесь: https://www.jcore.com/2017/01/29/force-https -он-Heroku-помощь-Htaccess /

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Фил
источник
2

Вот как это сделать на Heroku

Чтобы принудительно использовать SSL на ваших дино, но не локально, добавьте в конец вашего .htaccess в public /:

# 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 увидит запрос.

Вы получите этот ответ на своем локальном компьютере:

<!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>

Это перенаправление. Это то, что heroku вернет клиенту, если вы установите .htaccess, как указано выше. Но этого не происходит на вашем локальном компьютере, потому что X-forwarded не будет установлен (мы подделали это с помощью curl выше, чтобы увидеть, что происходит).

mwal
источник
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: //.

butaminas
источник
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://').
Маттиас Лилл,
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());
}
Priyank
источник
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);
    }
}
fomvasss
источник
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

Вы можете просто перейти в 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.
    }
Врия Мохаммед
источник
0

Для меня это работает в 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

});

здесь полная документация по промежуточному программному обеспечению

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

.HTACCESS Метод

Если вы предпочитаете использовать .htaccessфайл, вы можете использовать следующий код:

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

С уважением!

Радамес Э. Эрнандес
источник