コールバックURLは登録した。URLの文字列も合っているはず。それでも Invalid redirect_uri が消えない——この状態で検索してここにたどり着いたなら、原因は「設定ミス」ではなく「見えにくいズレ」である可能性が高い。この記事では、LINEログインの redirect_uri 検証が失敗する原因を頻出順に全パターン並べる。該当箇所を直せばエラーは消える。
仕組みの全体像(図解)
flowchart TD
A[ブラウザが認可リクエスト送信] --> B{LINE サーバーが\nredirect_uri を検証}
B -->|Developers Console\n登録値と完全一致| C[認可コードを発行]
B -->|1文字でも不一致| D[Invalid redirect_uri エラー]
C --> E[ブラウザがコールバックURLへリダイレクト]
E --> F[バックエンドがコードを受け取る]
F --> G{トークン取得リクエストで\nredirect_uriを再送信}
G -->|認可リクエスト時と完全一致| H[アクセストークン取得成功]
G -->|値が違う| I[トークン取得エラー]
LINEの redirect_uri 検証は 2段階ある点がポイントだ。
- 認可リクエスト時:
https://access.line.me/oauth2/v2.1/authorizeにリダイレクトするとき、クエリパラメータに含めるredirect_uri - トークン取得時:認可コードを受け取った後、
https://api.line.me/oauth2/v2.1/tokenにPOSTするボディに含めるredirect_uri
この2つが「Developers Consoleに登録した値」と 1文字単位で一致しないとエラーになる。どちらかのステップで値がズレていれば詰まる。
そのエラー、「設定していない」のとは別の問題かもしれない
LINEログインのエラーメッセージ Invalid redirect_uri は、登録がないときも、登録したが値がズレているときも、同じメッセージで返ってくる。エラーメッセージが同じなので「登録できていない」と誤解しがちだが、このキーワードで検索してくる人の多くはすでに登録済みで詰まっている。
原因の大半は次の6パターンに収まる。上から順に自分の設定と照合してほしい。
原因パターン別チェック
パターン1:末尾スラッシュの有無
LINEは末尾スラッシュの有無を区別する。
| 状態 | 例 |
|---|---|
| Developers Consoleに登録した値 | https://example.com/callback |
| コードが送っている値 | https://example.com/callback/ |
/ が1文字多いだけで Invalid redirect_uri になる。逆パターン(コンソールに / があり、コードにない)も同様に失敗する。
確認方法:Developers Consoleの「コールバックURL」欄を開き、末尾スラッシュの有無を1文字単位で確認する。コード側の文字列定数と目視で照合する。
直し方:どちらかに揃える。末尾スラッシュは付けないほうがシンプルで混乱が少ない。
パターン2:http と https の混在
ローカル開発でもっとも多い。コードが http://localhost:3000/callback を送っているのに、コンソールには https://localhost:3000/callback が登録されているケース(またはその逆)。
| 状態 | 例 |
|---|---|
| Developers Consoleに登録した値 | https://localhost:3000/callback |
| コードが送っている値 | http://localhost:3000/callback |
ローカルで HTTPS を用意せずに開発していると http:// を使いがちだが、コンソールに https:// で登録してしまうと必ず失敗する。
直し方:ローカル開発用は http://localhost:ポート番号/callback でコンソールに登録し直す。本番用は別途 https:// で追加登録する(コールバックURLは複数登録できる)。
注意:LINE Developers Consoleではhttp://のコールバックURLはlocalhostと127.0.0.1以外に対しては登録できない場合がある。本番ドメインは必ずhttps://を使うこと。詳細は公式ドキュメントで確認してほしい。
パターン3:ドメイン・ポート番号のズレ
開発環境でポート番号を変えたとき、コンソールへの登録更新を忘れるケース。
| 状態 | 例 |
|---|---|
| Developers Consoleに登録した値 | http://localhost:3000/callback |
| コードが送っている値 | http://localhost:8080/callback |
また、本番デプロイ後に staging.example.com と example.com を切り替えたときの登録漏れも同じ原因になる。
確認方法:ブラウザのアドレスバーに表示されているドメイン・ポートと、コンソールの登録値を突き合わせる。
直し方:使うすべての環境のURLをコンソールに登録する。ローカル・ステージング・本番で別々に行を追加する(次のセクション参照)。
パターン4:チャネルIDの取り違え
ステージングと本番で別のLINEチャネルを使っている場合、コード側のチャネルIDと、コールバックURLを登録しているチャネルIDが一致していないとエラーになる。
よくある状況:
- 本番チャネルのコンソールにはコールバックURLが登録されている
- コードは本番チャネルIDを使っている
- しかし開発環境のコードはステージングチャネルIDを
LINE_CHANNEL_ID環境変数にセットしていて、そのチャネルにはコールバックURLが登録されていない
確認方法:コードで使っているチャネルID(環境変数 LINE_CHANNEL_ID など)を確認し、Developers Consoleでそのチャネルを開いて「コールバックURL」タブを確認する。
直し方:環境ごとに使うチャネルIDと、そのチャネルのコンソール設定を対応させる。
パターン5:URLエンコードの扱い
認可リクエストのURLを組み立てるとき、redirect_uri の値をURLエンコード(パーセントエンコーディング)する必要がある。ただし、コンソールに登録するのはエンコード前の生のURLだ。
問題が起きるのは、エンコード処理が二重になったり、逆にエンコードされていない文字列が混入したりするとき。
例(Node.js):
// 正しい例:encodeURIComponent で一度だけエンコード
const redirectUri = 'https://example.com/callback';
const authUrl = `https://access.line.me/oauth2/v2.1/authorize` +
`?response_type=code` +
`&client_id=${channelId}` +
`&redirect_uri=${encodeURIComponent(redirectUri)}` + // ← ここで一度だけ
`&state=${state}` +
`&scope=profile%20openid`;
// トークン取得時はエンコードしない生のURLを使う
const tokenBody = new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: redirectUri, // ← エンコードしない
client_id: channelId,
client_secret: channelSecret,
});
URLの組み立てに URLSearchParams や専用のライブラリを使っている場合、ライブラリ側が内部でエンコードするため、自前でさらに encodeURIComponent を通すと二重エンコードになる。
確認方法:後述の「ブラウザ開発者ツールで実際のリクエストを読む」でデコード後のURLを確認する。
パターン6:複数URL登録時の改行・空白混入
Developers Consoleには複数のコールバックURLを登録できる。入力欄はテキストエリアで1行に1URLを入力する形式だが、ここに余分な空白や空行が混入するとエラーになる。
問題が起きやすいパターン:
- URLの後ろにスペースが入っている(
https://example.com/callback← 末尾スペース) - URLとURLの間に空行が入っている
- コピー&ペースト時に全角スペースが混入している
確認方法:Developers Consoleのコールバック欄を開き、各URLの行末をクリックしてカーソルを移動させてみる。見えない空白文字があれば気づける。
直し方:一度全部消してから、URLを1行ずつ手入力で登録し直す。コピー&ペーストは避けるか、貼り付け後に行末の余分な文字を削除する。
環境別の正しい設定例
Developers Consoleのコールバック欄には以下のように登録する(各行を1つのURLとして扱う)。
http://localhost:3000/auth/line/callback
https://staging.example.com/auth/line/callback
https://example.com/auth/line/callback
コード側(環境変数で切り替える例):
# .env.local(ローカル開発)
LINE_CHANNEL_ID=xxxxxxxxxx
LINE_CHANNEL_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
LINE_CALLBACK_URL=http://localhost:3000/auth/line/callback
# .env.staging(ステージング)
LINE_CHANNEL_ID=yyyyyyyyyy # ← ステージング用チャネルIDが別なら別のIDを
LINE_CHANNEL_SECRET=yyyyyyyy...
LINE_CALLBACK_URL=https://staging.example.com/auth/line/callback
# .env.production(本番)
LINE_CHANNEL_ID=zzzzzzzzzz
LINE_CHANNEL_SECRET=zzzzzzzz...
LINE_CALLBACK_URL=https://example.com/auth/line/callback
それでも直らないときのデバッグ手順
パターン1〜6を確認しても直らない場合、実際にブラウザが送っているリクエストURLを目で読むことで原因を特定できる。
ブラウザ開発者ツールでリクエストを確認する
- Chrome または Firefox を開き、
F12で開発者ツールを起動する - 「Network(ネットワーク)」タブを選択し、「Preserve log(ログを保持)」にチェックを入れる
- LINEログインボタンをクリックする
access.line.meへの最初のリクエストを探す- そのリクエストの「Headers(ヘッダー)」または「Request URL」を確認し、
redirect_uri=以降の値を取り出す
取り出した値を decodeURIComponent() でデコードし(ブラウザのコンソールに貼り付けて実行するだけでよい)、Developers Consoleの登録値と1文字ずつ照合する。
// ブラウザのコンソールで実行
decodeURIComponent('https%3A%2F%2Fexample.com%2Fcallback')
// → "https://example.com/callback"
これで「自分が送っている値」と「コンソールに登録した値」の差分が可視化できる。
よくあるエラーと対処
| 状況 | 原因 | 対処 |
|---|---|---|
| ローカルでは動くが本番でエラー | 本番ドメインがコンソール未登録 | 本番URLをコンソールに追加 |
| 本番では動くがローカルでエラー | http://localhost: がコンソール未登録 | ローカルURLをコンソールに追加 |
| 昨日まで動いていたのに突然エラー | チャネルIDの環境変数を誤って変更した可能性 | 環境変数とチャネルIDを再確認 |
エラーメッセージに state も含まれる | state パラメータの生成・検証の問題(redirect_uriとは別の問題) | state の生成ロジックとセッションの扱いを確認 |
| コンソールを保存したのにすぐ反映されない | 反映に数十秒かかることがある | 1〜2分待ってから再試行する |
まとめ:コピペで使える確認チェックリスト
以下を上から順に確認する。1つ当てはまれば、そこが原因である可能性が高い。
□ 末尾スラッシュが「コンソール登録値」と「コード内の値」で一致しているか
□ http / https が一致しているか
□ ドメイン・ポート番号が一致しているか
□ 使っているチャネルID が、コールバックURLを登録したチャネルと同じか
□ redirect_uri をURLエンコードしている箇所が1回だけになっているか
□ コンソールのコールバック欄に余分な空白・空行が混入していないか
□ ブラウザの開発者ツールで実際のリクエストURLを確認したか
チェックリストを全部通過してもエラーが続く場合は、LINE Developers公式ドキュメントの「LINEログインv2.1」→「認可コードを取得する」セクションで最新の仕様を確認することを推奨する。仕様は更新されることがあり、古い実装コードが現在の仕様と合わなくなっている可能性がある。