API Keys
每個打到 Supabase 的請求都帶一把 API key。它回答的是「什麼程式在連線」(網頁、App、伺服器),不是「哪一位使用者」。使用者身分另外由 Auth 的 JWT 處理。
選錯金鑰,輕則 RLS 擋下所有查詢,重則整庫被讀走。
該用哪一把
問自己:這段程式會不會被使用者拿到?
| 程式跑在哪 | 用這把 key | 原因 |
|---|---|---|
| 瀏覽器、手機 App、公開 CLI | Publishable key(sb_publishable_...) |
一定會被看到,只能碰到 RLS 允許的資料 |
| 你控制的伺服器、Edge Function、cron | Secret key(sb_secret_...) |
會略過 RLS,絕不能離開伺服器 |
官方預計在 2026 年底淘汰舊的 anon 與 service_role JWT 金鑰。新金鑰是短字串,不是以 eyJ 開頭的長 JWT。若教學或 AI 叫你貼一長串 eyJ...,那是舊寫法。
# 前端可公開。變數前綴依框架而定(Vite 用 VITE_,Next.js 用 NEXT_PUBLIC_)。
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_...
# 只給伺服器。不要加會被打包進前端的前綴。
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SECRET_KEY=sb_secret_...
金鑰本身不要寫進 git。用 .env,並把 .env 加進 .gitignore。
金鑰對應的 Postgres 角色
Client 用 publishable key 連線時,資料庫角色取決於使用者有沒有登入:
| Key | 使用者已登入 | Postgres 角色 |
|---|---|---|
| Publishable | 否 | anon |
| Publishable | 是 | authenticated |
| Secret | 不適用 | service_role(BYPASSRLS) |
RLS 政策 是寫給 anon 與 authenticated 看的。Secret key 對應的 service_role 會跳過所有政策,所以它等於資料庫管理員。
在程式裡使用
前端:
import { createClient } from '@supabase/supabase-js'
export const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY
)
後端才用 secret key。官方也會擋:把 secret key 從瀏覽器送出,平台會回 401。
import { createClient } from '@supabase/supabase-js'
export const supabaseAdmin = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SECRET_KEY
)
常見誤解
- 把 publishable key 藏起來就安全:做不到。建置後的 JS 一定看得到。安全靠 RLS。
- 前端用 secret key 比較方便:等於關掉所有列級權限,任何訪客都能讀寫整庫。
- 沒開 RLS 的表只給「自己的 App」用:任何人都能用你的 URL + publishable key 打 REST API。
金鑰外洩時:先修好外洩原因,再在 Dashboard 的 Settings → API Keys 建立新 secret key、換成新值,最後刪除舊的。不要先刪再改程式,否則線上會全面斷線。
相關資料
- 官方指南:API keys
- 下一步:建表並開 RLS,見 資料表與自動 API 與 Row Level Security