【Laravel 11.x】SSL期限切れ・監視コマンドをつくる方法

こんにちは!
九保すこひです(フリーランスのITコンサルタント、エンジニア)

さてさて、先日ウェブサイトを無料でHTTPS化できる有名サービス「Let’s Encrypt」からとても重要なメールが来ていました。

(抜粋)As a Let’s Encrypt Subscriber, you benefit from access to free, automated TLS certificates. One way we have supported Subscribers is by sending expiration notification emails when it’s time to renew a certificate.

We’re writing to inform you that we intend to discontinue sending expiration notification emails. You can learn more in this blog post.

ひと言で意訳「通知メールやめるわ!」

そうです。つまり・・・・・・

cronやsystemdで証明書の自動更新に失敗したら、気づかないうちにHTTPSが切れる😱

ということです。

最近のブラウザはHTTPSじゃないと警告が出るので、運営者へのマイナスは多大ですよね。

そして、Xにこの事実を投稿したところ、

  • 監視ツールをつかう
  • Linuxコマンドでチェックする

といった代替案を教えていただきました(ありがとうございます!)

いろいろ検討した結果、Let's Encryptがすすめている監視ツール「Red Sift Certificates Lite」を使うことにしたのですが、無料サービスなので制限が今後どうなるかわかりません。

そこで、念のため「自前でも作っとくか😊」となりました。

そこで❗

今回はLaravelで以下3つを実装してみます!

  1. SSL証明書が7日以内ならメールで通知
  2. チェックするドメインは、複数OK
  3. 自動で定期的にチェックできる←楽したいので、ここ重要

この記事は以下のような人に向けて書いています。

  • 「自動でSSL証明書を監視したい」
  • 「コピペだけで導入したい」
  • 「定期的に監視したい」
  • 「期限切れになったことがある…」
  • 「複数のドメインを一括で管理したい」
  • 「Let’s Encryptの通知メールで助かったことがある」

ぜひ最後まで読んでくださいね!

年末年始はコロナで予定が吹っとび…
ドラクエ11s真のラスボスを倒しました😊

通知用メールをつくる

今回はArtisanコマンドをつかってSSL証明書のチェックをして通知するので、先にメール部分(Mailable)をつくっておきます。

以下のコマンドを実行してください。

php artisan make:mail SSLCertificateChecked

すると、app/Mail/SSLCertificateChecked.phpというファイルが作成されるので、中身を以下のようにします。

<?php

namespace App\Mail;

use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class SSLCertificateChecked extends Mailable
{
use Queueable, SerializesModels;

/**
* Create a new message instance.
*/
public function __construct(private $host, private Carbon $dt)
{}

/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$to = 'test@example.com';
$subject = $this->getContent();

return new Envelope(
to: $to,
subject: $subject,
);
}

/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
htmlString: $this->getContent(),
);
}

/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}

private function getContent(): string
{
$days_in_days = (int) round(
now()->diffInDays($this->dt)
);

return '【'. $this->host .'】有効期限が「'. $days_in_days .'日後」に切れます。';
}
}

この中では、シンプルに

  • どのドメインをチェックしたか
  • そのドメインの証明書が切れるのは何日後か

を送信しているだけです。

コマンドをつくる

続いて、メインの独自のコマンド「CheckSSLCertificateCommand」をつくります。
以下のコマンドを実行してください。

php artisan make:command CheckSSLCertificateCommand

すると、app/Console/Commands/CheckSSLCertificateCommand.phpというファイルが作成されるので、中身を以下のように変更します。

<?php

namespace App\Console\Commands;

use App\Mail\SSLCertificateChecked;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;

class CheckSSLCertificateCommand extends Command
{
const CHECK_URLS = [ // チェックするするURL
'https://amazon.co.jp',
'https://google.com',
'https://yahoo.co.jp',
];

/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check:ssl-certificate';

/**
* The console command description.
*
* @var string
*/
protected $description = 'SSL証明書の有効期限を確認します';

/**
* Execute the console command.
*/
public function handle()
{
$url = $this->getCheckUrl();

if(Str::length($url) === 0) {

$this->error('URLが取得できませんでした');
return;

}

$this->checkSSLCertificate($url);

}

private function getCheckUrl()
{
$lastIndex = Cache::get('last_index', 0);
$nextIndex = ($lastIndex + 1) % count(self::CHECK_URLS);
Cache::put('last_index', $nextIndex);

return self::CHECK_URLS[$nextIndex] ?? '';
}

private function checkSSLCertificate($url)
{
$host = parse_url($url, PHP_URL_HOST);
$sslUrl = 'ssl://'. $host .':443';
$streamContext = stream_context_create([
'ssl' => ['capture_peer_cert' => true]
]);

try {

$fp = stream_socket_client(
$sslUrl,
$errno,
$errStr,
30,
STREAM_CLIENT_CONNECT,
$streamContext
);
$certificate_params = stream_context_get_params($fp);
$certification_data = openssl_x509_parse($certificate_params['options']['ssl']['peer_certificate']);
$timestamp = $certification_data['validTo_time_t'];

} catch (\Exception $e) {

$this->error('[失敗] SSL証明書の取得: ' . $e->getMessage());
return;

}

$dt = Carbon::createFromTimestamp($timestamp);
$host = parse_url($url, PHP_URL_HOST);

Mail::send(new SSLCertificateChecked($host, $dt));

$this->info('Done!');

}
}

この中でやっていることは、以下2つです。

  1. CHECK_URLSから順番に1つずつURLを取得
  2. SSL証明書の情報を取得

ちなみに今回openssl_x509_parseという標準関数があることを知りました。
PHPはいろいろ揃っていますね😊

ちなみに、openssl_x509_parseを使うと、phpstormがちょっとした警告を出していました。

どうやら、「composer.jsonにext-opensslを入れときな!」ってことだったので、以下のように追加することにしました。

{
"name": "laravel/laravel",
"type": "project",
"description": "The skeleton application for the Laravel framework.",
"keywords": ["laravel", "framework"],
"license": "MIT",
"require": {
"php": "^8.2",
"laravel/framework": "^11.0",
"laravel/tinker": "^2.9",
"ext-openssl": "*"
},

つまりは「opensslが使える環境じゃないとダメよ!」ってことですね。

では、今回は短いですがこれで作業は完了です。
お疲れ様でした😊

テストしてみる

では、実際にテストしてみましょう!

実際の運用では自動タイマーで実行することになりますが、今回は手動で実行します。

以下のコマンドを実行してください。

php artisan check:ssl-certificate

すると・・・・・・・

はい!
処理が完了しました。

では、メールの方を確認してみましょう。

どうなるでしょうか・・・・・・

はい!
ちゃんとメールが届いていました😊

では、次のドメインチェックができるかも確認したいので、そのままもう一度コマンドを実行してみましょう。

うまくいくでしょうか・・・・・・

はい!
amazon.comの次は、google.comがチェックされました。

すべて成功です😊

企業様へのご提案

繰り返しになりますが、HTTPSが使えないとブラウザが警告を出すようになっているため、証明書が切れてしまうと、集客にマイナスです。

Let's Encryptは無料でウェブサイトをHTTPS化することができるので便利ですが、2025年6月4日をもって期限切れメールが通知されなくなり「一度設定してしまえば安心」という状況ではなくなります。

そのため、もし証明書だけでなくサイトダウンの定期チェックなどの自動化をご希望でしたらいつでもお問い合わせよりご相談ください。

お待ちしております!😊

おわりに

ということで、今回はLaravelでSSL証明書チェックを自動化してみました。

実を言うと、今回の機能はGASGoogle Apps Script)で実装することを考えていました。

レンタルサーバー料金が不要だからですね。

でもダメでした😱
HTTPアクセスはできますが、証明書の情報を取得できなかったんですね…(もし方法を知っている方がいたら、ぜひ教えてください!)

ということで、記事は当初から大幅に変更してお届けしました。

ぜひLaravelだけでなく、いろんな方法でも試してみてくださいね。

ではでは〜❗

「今回のアイキャッチ画像から
デザインで勉強した『ガイド線』を使ってます!」

このエントリーをはてなブックマークに追加       follow us in feedly  
お問い合わせ、お待ちしております。
開発のご依頼はこちら: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ