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

開発記録 / junka制作記 / Vol.11 junka制作記 Vol.1

記事 02

GASはPOSTリクエストに302を返す——LINE Webhookの罠とEC2プロキシで解決した話

どんなシステムにも、「経験者なら5秒で気づく罠」がある。GAS(Google Apps Script)とLINEの組み合わせには、それが1つある。知らなければ何時間でも詰まる。知っていれば回避に10分かからない。

2026-06-07 公開

どんなシステムにも、「経験者なら5秒で気づく罠」がある。GAS(Google Apps Script)とLINEの組み合わせには、それが1つある。知らなければ何時間でも詰まる。知っていれば回避に10分かからない。


最初の設計は、1時間後に崩れた

LINEのMessaging APIでは、ユーザーがメッセージを送ると、LINEのサーバーが指定したURLにPOSTリクエストを送信する。そのURLでリクエストを受け取り、GASが処理する——設計としては明快だ。

GASでWebアプリを公開する方法は知っていた。doPost関数を定義してデプロイすれば、外部からPOSTを受け取れる。LINEのWebhook URLにGASのURLを設定した。配信テストを実行した。

失敗、と返ってきた。


302が返ってきた

ログを見た。LINEはHTTPステータス302を受け取ったと記録していた。302は「移動しました」というリダイレクト応答だ。「成功」でも「失敗」でもない、「別の場所を見てください」という応答だ。

GASのWebアプリはGoogleの認証基盤を経由する仕組みになっている。LINEのサーバーのような未認証の外部リクエストに対して、Googleは認証ページへのリダイレクトを返す。これがGASとLINE Webhookの相性の悪さの根本だ。

LINEはリダイレクト先に追従しない。302を受け取った時点で配信失敗として扱い、通知は届かない。

公開設定を「全員」にしても変わらない。問題はGASの仕組みの深いところにある。


EC2を間に挟む

詰まっているときに有効な問いは「問題を分割できないか」だ。

問題は「LINEからのPOSTをGASが受け取れない」だった。これを「LINEからのPOSTを誰かが受け取る」と「その誰かがGASを呼ぶ」に分割した。

既にEC2(Amazon Linux上のFastAPIサーバー)が稼働していた。DX経営ゲームや予約システムがそこにいた。このEC2がプロキシとして機能すればいい。

LINE → EC2(FastAPI)→ GAS(doGet)→ 処理完了

EC2はLINEからのPOSTを問題なく受け取れる。EC2が受け取った後、GASをGETで呼ぶ。GASのdoGetはGETリクエストを普通に受け取れる。302は返ってこない。


即座に200を返すことの重要性

LINEのWebhookには「数秒以内に200を返すこと」というルールがある。遅れると配信失敗とみなされてリトライが来る。GASの処理には数秒かかることがある。

EC2での実装はこうなる。

LINEからPOSTが届く。ボディからユーザーIDとメッセージを取り出す。LINEには即座に200を返す。それと並行して、バックグラウンドでGASのURLをhttpxで非同期呼び出しする。

返答と処理を切り離す。これは焼き菓子店や予約システムで確立したパターンだ。同じ問題が出たとき、同じ解法が使えた。


GETで情報を渡す設計の判断

GASをGETで呼ぶとき、生徒コードやLINEユーザーIDをクエリパラメータとして渡す。URLに情報が載る。「セキュリティ的に大丈夫か」と考えた。

生徒コードは「J001」のような記号だ。単独では個人を特定できない。LINEユーザーIDはLINE内部の識別子で、外部から悪用できるものではない。通信はHTTPS(暗号化済み)だ。

これらを踏まえて、許容と判断した。もし氏名や電話番号を渡す設計だったら判断は変わる。渡す情報の性質に応じた判断が必要だ。


この罠を知っていれば

GASとLINEの組み合わせで詰まったとき、まずこれを疑う。LINEからのWebhookはGASに直接当てない。EC2なりCloud Functionsなり、中継サーバーを挟む。それを最初のルールにしておけば、入退室管理システムの実装はもっと早く終わっていた。

同じことを焼き菓子店で学び、予約システムで確認し、入退室管理システムで当然のこととして設計した。知識は経験の数だけ深くなる。


*次回は「Google SheetsのDate型に気づかず2日詰まった話」*

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