61
66

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravel5で.htaccessを使用せず常時SSL化対応する方法

Last updated at Posted at 2017-05-11

はじめに

普通、全ページをSSL化(https)にするなら.htaccessにこんな風に書きますよね。

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

静的に作ってるページしかないならこれだけで全部httpsにリダイレクトするので、なんだかよさそうな気がします。
しかし、この書き方はformが絡むと結構深刻な問題を引き起こします。

Laravelで簡単なお問い合わせフォームを作ったとして、次のような書き方をしていた場合

{{ Form::open(['url' => route('inquiry.store'), 'method' => 'POST']) }}
	{{ csrf_token() }}
	{{ Form::text('name', NULL) }}
	{{ Form::submit('submit') }}
{{ Form::close() }}

実際には次のようなHTMLを出力します。

<form method="POST" action="http://www.example.com/inquiry/store">
	<input name="_token" type="hidden" value="*******">
	<input name="name" type="text" value=""></td>
	<input type="submit" value="submit">
</form>

.htaccessで記述した場合の問題点

注目すべきはaction属性の値。httpになっています。
Laravelのrouteやurlヘルパーの書き方は標準だと、httpから始まるURLを出力します。

POSTの値はリダイレクトを挟むと消失するので、もし.htaccessでリダイレクトさせたら、
お問い合わせフォームから問い合わせてもSSL対応完了後は一件も問い合わせがこなくなった...
場合によっては500エラー(Non Object Error)になってエラーログが出まくる可能性も...
っていうことがあり得てしまいます。

なので、HTTPからのアクセスはHTTPSにリダイレクトさせつつ、routeやurlヘルパーで書いてきて、
今までhttpとして出力されていた文字列もhttpsとして出力して欲しいですよね?
そんなムシのいい話を、Laravelは実現してくれます。

実装

Middleware
php artisan make:middleware ForceHttpProtocol
/app/Http/Middleware/ForceHttpProtocol.php
<?php

namespace App\Http\Middleware;
use Closure;

class ForceHttpProtocol {

    public function handle($request, Closure $next) {
        if (!$request->secure() && env('APP_ENV') === 'production') { // 本番環境のみ常時SSL化する
            return redirect()->secure($request->getRequestUri());
        }

        return $next($request);
    }

}
Kernel

必要箇所以外の記述は省略します

/app/Http/Kernel.php
<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \App\Http\Middleware\ForceHttpProtocol::class, // 追加
    ];

特定ページのみSSL対応

サーバー証明書のコストの関係とかで、常時SSL化対応ではなく、特定のページだけSSL化することになった場合は次のようにします。

Kernel
/app/Http/Kernel.php
<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'forceSsl' => App\Http\Middleware\ForceHttpProtocol::class, // 追加
    ];
Route
/routes/web.php
<?php

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/

Route::get('/', 'HomeController@index'); // http

Route::group(['prefix' => 'inquiry', 'as' => 'inquiry', 'middleware' => 'forceSsl'], function(){ // https
    Route::get('/', 'InquiryController@index');
    Route::post('/store', 'InquiryController@store');
    Route::get('/finish', 'InquiryController@finish');
});

追記:301リダイレクトさせる方法

なんか結構このページが見られているようなので、追記します。
上に書いたhttpsリダイレクトは、302リダイレクトになっています。

なぜなら、secureメソッドが送るデフォルトのステータスコードが302だからです。

/vendor/laravel/framework/src/Illuminate/Routing/Redirector.php

    /**
     * Create a new redirect response to the given HTTPS path.
     *
     * @param  string  $path
     * @param  int     $status
     * @param  array   $headers
     * @return \Illuminate\Http\RedirectResponse
     */
    public function secure($path, $status = 302, $headers = [])
    {
        return $this->to($path, $status, $headers, true);
    }

301リダイレクトさせたいならば、下のように、第二引数に301と明示的に書けばそれでOKです。

/app/Http/Middleware/ForceHttpProtocol.php
<?php

namespace App\Http\Middleware;
use Closure;

class ForceHttpProtocol {

    public function handle($request, Closure $next) {
        if (!$request->secure() && env('APP_ENV') === 'production') {
            return redirect()->secure($request->getRequestUri(), 301); // ← ここを変更しました
        }

        return $next($request);
    }

}

当たり前すぎるのか、誰も書いてなかったので。

61
66
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
61
66

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?