全国オンライン対応受付 平日 9:00-18:00
お問い合わせ

開発記録 / toi-toi-toi制作記 / Vol.31 toi-toi-toi制作記 Vol.5

記事 05

LIFFがURLを書き換えた——type=eventが消えるまでの追跡

Codexがブラウザで `/reserve?type=event` を開いた。

2026-06-13 公開

Codexがブラウザで /reserve?type=event を開いた。

LINEにログインした。認証が完了してページに戻ってきた。

表示されたのはイベント申込み画面ではなく、通常のサービス一覧だった。


最初の修正は不十分だった

window.addEventListener('DOMContentLoaded', ...) の中で、await initLiff() を呼んだ後に location.search からパラメータを読んでいた。

await initLiff();

const p = new URLSearchParams(location.search);
let typeParam = p.get('type');

問題はここだった。liff.init() を呼ぶと、LIFFがURL処理を行う。処理の中で history.replaceState が実行され、location.search の内容が書き換わる。type=event が消える。

だから initLiff() の呼び出しより前に読めばいいと考えた。

const _p0 = new URLSearchParams(location.search);
let typeParam = _p0.get('type');
// liff.stateからの復元処理...

await initLiff();

if (typeParam === 'event') { ... }

Codexが再検証した。まだ通常サービス一覧が表示された。


ページ遷移のことを忘れていた

LIFFの認証フローを整理した。

LINEの外のブラウザで /reserve?type=event を開く。liff.init() が起動する。未ログインと判定される。liff.login({ redirectUri: location.href }) が呼ばれる。

この瞬間、ページがLINEのログイン画面に移動する。

JavaScriptの実行コンテキストがリセットされる。変数はすべて消える。

LINEでログインが完了すると、ブラウザはredirectUriに指定したURLに戻ってくる。新しいページロードが始まる。

ここで typeParam を変数に保持していても意味がない。ページ遷移で消えている。

では location.href の値は何か。liff.login() を呼ぶとき、内部で liff.init() がすでに location.href を書き換えていた可能性がある。もし location.hrefhttps://yoyaku.example.jp/reserve(typeなし)になっていたとすると、redirectUriは typeなしのURLになる。ログイン後に戻るURLも typeなしになる。


2つの対策を重ねた

ひとつ目は redirectUri を明示的に渡すことだ。liff.init() が実行される前の location.href を保存しておき、それを liff.login() に渡す。

const _savedHref = location.href;  // liff.init()よりも前に保存する
async function initLiff(redirectUri) {
  await liff.init({ liffId: LIFF_ID });
  if (liff.isLoggedIn()) {
    const profile = await liff.getProfile();
    state.lineUserId = profile.userId;
  } else {
    if (!liff.isInClient()) {
      liff.login({ redirectUri: redirectUri || location.href });
    }
  }
}

initLiff(_savedHref) として呼ぶ。liff.login() には書き換え前の元のURLが渡る。ログイン後に戻るURLには type=event が入っている。

ふたつ目は sessionStorage を使うことだ。ページ遷移をまたいでも sessionStorage は消えない。typeParam を検出したタイミングで保存しておき、ページ再ロード後に読み出す。

if (typeParam) {
  sessionStorage.setItem('ttt_type', typeParam);
} else {
  typeParam = sessionStorage.getItem('ttt_type') || '';
}

await initLiff(_savedHref);

sessionStorage.removeItem('ttt_type');  // 使い終わったら消す

第1の保護(redirectUri)が機能すれば、ページ再ロード後のURLに type=event が入っている。第2の保護(sessionStorage)は、なんらかの理由でURLからtypeが取れなかった場合のフォールバックだ。


LIFFのURLへの介入は深い

liff.init() がどこまでURLを変えるかは、LIFF SDKの実装に依存する。

通常のブラウザでは history.replaceState を使ってURLを静かに書き換えることができる。ページをリロードせずにURLを変える。これをLIFFがやっている。

LINEアプリの内部ブラウザではこの挙動が違う可能性がある。外部ブラウザでの動作とLINEアプリ内の動作が一致しないことがある。テスト環境をLINEアプリに統一できないと、再現させることも難しい。

今回は「書き換え前に保存して渡す」と「sessionStorageで保険をかける」という2段構えで解決した。どちらか一方が失敗しても、もう一方がカバーする。


Codexの最終検証で「イベントが0件の場合に『現在募集中のイベントはありません』と表示される」ことが確認された。イベント申込み画面へのルーティングが正しく機能していた。

4機能の追加と、その過程で出てきたバグの修正。@32から@36まで、GASのデプロイ番号が4つ進んだ。

*シンプルシステム株式会社 代表 伊藤勝彦*