Webアプリケーションでログアウト機能を実装する際、デザインの都合上 <a> タグ(リンク)を使いたい場面は多いと思います。
しかし、ログアウト処理を単純なGETリクエストで行うと、セキュリティ上のリスクが生じます。
今回は、見た目は「スマートなリンク」でありながら、裏側では「安全にPOST送信」を行う実装方法について、備忘録としてまとめます。
1. 実装コード
結論から言うと、以下のように 「JavaScriptでデフォルトの挙動をキャンセルし、裏に隠したフォームを送信する」 というアプローチを取ります。
フロントエンド(Bladeテンプレートなど)
HTML
<!-- ユーザーに見えるリンク -->
<a href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">ログアウト</a>
<!-- 裏側に隠されているフォーム(表示はされない) -->
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>2. コードの挙動解説
この実装の肝は、<a> タグの onclick 属性に書かれた2つのJavaScript関数です。
event.preventDefault();- リンクをクリックした際の本来的(デフォルト)な挙動(例:
hrefに書かれたURLへのページ遷移)を無効化します。
- リンクをクリックした際の本来的(デフォルト)な挙動(例:
document.getElementById('logout-form').submit();- ページ内にある
id="logout-form"を持つ<form>要素をJavaScriptから強制的に送信(Submit)します。
- ページ内にある
これにより、「リンクをクリックしたのに、実際にはフォームがPOST送信される」という挙動を実現しています。
3. なぜこの実装が必要なのか?(セキュリティとUXの理由)
「最初から href="#" にしておけばいいのでは?」や「なぜボタン(<button>)を使わないのか?」という疑問に対する回答です。
① セキュリティ対策(CSRF・意図しないログアウトの防止)
ログアウト処理を通常のリンク(GETリクエスト)にしてしまうと、以下のようなリスクが生じます。
- ブラウザの「リンク先読み(プレフェッチ)機能」によって、ユーザーがクリックしていないのに勝手にログアウトされる。
- 外部サイトに
<img>タグなどでログアウトURLを埋め込まれ、ページを開いただけでログアウトさせられる(CSRF攻撃)。
そのため、ログアウトは状態を変化させる処理としてPOSTメソッドで送り、CSRFトークン(@csrf)を検証するのがWeb開発のセオリーです。
② なぜ href="#" ではなく route('logout') を書くのか?
JavaScriptで遷移をキャンセルするなら href="#" でも動作はしますが、正しいURLを記述することにメリットがあります。
- アクセシビリティ: マウスホバー時にブラウザの左下に正しいURLが表示され、スクリーンリーダーを利用するユーザーにも意図が伝わりやすい。
- フォールバック: 万が一JavaScriptの読み込みエラーなどでスクリプトが動かなかった場合でも、リンクとして機能する(※サーバー側でGETリクエストを受け付けるか、エラーハンドリングが必要)。
まとめ
- ログアウトは POSTリクエスト で処理するのが安全。
- デザイン上
<a>タグを使いたい場合は、event.preventDefault()と隠しフォーム(display: none;)を組み合わせる。 hrefにはjavascript:void(0)や#ではなく、適切なURLを設定しておくのがベストプラクティス。



コメント