Laravel 5 - redirigir a HTTPS


127

Trabajando en mi primer proyecto de Laravel 5 y no estoy seguro de dónde o cómo colocar la lógica para forzar HTTPS en mi aplicación. El factor decisivo aquí es que hay muchos dominios que apuntan a la aplicación y solo dos de cada tres usan SSL (el tercero es un dominio de respaldo, una larga historia). Así que me gustaría manejar esto en la lógica de mi aplicación en lugar de .htaccess.

En Laravel 4.2 logré la redirección con este código, ubicado en filters.php:

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

Estoy pensando que Middleware es donde se debería implementar algo como esto, pero no puedo resolverlo usándolo.

¡Gracias!

ACTUALIZAR

Si está utilizando Cloudflare como yo, esto se logra agregando una nueva regla de página en su panel de control.


Entonces, ¿qué pasa con el tercer dominio? Si fuerza https en todas las rutas, ¿seguirá funcionando el tercer dominio?
Laurence

Detectando eso con$_SERVER['HTTP_HOST']
NightMICU

¿Cuánto tiempo tomó para que la regla de la página cloudflare entrara en vigencia?
CodeGuru

Oh, tuve que encender el proxy en la configuración de DNS ¡jaja!
CodeGuru

Respuestas:


253

Puede hacer que funcione con una clase de Middleware. Déjame darte una idea.

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

Luego, aplique este middleware a cada solicitud agregando la configuración de la regla en el Kernel.phparchivo, así:

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'       

];

En el ejemplo anterior, el middleware redirigirá cada solicitud a https si:

  1. La solicitud actual viene sin protocolo seguro (http)
  2. Si su entorno es igual a production. Entonces, simplemente ajuste la configuración de acuerdo con sus preferencias.

Cloudflare

Estoy usando este código en un entorno de producción con un SSL WildCard y el código funciona correctamente. Si lo elimino && App::environment() === 'production'y lo pruebo en localhost, la redirección también funciona. Entonces, tener o no un SSL instalado no es el problema. Parece que necesita prestar mucha atención a su capa de Cloudflare para ser redirigido al protocolo Https.

Editar 23/03/2015

Gracias a @Adam Linkla sugerencia de: es probable que sea causado por los encabezados que está pasando Cloudflare. Es probable que CloudFlare llegue a su servidor a través de HTTP y pase un encabezado X-Fordered-Proto que declara que está reenviando una solicitud HTTPS. Necesita agregar otra línea en su Middleware que diga ...

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

... confiar en los encabezados que envía CloudFlare. Esto detendrá el ciclo de redireccionamiento

Editar 27/09/2016 - Laravel v5.3

Solo necesita agregar la clase de middleware al webgrupo en 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

    ],
];

Recuerde que el webgrupo se aplica a todas las rutas de forma predeterminada, por lo que no es necesario establecerlo webexplícitamente en las rutas ni en los controladores.

Editar 23/08/2018 - Laravel v5.7

  • Para redirigir una solicitud en función del entorno que puede utilizar App::environment() === 'production'. Para la versión anterior era env('APP_ENV') === 'production'.
  • El uso en \URL::forceScheme('https');realidad no redirige. Simplemente crea enlaces con https://una vez que se renderiza el sitio web.

5
Parece que esto me está dando un bucle de redireccionamiento ... aunque parece que debería funcionar. No sé si hay alguna diferencia, pero estamos usando un SSL de Cloudflare. Pero no creo que eso cambie la simple redirección.
NightMICU

3
@NightMICU No estoy seguro de si ha resuelto el problema con la redirección, pero es probable que sea causado por los encabezados que pasa Cloudflare. Es probable que CloudFlare llegue a su servidor a través de HTTP y pase un encabezado X-Fordered-Proto que declara que está reenviando una solicitud HTTPS. Necesita agregar otra línea en su Middleware que diga $request->setTrustedProxies( [ $request->getClientIp() ] );que confíe en los encabezados que CloudFlare está enviando. Esto detendrá el ciclo de redireccionamiento.
Adam Link

2
@manix Impresionante. Acabo de pasar por este problema HTTPS este fin de semana con mi propio proyecto: ¡esas pequeñas cosas te frustrarán durante horas!
Adam Link

8
¡Excelente respuesta! Solo un detalle: es mejor usar un redireccionamiento 301 para indicarle a Google que es un movimiento permanente. Me gusta:return redirect()->secure($request->getRequestUri(), 301);
adriaroca

4
para aquellos que están bajo el balanceador de carga o proxy pueden cambiar a secure () para que $request->server('HTTP_X_FORWARDED_PROTO') != 'https'esto funcione para mí
Shiro

63

Otra opción que funcionó para mí, en AppServiceProvider coloque este código en el método de arranque:

\URL::forceScheme('https');

La función escrita antes de forceSchema ('https') era incorrecta, su forceScheme


16
Oye, acabo de encontrar esto buscando en Google; ten en cuenta que en 5.4 es\URL::forceScheme('https');
dev

5
En el mismo archivo, también puede hacerloif($this->app->environment() === 'production'){ $this->app['request']->server->set('HTTPS', true); }
Rory

2
quiso decir\URL::forceScheme('https')
Ernest Okot

17
Estoy bastante seguro de que esto es solo para crear enlaces. Esto no obligará al usuario a https, solo servirá enlaces precedidos de https: //
Weston Watson

sí, sucedió @WestonWatson. comparta amablemente la solución si la encuentra
Harat

33

Alternativamente, si está usando Apache, puede usar el .htaccessarchivo para hacer que sus URL usen httpsprefijo. En Laravel 5.4, agregué las siguientes líneas a mi .htaccessarchivo y funcionó para mí.

RewriteEngine On

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

2
Esto no es bueno si tiene varios entornos (desarrollo, etapa, producción) de los que necesita para configurar SSL en todos ellos.
Mladen Janjetovic

@MladenJanjetovic puede usar RewriteCond %{HTTP_HOST} !=localhosten dev para evitar esto.
Dan

3
@Dan - Sí, pero aún tendrá que configurarlo para el escenario, local (y esto es más complicado si los desarrolladores usan diferentes URL en el desarrollo local, es decir, .dev, .local, subdominios, ... etc.). Preferiría tener ese tipo de lógica en la aplicación.
Mladen Janjetovic

16

para laravel 5.4 use este formato para obtener la redirección https en lugar de .htaccess

namespace App\Providers;

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

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        URL::forceScheme('https');
    }
}

15
Solo para aclarar: 1) estos cambios deben realizarse en app / Providers / AppServiceProvider.php; 2) esto es solo para configurar los enlaces generados dentro de la aplicación para usar SSL, no lo obliga a usar SSL
phoenix

Hola, la ruta no se genera con este método si hago clic en un botón que me envía a la siguiente ruta, no me da el error 404
Saket Sinha

Esta no es una redirección https. Pero permite la publicación de https: // site. Si lo cambia a http: //, también servirá.
franco

12

Similar a la respuesta de manix pero en un solo lugar. Middleware para forzar 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);
    }
}

¿La solicitud debe ser estática?
GFxJamal

@jRhesk probablemente no, pero intente modificar la respuesta y siéntase libre de modificar la respuesta
Mladen Janjetovic

8

Esto es para Larave 5.2.xy versiones posteriores. Si desea tener una opción para servir algún contenido a través de HTTPS y otros a través de HTTP, aquí hay una solución que funcionó para mí. Quizás se pregunte, ¿por qué alguien querría ofrecer solo algunos contenidos a través de HTTPS? ¿Por qué no servir todo a través de HTTPS?

Aunque está totalmente bien servir todo el sitio a través de HTTPS, cortar todo a través de HTTPS tiene una sobrecarga adicional en su servidor. Recuerde que el cifrado no es barato. La ligera sobrecarga también tiene un impacto en el tiempo de respuesta de su aplicación. Se podría argumentar que el hardware básico es barato y el impacto es insignificante, pero estoy divagando :) No me gusta la idea de ofrecer contenido de marketing en páginas grandes con imágenes, etc. a través de https. Así que aquí va. Es similar a lo que otros han sugerido anteriormente usando middleware, pero es una solución completa que le permite alternar entre HTTP / HTTPS.

Primero cree un middleware.

php artisan make:middleware ForceSSL

Este es el aspecto que debería tener su middleware.

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

Tenga en cuenta que no estoy filtrando en función del entorno porque tengo una configuración HTTPS tanto para el desarrollo local como para la producción, por lo que no es necesario.

Agregue lo siguiente a su routeMiddleware \ App \ Http \ Kernel.php para que pueda elegir qué grupo de rutas debe forzar 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,
];

A continuación, me gustaría asegurar el inicio de sesión / registro de dos grupos básicos, etc. y todo lo demás detrás del middleware 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
});

Confirme que sus middlewares se hayan aplicado a sus rutas correctamente desde la consola.

php artisan route:list

Ahora que ha asegurado todos los formularios o áreas sensibles de su aplicación, la clave ahora es usar su plantilla de vista para definir sus enlaces seguros y públicos (no https).

Según el ejemplo anterior, representaría sus enlaces seguros de la siguiente manera:

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

Los enlaces no seguros se pueden representar como

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

Lo que hace es generar una URL completamente calificada como https: // yourhost / login y http: // yourhost / aboutus

Si no representaba una URL completamente calificada con http y usaba una URL de enlace relativa ('/ aboutus'), https persistirá después de que un usuario visite un sitio seguro.

¡Espero que esto ayude!


7

¿Qué hay de usar el archivo .htaccess para lograr la redirección https? Esto debe colocarse en la raíz del proyecto (no en la carpeta pública). Su servidor debe estar configurado para apuntar al directorio raíz del proyecto.

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

Utilizo esto para laravel 5.4 (la última versión al momento de escribir esta respuesta) pero debería seguir funcionando para las versiones de funciones incluso si laravel cambia o elimina alguna funcionalidad.


Chrome me da un error: Demasiados redireccionamientos ... Parece que esto hace un bucle
phoenix

Hola, he actualizado la respuesta. Asegúrese de poner este .htaccess en el directorio raíz del proyecto y apunte su servidor (configuración de apache) a la raíz del proyecto.
Maulik Gangani

1
@MladenJanjetovic puede tener diferentes archivos htaccess para estos entornos
Burgi

1
@MladenJanjetovic tenerlo en la aplicación definitivamente tiene sus ventajas, pero desde una perspectiva de eficiencia y velocidad, diría que es ventajoso tener esto en la configuración del servidor para que no tenga que cargar Laravel solo para una redirección. Las configuraciones específicas del entorno en un .htaccess con una sola versión se pueden lograr utilizando una condición de reescritura para verificar el dominio, algo comoRewriteCond %{HTTP_HOST} productiondomain\.com$ [NC]
Chris

1
Yo también prefiero colocar esto en .htaccess en el directorio raíz y, como dijo Crhis, las configuraciones específicas del entorno se pueden lograr aquí, aunque con un poco menos de elegancia que en la solución de Mladen.
jovan

6

Puede usar RewriteRule para forzar ssl en .htaccess en la misma carpeta con su index.php
Por favor, agregue como imagen adjunta, agréguela antes que todas las reglas de los demás configuración ssl .htaccess


3

en IndexController.php poner

public function getIndex(Request $request)
{
    if ($request->server('HTTP_X_FORWARDED_PROTO') == 'http') {

        return redirect('/');
    }

    return view('index');
}

en AppServiceProvider.php poner

public function boot()
{
    \URL::forceSchema('https');

}

En AppServiceProvider.php, cada redireccionamiento irá a la url https y para la solicitud http necesitamos redireccionar una vez, por lo que en IndexController.php Solo tenemos que redirigir una vez


¿Puede explicar cómo su respuesta resuelve la pregunta?
soundslikeodd

Agregue esta explicación a su respuesta.
soundslikeodd


3

Estoy agregando esta alternativa porque sufrí mucho con este problema. Intenté todas las formas diferentes y nada funcionó. Entonces, se me ocurrió una solución alternativa. Puede que no sea la mejor solución, pero funciona.

FYI, estoy usando Laravel 5.6

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

producción <: debe reemplazarse con el valor APP_ENV en su archivo .env


2

He aquí cómo hacerlo en Heroku

Para forzar SSL en sus dynos pero no localmente, agregue al final de su .htaccess en 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]

Puede probar esto en su máquina local con:

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

Eso establece el encabezado X-reenviado a la forma que tomará en heroku.

es decir, simula cómo un heroku dyno verá una solicitud.

Obtendrá esta respuesta en su máquina local:

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

Eso es una redirección. Eso es lo que heroku le devolverá a un cliente si configura el .htaccess como se indicó anteriormente. Pero no sucede en su máquina local porque X-reenviado no se configurará (lo simulamos con curl arriba para ver qué estaba sucediendo).


2

Si está utilizando CloudFlare, puede crear una regla de página para usar siempre HTTPS: Forzar SSL Cloudflare esto redirigirá cada solicitud http: // a https: //

Además de eso, también tendría que agregar algo como esto a su función \ app \ Providers \ AppServiceProvider.php boot ():

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

Esto garantizaría que todos los enlaces o rutas de su aplicación utilicen https: // en lugar de http: //.


2

Un enfoque un poco diferente, probado en 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);
    }
}

PD. Código actualizado basado en los comentarios de @ matthias-lill.


1
También funciona en Laravel 6.
Rubens

1
el uso de las funciones env () no funcionará con archivos de configuración almacenados en caché. Esto debería apuntar a en su config('app.url')lugar. Además, Laravel viene con una función de cadena muy útil Str::startsWith(config('app.url'), 'https://').
Matthias Lill

1

Para Laravel 5.6, tuve que cambiar un poco la condición para que funcionara.

de:

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

A:

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

1

Esto funcionó para mí. Hice un código php personalizado para forzar la redirección a https. Solo incluye este código en el 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

Estoy usando en Laravel 5.6.28 siguiente middleware:

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

La forma más sencilla sería a nivel de aplicación. En el archivo

app/Providers/AppServiceProvider.php

agregue lo siguiente:

use Illuminate\Support\Facades\URL;

y en el método boot () agregue lo siguiente:

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

Esto debería redirigir todas las solicitudes a https en el nivel de la aplicación.

(Nota: esto ha sido probado con laravel 5.5 LTS)


1

Puede simplemente ir a la aplicación -> Proveedores -> AppServiceProvider.php

agregar dos líneas

use Illuminate \ Support \ Facades \ URL;

URL :: forceScheme ('https');

como se muestra en los siguientes códigos:

use Illuminate\Support\Facades\URL;

class AppServiceProvider extends ServiceProvider
{
   public function boot()
    {
        URL::forceScheme('https');

       // any other codes here, does not matter.
    }

0

Esto me funciona en Laravel 7.x en 3 sencillos pasos usando un middleware:

1) Genere el middleware con el comando php artisan make:middleware ForceSSL

Middleware

<?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) Registre el middleware en el routeMiddlewarearchivo Kernel interno

Núcleo

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

3) Úsalo en tus rutas

Rutas

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

});

aquí la documentación completa sobre middlewares

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

.HTACCESS Método

Si prefiere usar un .htaccessarchivo, puede usar el siguiente código:

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

¡Saludos!

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.