Laravel 5 - reindirizza a HTTPS


127

Sto lavorando al mio primo progetto Laravel 5 e non sono sicuro di dove o come posizionare la logica per forzare HTTPS sulla mia app. L'aspetto decisivo qui è che ci sono molti domini che puntano all'app e solo due su tre usano SSL (il terzo è un dominio di fallback, lunga storia). Quindi mi piacerebbe gestirlo nella logica della mia app piuttosto che con .htaccess.

In Laravel 4.2 ho realizzato il reindirizzamento con questo codice, situato in filters.php:

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

Penso che il middleware sia dove dovrebbe essere implementato qualcosa di simile, ma non riesco a capirlo usandolo.

Grazie!

AGGIORNARE

Se stai usando Cloudflare come me, questo si ottiene aggiungendo una nuova regola pagina nel tuo pannello di controllo.


Quindi cosa succede con il terzo dominio? Se imponi https su tutte le rotte, il terzo dominio continuerà a funzionare?
Laurence

Rilevamento che con$_SERVER['HTTP_HOST']
NightMICU

Quanto tempo ci è voluto perché la regola della pagina cloudflare avesse effetto
CodeGuru

Oh ho dovuto attivare il proxy nell'impostazione DNS haha!
CodeGuru

Risposte:


253

Puoi farlo funzionare con una classe middleware. Lascia che ti dia un'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); 
    }
}

Quindi, applica questo middleware a ogni richiesta aggiungendo l'impostazione della regola al Kernel.phpfile, in questo modo:

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'       

];

Nell'esempio sopra, il middleware reindirizzerà ogni richiesta a https se:

  1. La richiesta corrente viene fornita senza protocollo sicuro (http)
  2. Se il tuo ambiente è uguale a production. Quindi, regola le impostazioni in base alle tue preferenze.

Cloudflare

Sto usando questo codice in un ambiente di produzione con un WildCard SSL e il codice funziona correttamente. Se lo rimuovo && App::environment() === 'production'e lo provo in localhost, funziona anche il reindirizzamento. Quindi, avere o meno un SSL installato non è il problema. Sembra che tu debba prestare molta attenzione al tuo livello Cloudflare per essere reindirizzato al protocollo Https.

Modifica 23/03/2015

Grazie al @Adam Linksuggerimento di: è probabilmente causato dalle intestazioni che Cloudflare sta passando. CloudFlare probabilmente colpisce il tuo server tramite HTTP e passa un'intestazione X-Forwarded-Proto che dichiara che sta inoltrando una richiesta HTTPS. Devi aggiungere un'altra riga nel tuo middleware che dice ...

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

... per fidarsi delle intestazioni che CloudFlare sta inviando. Questo interromperà il ciclo di reindirizzamento

Modifica 27/09/2016 - Laravel v5.3

Ho solo bisogno di aggiungere la classe middleware nel webgruppo in 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

    ],
];

Ricorda che il webgruppo viene applicato a ogni rotta per impostazione predefinita, quindi non è necessario impostare webesplicitamente nelle rotte né nei controller.

Modifica 23/08/2018 - Laravel v5.7

  • Per reindirizzare una richiesta a seconda dell'ambiente che puoi utilizzare App::environment() === 'production'. Per la versione precedente era env('APP_ENV') === 'production'.
  • L'utilizzo in \URL::forceScheme('https');realtà non reindirizza. Crea solo collegamenti con https://una volta che il sito web è stato reso.

5
Questo sembra darmi un ciclo di reindirizzamento ... sembra che dovrebbe funzionare, però. Non so se fa differenza, ma stiamo usando un SSL Cloudflare. Ma non credo che cambierebbe il semplice reindirizzamento.
NightMICU

3
@NightMICU Non sono sicuro che tu abbia risolto il problema con il reindirizzamento, ma probabilmente è causato dalle intestazioni che Cloudflare sta passando. CloudFlare probabilmente colpisce il tuo server tramite HTTP e passa un'intestazione X-Forwarded-Proto che dichiara che sta inoltrando una richiesta HTTPS. È necessario aggiungere un'altra riga nel middleware che dice $request->setTrustedProxies( [ $request->getClientIp() ] );di considerare attendibili le intestazioni che CloudFlare sta inviando. Questo interromperà il ciclo di reindirizzamento.
Adam Link

2
@manix Awesome. Ho appena affrontato questo problema HTTPS questo fine settimana con il mio progetto: quelle piccole cose ti frustreranno per ore!
Adam Link

8
Ottima risposta! Solo un dettaglio: è meglio utilizzare un reindirizzamento 301 per indicare a Google che si tratta di un movimento permanente. Like:return redirect()->secure($request->getRequestUri(), 301);
adriaroca

4
per coloro che sono sotto bilanciamento del carico o proxy possono passare a secure () $request->server('HTTP_X_FORWARDED_PROTO') != 'https'per me funziona
Shiro

63

Un'altra opzione che ha funzionato per me, in AppServiceProvider, inserisci questo codice nel metodo di avvio:

\URL::forceScheme('https');

La funzione scritta prima di forceSchema ('https') era sbagliata, il suo forceScheme


16
Ehi, ho appena trovato questo tramite googling in giro - nota che in 5.4 è\URL::forceScheme('https');
dev

5
Nello stesso file, puoi anche fareif($this->app->environment() === 'production'){ $this->app['request']->server->set('HTTPS', true); }
Rory

2
intendevi\URL::forceScheme('https')
Ernest Okot

17
Sono abbastanza sicuro che questo sia solo per la creazione di collegamenti. Questo non costringerà un utente a collegarsi a https, servirà solo collegamenti preceduti da https: //
Weston Watson

sì, è successo @WestonWatson. condividi gentilmente la soluzione se trovata
Harat

33

In alternativa, se stai usando Apache, puoi usare .htaccessfile per imporre agli URL di usare il httpsprefisso. Su Laravel 5.4, ho aggiunto le seguenti righe al mio .htaccessfile e ha funzionato per me.

RewriteEngine On

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

2
Questo non va bene se hai più ambienti (dev, stage, production) di quelli necessari per impostare SSL su tutti loro.
Mladen Janjetovic

@MladenJanjetovic puoi usare RewriteCond %{HTTP_HOST} !=localhostsu dev per aggirare questo problema.
Dan

3
@ Dan - Sì, ma dovrai comunque configurarlo per stage, local (e questo è più complicato se gli sviluppatori utilizzano URL diversi nello sviluppo locale, ad esempio .dev, .local, sottodomini, ... ecc.). Preferirei avere quel tipo di logica nell'applicazione.
Mladen Janjetovic

16

per laravel 5.4 usa questo formato per ottenere il reindirizzamento https invece di .htaccess

namespace App\Providers;

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

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

15
Giusto per chiarire: 1) queste modifiche dovrebbero essere fatte in app / Providers / AppServiceProvider.php; 2) questo serve solo per impostare i link generati all'interno dell'app per usare SSL, non ti obbliga ad usare SSL
phoenix

Ciao, il percorso non viene generato con questo metodo se clicco su un pulsante che mi invia al percorso successivo non mi dà l'errore 404
Saket Sinha

Questo non è un reindirizzamento https. Ma consente la pubblicazione di https: // site. Se lo cambi in http: //, funzionerà anche.
franco

12

Simile alla risposta di Manix ma in un unico punto. Middleware per forzare 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 richiesta deve essere statica?
GFxJamal

@jRhesk probabilmente no, ma per favore prova e sentiti libero di modificare la risposta
Mladen Janjetovic

8

Questo è per Larave 5.2.xe versioni successive. Se vuoi avere un'opzione per servire alcuni contenuti su HTTPS e altri su HTTP, ecco una soluzione che ha funzionato per me. Potresti chiederti, perché qualcuno dovrebbe voler servire solo alcuni contenuti su HTTPS? Perché non pubblicare tutto tramite HTTPS?

Anche se va benissimo servire l'intero sito su HTTPS, tagliare tutto su HTTPS ha un sovraccarico aggiuntivo sul tuo server. Ricorda che la crittografia non è economica. Il leggero sovraccarico ha anche un impatto sul tempo di risposta della tua app. Si potrebbe sostenere che l'hardware di base è economico e l'impatto è trascurabile, ma sto divagando :) Non mi piace l'idea di offrire contenuti di marketing grandi pagine con immagini ecc. Su https. Quindi eccolo qui. È simile a quello che altri hanno suggerito sopra usando il middleware, ma è una soluzione completa che ti consente di alternare tra HTTP / HTTPS.

Per prima cosa crea un middleware.

php artisan make:middleware ForceSSL

Questo è l'aspetto che dovrebbe avere il tuo 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);
    }
}

Nota che non sto filtrando in base all'ambiente perché ho una configurazione HTTPS sia per lo sviluppo locale che per la produzione, quindi non è necessario.

Aggiungi quanto segue a routeMiddleware \ App \ Http \ Kernel.php in modo da poter scegliere e scegliere quale gruppo di route deve forzare 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,
];

Successivamente, vorrei proteggere due gruppi di base di accesso / registrazione ecc. E tutto il resto dietro il middleware di 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
});

Conferma che i tuoi middleware siano applicati correttamente ai tuoi percorsi dalla console.

php artisan route:list

Ora che hai protetto tutti i moduli o le aree sensibili della tua applicazione, la chiave ora è usare il tuo modello di visualizzazione per definire i tuoi collegamenti sicuri e pubblici (non https).

In base all'esempio sopra, renderesti i tuoi link protetti come segue:

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

I collegamenti non protetti possono essere visualizzati come file

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

Ciò che fa è restituire un URL completo come https: // yourhost / login e http: // yourhost / aboutus

Se non si esegue il rendering dell'URL completo con http e si utilizza un URL di collegamento relativo ("/ aboutus"), https persisterà dopo che un utente ha visitato un sito protetto.

Spero che questo ti aiuti!


7

Che ne dici di utilizzare il file .htaccess per ottenere il reindirizzamento https? Dovrebbe essere posizionato nella radice del progetto (non nella cartella pubblica). Il tuo server deve essere configurato per puntare alla directory principale del progetto.

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

Lo uso per laravel 5.4 (ultima versione al momento della stesura di questa risposta) ma dovrebbe continuare a funzionare per le versioni delle funzionalità anche se laravel cambia o rimuove alcune funzionalità.


Chrome mi dà un errore: troppi reindirizzamenti .. Sembra che questo faccia un loop
Phoenix,

Ciao, ho aggiornato la risposta. Assicurati di mettere questo .htaccess nella directory root del progetto e indirizza il tuo server (apache config) alla root del progetto.
Maulik Gangani

1
@MladenJanjetovic puoi avere diversi file htaccess per questi ambienti
Burgi

1
@MladenJanjetovic averlo nell'applicazione ha sicuramente i suoi vantaggi, ma dal punto di vista dell'efficienza e della velocità direi che è vantaggioso averlo nella configurazione del server in modo da non dover caricare Laravel solo per un reindirizzamento. Configurazioni specifiche dell'ambiente in un unico .htaccess con versione possono essere ottenute utilizzando una condizione di riscrittura per controllare il dominio, qualcosa comeRewriteCond %{HTTP_HOST} productiondomain\.com$ [NC]
Chris

1
Anch'io preferisco metterlo in .htaccess nella directory root e, come ha detto Crhis, le impostazioni specifiche dell'ambiente possono essere realizzate qui, anche se un po 'meno elegantemente rispetto alla soluzione di Mladen.
jovan

6

Puoi usare RewriteRule per forzare ssl nella stessa cartella .htaccess con il tuo index.php
Per favore aggiungi come allegato immagine, aggiungilo prima di tutti gli altri impostando ssl .htaccess


3

in IndexController.php put

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

        return redirect('/');
    }

    return view('index');
}

in AppServiceProvider.php put

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

}

In AppServiceProvider.php ogni reindirizzamento andrà all'URL https e per la richiesta http abbiamo bisogno di un reindirizzamento una volta quindi in IndexController.php Dobbiamo solo reindirizzare una volta


Puoi spiegare come la tua risposta risolve la domanda?
soundslikeodd

Aggiungi questa spiegazione alla tua risposta.
soundslikeodd


3

Aggiungo questa alternativa poiché ho sofferto molto con questo problema. Ho provato tutti i modi diversi e niente ha funzionato. Quindi, ho escogitato una soluzione alternativa. Potrebbe non essere la soluzione migliore ma funziona -

Cordiali saluti, sto usando Laravel 5.6

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

produzione <- Dovrebbe essere sostituito con il valore APP_ENV nel file .env


2

Ecco come farlo su Heroku

Per forzare SSL sul tuo dynos ma non localmente, aggiungi alla fine del tuo .htaccess in 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]

Puoi testarlo sulla tua macchina locale con:

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

Questo imposta l'intestazione X-forwarded sulla forma che assumerà su heroku.

cioè simula come un heroku dyno vedrà una richiesta.

Riceverai questa risposta sul tuo computer locale:

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

Questo è un reindirizzamento. Questo è ciò che heroku restituirà a un cliente se imposti .htaccess come sopra. Ma non succede sulla tua macchina locale perché X-forwarded non sarà impostato (lo abbiamo simulato con curl sopra per vedere cosa stava succedendo).


2

Se stai usando CloudFlare, puoi semplicemente creare una regola pagina per usare sempre HTTPS: Forza SSL Cloudflare questo reindirizzerà ogni richiesta http: // a https: //

Oltre a ciò, dovresti anche aggiungere qualcosa di simile alla tua funzione \ app \ Providers \ AppServiceProvider.php boot ():

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

Ciò garantirebbe che ogni collegamento / percorso nella tua app utilizzi https: // invece di http: //.


2

Un approccio leggermente diverso, testato in 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. Codice aggiornato in base ai commenti di @ matthias-lill.


1
Funziona anche su Laravel 6.
Rubens

1
l'utilizzo delle funzioni env () non funzionerà con i file di configurazione memorizzati nella cache. Questo dovrebbe puntare config('app.url')invece a. Inoltre, Laravel viene fornito con una funzione stringa molto utile Str::startsWith(config('app.url'), 'https://').
Matthias Lill

1

Per Laravel 5.6, ho dovuto modificare leggermente le condizioni per farlo funzionare.

a partire dal:

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

Per:

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

1

Questo ha funzionato per me. Ho creato un codice php personalizzato per forzare il reindirizzamento a https. Basta includere questo codice nel file 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

Sto usando in Laravel 5.6.28 il prossimo 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

Il modo più semplice sarebbe a livello di applicazione. Nel file

app/Providers/AppServiceProvider.php

aggiungi quanto segue:

use Illuminate\Support\Facades\URL;

e nel metodo boot () aggiungere quanto segue:

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

Questo dovrebbe reindirizzare tutte le richieste a https a livello di applicazione.

(Nota: questo è stato testato con laravel 5.5 LTS)


1

Puoi semplicemente andare su app -> Provider -> AppServiceProvider.php

aggiungi due righe

usa Illuminate \ Support \ Facades \ URL;

URL :: forceScheme ( 'https');

come mostrato nei seguenti codici:

use Illuminate\Support\Facades\URL;

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

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

0

Questo funziona per me in Laravel 7.x in 3 semplici passaggi utilizzando un middleware:

1) Genera il middleware con il 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) Registrare il middleware nel routeMiddlewarefile del kernel all'interno

nocciolo

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

3) Usalo nei tuoi percorsi

Itinerari

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

});

qui la documentazione completa sui middleware

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

Metodo .HTACCESS

Se preferisci usare un .htaccessfile, puoi usare il seguente codice:

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

Saluti!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.