為什麼要做這個? #
相信各位在 Instagram 或其他社群上,常常會看到那種「匿名問答箱」(例如 NGL.link 或 Marshmallow-qa 等等) :丟一條連結給粉絲,大家匿名發送問題或想說的話,你再挑幾則貼到限時動態上回答。
用起來很方便,沒錯,我自己也用過。但資料都在第三方手上,他們怎麼存、怎麼用、會不會外洩,你完全無法掌控。
於是我就想:這種東西為什麼不能自己做?
而且我要的是——
- 完全免費(不要為了解鎖什麼特殊留言功能而付月費)
- 不用自己架伺服器(我不是伺服器管理員的,雖然管理員聽起來很酷)
- 資料 100% 自己掌控
- 能一鍵把留言變成好看的圖卡,直接貼 IG 限動
- (且包含市面上常見的匿名問答系統有的功能)
這就是這個專案的起點:一個我自己做的、開源、可自主架設的匿名問答系統。
我這個自製的開源專案的 GitHub 頁面: AiixiBear/aqa_system


特色一覽 #
- 完全無須架設自己的伺服器,而且免費——靠 Cloudflare Worker 與 Google Sheet + Apps Script 就能跑起來,並且不需要輸入信用卡資訊即可直接使用
- Cloudflare Turnstile 驗證防機器人
- 收集 IP、User-Agent,並透過 ipinfo.io API 解析 IP 所在地(國家 / ASN)
- 內建篩選後台,可直接查看留言、一鍵生成留言圖卡,貼上 Instagram 限時動態超方便
- 每一則留言都有專屬辨識碼(例如
AQA-9487),粉絲可以在限動上認出自己的留言 - URL 加上
?tag=就能標記留言來源,方便分流 - 資料自己掌控,並且本專案使用 MIT License 開源,你各位想怎麼改就怎麼改
系統怎麼運作? #
整個架構是 Serverless(無伺服器) 的,可以拆成兩條路徑:
- 公開留言路徑:使用者 → 前台網頁 → Cloudflare Worker(驗證 Turnstile)→ Google Apps Script(查 IP、發流水號)→ Google Sheets
- 後台管理路徑:管理者 → 後台網頁 → 後台 Google Apps Script → Google Sheets → 回傳 JSON → 渲染成留言卡片 → 匯出 PNG 圖卡
有人留言時:
- 使用者在前台輸入內容、通過 Turnstile 人機驗證
- Cloudflare Worker 檢查欄位、驗證 Turnstile Token
- 驗證通過後轉送到 Google Apps Script
- Apps Script 去 ipinfo.io 查 IP 國家與 ASN,用 Lock Service 配發唯一流水號(
AQA-0001這種),最後寫入 Google Sheets - 前台顯示這則留言的專屬代碼,粉絲可以複製保存
管理者則是打開後台,用關鍵字、IP、tag、代碼篩選留言,點一下就能匯出成 PNG 圖卡,貼上 IG 限動。
後台的留言卡片支援一鍵下載圖片,圖卡裡不會包含下載按鈕,看起來就是乾乾淨淨的一張留言圖。


怎麼讓別人來留言? #
很簡單,跟一般匿名問答箱一樣:把前台網頁的連結傳給粉絲或朋友就行了。
你也可以在網址後面加上 ?tag= 參數,例如:
https://example.com/?tag=ig-stories這個 tag 會一起被記錄到留言資料裡,而且只有你(資料庫持有者)看得到,適合用來辨識留言是從哪個管道來的。
送出留言後,系統會配發一組獨一無二的流水號(像是 AQA-9487)。這組號碼會:
- 一起存進 Google Sheets
- 顯示在後台的留言卡片上
這樣粉絲在你的 IG 限動上看到圖卡時,一眼就能認出「啊!這是我留的那則欸!」。
架設前的準備 #
這個系統全部都使用免費服務,而且全都不需要輸入信用卡資訊即可直接使用:
- Google 帳號:建立 Google Sheets 與部署 Google Apps Script
- Cloudflare 帳號:部署 Worker 與建立 Turnstile Widget
- IPinfo Lite API Token:查詢 IP 的國家 / 地區與 ASN(ipinfo.io/lite 註冊)
- 靜態網頁空間:GitHub Pages、Cloudflare Pages 或任何能託管 HTML 的服務都可以
架設教學:公開留言系統 #
1. Google Sheets #
- 到 Google Sheets 建立一個空白試算表
- 在第一列貼上表頭:
接收時間 (台北時間) 留言 來源IP Tags 來源國家/地區 AS_Name User-Agent 唯一辨識碼- 記下試算表 URL 與 試算表名稱

2. 寫入用 Google Apps Script #
- 打開 Google Apps Script 建立新專案
- 程式碼貼入專案裡的
gas/main.gs - 到 ipinfo.io/lite 註冊,取得免費 API Token
- 在專案的 設定 → 指令碼屬性 填入:
| 屬性 | 值 | 說明 |
|---|---|---|
IPINFO_TOKEN |
你的 ipinfo.io API Token | 剛剛申請的 |
SHEET_NAME |
試算表的名稱 | 剛剛記下的 |
SHEET_URL |
試算表 URL | 剛剛記下的 |
WRITE_KEY |
自訂的隨機密鑰 | 一串夠長的亂數。Worker 與本專案共用,避免有人繞過 Worker 直灌資料 |
- 部署成網頁應用程式,記下部署後的 URL


關於
WRITE_KEY:這是寫入端的門禁。沒有這把鑰匙,就算知道 Apps Script 的網址也無法直接寫入留言。請自己生成一串夠長的亂數字串(例如 32 碼以上),並記好,稍後要填進 Worker。
3. Cloudflare Worker #
- 註冊 Cloudflare,前往 運算 → Workers 和 Pages
- 建立應用程式,先部署 Hello World,記下 Worker 網址
- 按下編輯代碼,把專案裡的
worker/index.js整份貼上取代 - 部署
- 去註冊一個 Cloudflare Turnstile 小工具,記下
Site Key與Secret Key - 回到 Worker 的 設定 → 環境變數 (Runtime variables and secrets) 填入:
| 變數名稱 | 值 |
|---|---|
TURNSTILE_SECRET_KEY |
你的 Turnstile Secret Key |
APPS_SCRIPT_URL |
剛剛寫入用 Apps Script 的 URL |
APPS_SCRIPT_WRITE_KEY |
剛剛在寫入用 GAS 設定的 WRITE_KEY,必須完全一致 |
- 再部署一次


4. 前台網頁 #
前台只有一個 HTML 檔:frontend/index.html,丟到任何靜態網頁空間都行。
打開檔案,替換這幾個變數:
| 名稱 | 值 | 說明 |
|---|---|---|
YOUR_ICON_URL_HERE |
你的 Icon | 預設大小 64x64 |
YOUR_WORKER_URL_HERE |
你的 Worker URL | |
YOUR_TURNSTILE_SITE_KEY_HERE |
你的 Turnstile Site Key |
到這裡,公開留言系統與資料庫就完成了。
架設教學:留言查看面板(後台) #
1. 後台 Google Apps Script #
- 再開一個 Google Apps Script 專案
- 程式碼貼入
admin_panel_gas/main.gs - 指令碼屬性填入:
| 屬性 | 值 | 說明 |
|---|---|---|
IPINFO_TOKEN |
你的 ipinfo.io API Token | 可選。若試算表的 IP 地理資料被清掉,可手動執行 repairOldData 補回來 |
SHEET_NAME |
留言資料庫的試算表名稱 | |
SHEET_URL |
留言資料庫的試算表 URL | |
TIMEZONE |
圖卡顯示的時區 | 不填預設 Asia/Taipei |
READ_KEY |
自訂的隨機密鑰 | 後台讀取留言時必須帶上,避免有人拿到後台 GAS 網址就 dump 全部留言與 IP |
2. 後台網頁 #
檔案在 admin_panel/index.html,同樣丟到靜態網頁空間即可。
要替換的變數:
| 變數名稱 | 值 | 說明 |
|---|---|---|
YOUR_DATABASE_APPS_SCRIPT_URL |
後台 GAS 的 URL | |
YOUR_READ_KEY_HERE |
剛剛後台 GAS 的 READ_KEY |
必須完全一致,否則會顯示「驗證失敗」 |
YOUR_CARD_IMAGE_URL_HERE |
留言圖卡的背景圖 | 程式碼中有兩處,記得都換。1:1 圖片效果最好 |
後台目前已內建 READ_KEY 驗證(是 API 金鑰驗證,不是帳號密碼登入)。若還想再加一層保護,可以自行使用帶有隨機路徑的 URL,或靜態託管商的存取限制功能。
到這裡,後台就架設好了。
安全性我做了哪些事? #
這種「自己架設的匿名留言」系統最怕兩件事:有人亂灌垃圾訊息,以及後台資料外洩等等。這個專案目前有這些防護:
- Cloudflare Turnstile:公開留言前必須通過人機驗證
WRITE_KEY:寫入端 GAS 只接受帶有正確金鑰的請求,繞過 Worker 直接打 Apps Script 也寫不進去READ_KEY:後台 GAS 讀取留言也要金鑰,保護 IP、User-Agent 等資料- Lock Service:配發流水號時使用 Google Apps Script 的互斥鎖,多人同時送出也不會撞號
- 前端字數限制與冷卻時間:基本的濫用緩衝
當然,這仍然不是「企業級防護」。如果你要拿去做更大型的公開服務,建議再自己加 rate limit、WAF 之類的機制。但以個人使用、粉絲留言這種場景來說,已經比系統直接裸奔好非常多了。
常見問題與故障排除 (FAQ) #
Q1. 自己測試時送出留言時顯示錯誤或「驗證失敗」,該怎麼辦? #
請依序檢查以下幾點:
WRITE_KEY是否一致:請確認 Worker 的環境變數APPS_SCRIPT_WRITE_KEY與寫入用 GAS 的指令碼屬性WRITE_KEY是否完全相同(包含大小寫與英數拼寫)。- Turnstile 金鑰是否配對:檢查前台 HTML 的
Site Key與 Worker 環境變數的TURNSTILE_SECRET_KEY是否來自同一組 Cloudflare Turnstile 設定。 - GAS 部署權限:確認寫入用 GAS 部署時,「誰有權存取」是否有設定為 「所有人 (Anyone)」。
Q2. 打開後台面板抓不到留言資料? #
READ_KEY是否一致:請確認後台 HTML 裡的YOUR_READ_KEY_HERE與後台 GAS 指令碼屬性的READ_KEY完全一致。- 網址是否填錯:請確認後台 HTML 填入的是後台 GAS 的部署 URL,而不是寫入用 GAS 的 URL。
Q3. 免費配額真的夠用嗎?會不會突然要付費? #
對個人與一般社群粉絲互動來說完全夠用!
- Cloudflare Worker:免費版每日提供 100,000 次請求。
- Google Apps Script:一般 Google 帳號每日提供 20,000 次 URL Fetch 呼叫與足夠的試算表寫入額度。
詳情可參考 本專案使用的相關平台定價配額文件
並且這些免費服務完全不需要輸入信用卡資訊試用,因此不會自動產生任何費用。即使超過額度,服務也只是暫時停止回應。
結語 #
我的這個專案把「匿名問答箱系統」從第三方手裡拿回來,全部建在免費、常見、可長期使用的服務上:
- Cloudflare Worker
- Google Sheets / Google Apps Script
- Cloudflare Turnstile
- 免費靜態網頁託管
不需要伺服器、不需要資料庫、不需要月費,資料自己存、圖卡自己生、想改程式就直接改(因為本專案採用 MIT 授權)。
還是要提醒一下:上面雖然有提到,Cloudflare Worker 有每天免費 10 萬次請求、Google Apps Script 有相關上限等等,不過對個人使用真的完完全全綽綽有餘。
如果你也想要一個自己的匿名留言箱,歡迎直接拿去用、去改、去亂搞。
- GitHub:AiixiBear/aqa_system
- License:MIT
有問題或想法,也歡迎到 GitHub 留 issue。