จัดการ Environment Variables บน Vercel ระหว่าง Preview กับ Production ยังไงให้ Secret ไม่รั่ว

สรุปสั้น ๆ
Vercel เก็บ Environment Variables แยกได้ตาม Environment คือ Production, Preview และ Development ทำให้ตั้งค่า Secret ต่างชุดกันได้ตามจุดประสงค์ กฎสำคัญที่สุดคือห้ามใส่ Secret จริงในตัวแปรที่ขึ้นต้นด้วย NEXT_PUBLIC_ เพราะค่านั้นถูก Bundle ไปอยู่ใน Code ฝั่ง Client ที่ใครก็เปิดดูได้ผ่าน Browser DevTools
มีเหตุการณ์หนึ่งที่ผมจำได้แม่นเพราะเกือบทำให้ทีมต้อง Rotate API Key ทั้งระบบกลางดึก นักพัฒนาคนหนึ่งตั้งชื่อตัวแปรเก็บ Secret Key ของ Payment Gateway ว่า `NEXT_PUBLIC_PAYMENT_SECRET` เพราะเข้าใจผิดว่า Prefix นั้นแค่ทำให้ตัวแปรใช้งานง่ายขึ้น ไม่รู้ว่ามันหมายถึงให้ Next.js Bundle ค่านั้นฝังไปในไฟล์ JavaScript ที่ Browser ของทุกคนดาวน์โหลดไปเปิดดูได้ตรง ๆ
โชคดีที่มีคนในทีมสังเกตเห็นตอนรีวิว Code ก่อน Deploy จริง แต่ถ้าไม่มีใครจับได้ Secret Key นั้นจะหลุดออกไปอยู่ใน Bundle สาธารณะทันทีที่ Deploy ขึ้น Production ซึ่งเป็นความผิดพลาดที่พบได้บ่อยกว่าที่คิด โดยเฉพาะกับทีมที่เพิ่งย้ายมาใช้ Next.js และยังไม่คุ้นกับกฎเรื่อง Prefix ของตัวแปร
บทความนี้จะอธิบายให้ชัดว่า Environment Variables บน Vercel ทำงานยังไง แยก Environment กันอย่างไร และมีกฎอะไรบ้างที่ต้องรู้ก่อนจะตั้งชื่อตัวแปรตัวแรกในโปรเจกต์
สาม Environment ที่ Vercel แยกให้: Production, Preview, Development
Vercel เปิดให้ตั้งค่า Environment Variables แยกกันได้สามชุดตามจุดประสงค์การใช้งาน แต่ละชุดมีค่าต่างกันได้โดยไม่ปนกัน ทำให้ทีมสามารถใช้ API Key ทดสอบใน Preview ขณะที่ Production ใช้ API Key จริงคนละชุดกัน
Production คือค่าที่ใช้เมื่อ Deploy ขึ้น Production Branch จริง Preview คือค่าที่ใช้กับทุก Pull Request หรือ Branch ทดสอบ ส่วน Development คือค่าที่ใช้ตอนรันโปรเจกต์บนเครื่อง Local ผ่านคำสั่ง `vercel dev` หรือดึงมาไว้ในไฟล์ `.env.local`
การแยกสามชุดนี้สำคัญมากสำหรับทีมที่ต่อกับ Service ภายนอกอย่าง Payment Gateway หรือ Email Provider เพราะไม่มีใครอยากให้การทดสอบใน Preview เผลอส่ง Email จริงหรือตัดเงินบัตรจริงของลูกค้าโดยไม่ตั้งใจ
NEXT_PUBLIC_ กับตัวแปรฝั่ง Server ต่างกันตรงไหน
กฎที่สำคัญที่สุดของ Next.js คือตัวแปรที่ขึ้นต้นด้วย `NEXT_PUBLIC_` จะถูก Bundle เข้าไปในโค้ด JavaScript ฝั่ง Client ตั้งแต่ตอน Build ทำให้ค่านั้นเปิดดูได้จาก Browser DevTools ของใครก็ได้ที่เข้าเว็บ ควรใช้กับค่าที่ไม่เป็นความลับเท่านั้น เช่น Public API Endpoint หรือ Analytics ID ที่เปิดเผยได้อยู่แล้ว
ส่วนตัวแปรที่ไม่มี Prefix นี้จะอยู่เฉพาะฝั่ง Server เท่านั้น เข้าถึงได้จาก Server Component, Route Handler หรือ Server Action แต่ไม่สามารถเรียกใช้จาก Code ฝั่ง Client ได้เลย เหมาะกับ Secret Key, Database Connection String หรือ Token ที่ต้องปิดเป็นความลับ
ความสับสนที่พบบ่อยคือทีมที่ย้ายมาจาก Framework อื่นที่ไม่มีกฎ Prefix แบบนี้ อาจตั้งชื่อตัวแปรตามความเคยชินโดยไม่รู้ว่า Next.js มีพฤติกรรมพิเศษตรงนี้ วิธีป้องกันที่ดีที่สุดคือตั้งกฎในทีมว่าก่อนเพิ่มตัวแปรใหม่ ต้องถามตัวเองก่อนเสมอว่าค่านี้เปิดเผยต่อสาธารณะได้จริงไหม ถ้าไม่แน่ใจให้เก็บไว้ฝั่ง Server ก่อนเป็นค่าเริ่มต้น
ตารางเทียบการตั้งค่าตามสถานการณ์ที่พบบ่อย
ตารางนี้สรุปว่าตัวแปรแบบไหนควรตั้งค่าที่ไหน:
| ประเภทตัวแปร | ตั้งที่ Environment ไหน | ใช้ Prefix NEXT_PUBLIC_ ไหม |
|---|---|---|
| Database Connection String | แยก Production กับ Preview คนละชุด | ไม่ใช้ |
| Payment Gateway Secret Key | Production ใช้ Key จริง, Preview ใช้ Key ทดสอบ | ไม่ใช้ |
| Public Analytics ID | ใช้ค่าเดียวกันได้ทุก Environment | ใช้ได้ |
| Feature Flag ที่ควบคุม UI | แล้วแต่ต้องการเปิดปิดต่าง Environment | ใช้ได้ถ้าไม่ใช่ความลับ |
Rotate Secret เมื่อสงสัยว่าหลุด ควรทำยังไงให้กระทบน้อยที่สุด
ถ้าพบว่า Secret หลุดออกไปแล้วไม่ว่าจะจาก Commit ผิดพลาดหรือตั้ง Prefix ผิดแบบในตัวอย่างต้นบทความ ขั้นตอนแรกที่ต้องทำทันทีคือ Rotate Key นั้นจากฝั่งผู้ให้บริการก่อน ไม่ใช่แค่ลบตัวแปรออกจาก Vercel เพราะ Key เก่าที่เคยหลุดไปแล้วยังใช้งานได้อยู่จนกว่าจะถูก Revoke จากต้นทาง
หลัง Rotate Key ใหม่แล้ว ให้อัปเดตค่าใน Vercel Project Settings ทั้งสาม Environment ให้ครบ ไม่ใช่แค่ Production เพราะถ้า Preview ยังใช้ Key เก่าที่ถูก Revoke ไปแล้ว การทดสอบใน Preview จะพังทันทีโดยไม่มีใครรู้สาเหตุจนกว่าจะไล่เช็ค
ทีมควรมี Log หรือรายการบันทึกภายในว่า Key ไหนผูกกับ Service ไหนบ้าง เพื่อให้ Rotate ได้ครบทุกจุดโดยไม่ลืม เพราะบาง Key อาจถูกใช้ซ้ำในหลาย Project หรือหลาย Environment โดยไม่มีใครจำได้ครบถ้าไม่มีการบันทึกไว้เป็นลายลักษณ์อักษร
จำกัดสิทธิ์เข้าถึง Environment Variables ในทีมให้เหมาะสม
Vercel เปิดให้กำหนด Role ของสมาชิกในทีมได้ว่าใครมีสิทธิ์ดูหรือแก้ Environment Variables ได้บ้าง ทีมที่มีสมาชิกจำนวนมากควรจำกัดสิทธิ์นี้ให้แคบที่สุดเท่าที่จำเป็น ไม่ใช่เปิดให้ทุกคนที่มีสิทธิ์เข้า Repository เห็น Secret ทั้งหมดโดยอัตโนมัติ
แนวทางที่ปลอดภัยกว่าคือให้เฉพาะคนที่รับผิดชอบ Infrastructure โดยตรงมีสิทธิ์แก้ไข Environment Variables ของ Production ส่วนนักพัฒนาทั่วไปมีสิทธิ์แค่ดูหรือแก้ของ Preview และ Development เท่านั้น วิธีนี้ลดความเสี่ยงที่ Secret จะหลุดจากการที่คนจำนวนมากเข้าถึงได้พร้อมกันโดยไม่จำเป็น
Vercel เก็บ Environment Variables ยังไง และทำไมยังต้องระวังอยู่ดี
ค่าที่ตั้งไว้ใน Environment Variables ของ Vercel ถูกเข้ารหัสเก็บไว้ในระบบ ไม่ได้เก็บเป็น Plain Text ตรง ๆ และเข้าถึงได้เฉพาะผ่าน Dashboard หรือ CLI ของบัญชีที่มีสิทธิ์เท่านั้น ซึ่งปลอดภัยกว่าการฝัง Secret ไว้ในไฟล์ Code ที่ Commit เข้า Git โดยตรงมาก
แต่การเข้ารหัสฝั่ง Vercel ไม่ได้แปลว่า Secret จะปลอดภัยเสมอไปหลังจากนั้น เพราะเมื่อ Deploy จริง ค่าตัวแปรจะถูกโหลดเข้าไปอยู่ใน Memory ของ Function ตอน Runtime ถ้า Code มีจุดที่ Log ค่าตัวแปรออกมาโดยไม่ตั้งใจ เช่นเขียน `console.log(process.env)` ทิ้งไว้ตอน Debug แล้วลืมลบก่อน Deploy ค่า Secret ก็จะไปโผล่ใน Function Log ที่คนมีสิทธิ์เข้าถึง Dashboard เห็นได้อยู่ดี
อีกจุดที่ต้องระวังคือ Error Message บางประเภทที่ระบบสร้างขึ้นอัตโนมัติ เช่น Error จากการเชื่อมต่อ Database ที่พิมพ์ Connection String เต็ม ๆ ออกมาในข้อความ Error เพื่อช่วย Debug ถ้า Error แบบนี้หลุดไปอยู่ใน Log ที่ส่งต่อไปยัง Third-party Monitoring Tool โดยไม่มีการกรองก่อน ก็มีความเสี่ยงที่ Secret จะรั่วออกไปนอกระบบของ Vercel เองได้เช่นกัน
ซิงก์ค่าระหว่างเครื่อง Local กับ Vercel ให้ตรงกันโดยไม่ต้อง Copy มือ
ทีมที่มีสมาชิกหลายคนมักเจอปัญหาว่าไฟล์ `.env.local` บนเครื่องแต่ละคนไม่ตรงกัน เพราะต่างคน Copy ค่ามาจากคนละช่วงเวลา บางคนมีตัวแปรใหม่ที่เพิ่งเพิ่มเข้าไป บางคนยังใช้ค่าเก่าที่ล้าสมัยไปแล้ว ทำให้ Bug บางตัวเกิดเฉพาะบนเครื่องบางเครื่องโดยหาสาเหตุยาก
Vercel CLI มีคำสั่งที่ดึงค่า Environment Variables ของ Environment ที่เลือกลงมาสร้างเป็นไฟล์ `.env.local` บนเครื่อง Local ได้โดยตรง ทำให้ทุกคนในทีมดึงค่าล่าสุดจากส่วนกลางแทนการส่งไฟล์ต่อกันทาง Chat หรือ Email ซึ่งเป็นช่องทางที่ Secret หลุดง่ายที่สุดวิธีหนึ่งที่คนมักมองข้าม
ข้อดีอีกอย่างของการดึงค่าผ่าน CLI แทนการ Copy มือคือเมื่อมีการ Rotate Key หรือเพิ่มตัวแปรใหม่ ทุกคนในทีมสามารถรันคำสั่งเดิมซ้ำเพื่อดึงค่าล่าสุดได้ทันที ไม่ต้องรอให้ใครสักคนส่งไฟล์ใหม่ให้ทีละคน ลดทั้งความล่าช้าและความเสี่ยงที่ไฟล์จะหลุดไปอยู่ในที่ที่ไม่ควรอยู่ เช่นแนบไปกับ Chat กลุ่มที่มีคนนอกทีมอยู่ด้วย
ตัวอย่างสมมติ: ทีมที่ตั้งค่าผิดจน Preview ส่ง Email จริงให้ลูกค้า
ลองสมมติทีมหนึ่งที่ยังใหม่กับ Vercel ตั้งค่า Email Provider Key ไว้ค่าเดียวกันทั้ง Production และ Preview เพราะคิดว่าสะดวกกว่าไม่ต้องสร้างสองชุด พอมีคนเปิด Pull Request ทดสอบ Flow การสมัครสมาชิกใน Preview Environment ระบบกลับส่ง Email ยืนยันจริงไปหาอีเมลทดสอบที่บังเอิญเป็นอีเมลของลูกค้าจริงที่เคยใช้ทดสอบไว้ก่อนหน้า
หลังเหตุการณ์นี้ ทีมตัดสินใจแยก Email Provider Account เป็นสองชุดจริงจัง คือชุดทดสอบที่ผูกกับ Preview Environment ซึ่งส่ง Email ไปที่ Sandbox หรือ Inbox ทดสอบเท่านั้น กับชุดจริงที่ผูกกับ Production Environment เท่านั้น ตั้งแต่นั้นมาการทดสอบ Flow ต่าง ๆ ใน Preview ก็ไม่มีความเสี่ยงกระทบผู้ใช้จริงอีก (สถานการณ์นี้เป็นตัวอย่างสมมติเพื่อประกอบการอธิบาย ไม่ใช่กรณีศึกษาที่มีการเก็บสถิติจริง)
ขั้นตอนตั้งค่าตอนเริ่มโปรเจกต์ใหม่ให้ถูกตั้งแต่ต้น
สำหรับโปรเจกต์ใหม่ การวางระบบ Environment Variables ให้ถูกตั้งแต่ต้นง่ายกว่าการมาแก้ทีหลังมาก เพราะยิ่งโปรเจกต์โต จำนวนตัวแปรก็ยิ่งเพิ่มและยิ่งยากต่อการไล่แก้ทั้งหมด
- แยก Service ภายนอกที่มีทั้งโหมดทดสอบและโหมดจริง เช่น Payment Gateway หรือ Email Provider ให้มีบัญชีทดสอบแยกจากบัญชีจริงตั้งแต่ต้น
- ตั้งค่า Production และ Preview ให้ชี้ไปที่บัญชีทดสอบกับบัญชีจริงตามลำดับ ไม่ใช้ค่าเดียวกันทั้งสอง Environment
- เขียนเอกสารภายในทีมว่าตัวแปรแต่ละตัวมีไว้ทำอะไร ใช้ที่ Environment ไหน เพื่อให้สมาชิกใหม่เข้าใจได้เร็วโดยไม่ต้องเดา
- รีวิว Prefix ของทุกตัวแปรก่อน Deploy จริงครั้งแรก ตรวจว่าไม่มี Secret ตัวไหนเผลอใส่ NEXT_PUBLIC_ นำหน้าโดยไม่ตั้งใจ
ทำแบบนี้แล้วพัง เพราะอะไร
- ใส่ Secret Key จริงในตัวแปรที่ขึ้นต้นด้วย NEXT_PUBLIC_ ทำให้ค่านั้นเปิดดูได้จาก Browser DevTools ของทุกคนที่เข้าเว็บ
- ใช้ Environment Variables ชุดเดียวกันทั้ง Production และ Preview สำหรับ Service ที่มีผลกระทบจริงต่อผู้ใช้ เช่น Payment หรือ Email
- ลบ Secret ออกจาก Vercel แต่ไม่ Revoke จากฝั่งผู้ให้บริการต้นทาง ทำให้ Key เก่าที่หลุดไปแล้วยังใช้งานได้อยู่
- เปิดสิทธิ์ให้ทุกคนในทีมแก้ Environment Variables ของ Production ได้โดยไม่จำกัด ทำให้ตรวจสอบย้อนหลังยากเมื่อมีการเปลี่ยนแปลงผิดพลาด
- ไม่มีเอกสารบันทึกว่าตัวแปรแต่ละตัวมีไว้ทำอะไร ทำให้สมาชิกใหม่ในทีมต้องเดาหรือไปถามคนเก่าทุกครั้งที่ต้องแก้ไข้ Config
สรุป
Environment Variables บน Vercel ให้ความยืดหยุ่นสูงในการแยกค่าตาม Production, Preview และ Development แต่ความยืดหยุ่นนี้ก็มาพร้อมความเสี่ยงถ้าทีมไม่เข้าใจกฎเรื่อง NEXT_PUBLIC_ ให้ชัดเจน
สิ่งที่ทำได้ทันทีคือตรวจสอบตัวแปรทั้งหมดในโปรเจกต์ปัจจุบันว่ามีตัวไหนตั้ง Prefix ผิดหรือใช้ค่าเดียวกันปนกันระหว่าง Environment หรือเปล่า แล้วแยกให้ถูกก่อนที่จะกลายเป็นปัญหาจริงตอนมีผู้ใช้เยอะขึ้น
- NEXT_PUBLIC_ ทำให้ค่าถูก Bundle ไปฝั่ง Client เปิดดูได้จากทุกคน ห้ามใส่ Secret จริง
- แยก Production กับ Preview ให้ใช้ Service ภายนอกคนละชุด โดยเฉพาะ Payment และ Email
- Rotate Key จากต้นทางเสมอเมื่อสงสัยว่าหลุด ไม่ใช่แค่ลบออกจาก Vercel
- จำกัดสิทธิ์เข้าถึง Environment Variables ของ Production ให้แคบที่สุดเท่าที่จำเป็น
คำถามที่พบบ่อย
ตัวแปรที่ไม่มี NEXT_PUBLIC_ นำหน้าปลอดภัยเสมอไปไหม
ปลอดภัยกว่าเพราะไม่ถูก Bundle ไปฝั่ง Client แต่ยังต้องระวังไม่ให้ค่านั้นหลุดผ่านช่องทางอื่น เช่น Log ที่พิมพ์ค่าตัวแปรออกมาโดยไม่ตั้งใจ หรือ Error Message ที่แสดงค่า Config บางส่วน
Preview กับ Production ต้องใช้ Database คนละตัวเสมอไปไหม
แนะนำให้แยกโดยเฉพาะถ้าเป็น Production Data จริง เพราะการทดสอบใน Preview อาจไปแก้ไขข้อมูลจริงโดยไม่ตั้งใจ ควรมี Database สำหรับทดสอบแยกต่างหาก
ลืม Rotate Key หลัง Secret หลุดมีผลแค่ไหน
Key เก่าที่หลุดไปแล้วยังใช้งานได้จนกว่าจะถูก Revoke จากฝั่งผู้ให้บริการ การลบออกจาก Vercel เพียงอย่างเดียวไม่ได้ทำให้ Key นั้นใช้งานไม่ได้อีก จึงต้อง Rotate จากต้นทางเสมอเมื่อสงสัยว่าหลุด
ตั้งค่า Environment Variables ผ่าน CLI ได้ไหม ไม่ต้องเข้า Dashboard
ได้ Vercel CLI มีคำสั่งจัดการ Environment Variables ผ่าน Terminal ซึ่งสะดวกสำหรับทีมที่ต้องการ Automate ขั้นตอนตั้งค่าตอนสร้างโปรเจกต์ใหม่
Feature Flag ควรเก็บเป็น Environment Variable ไหม
ทำได้สำหรับ Flag ที่ไม่เปลี่ยนบ่อยและไม่ต้องการ UI จัดการแยก แต่ถ้าต้องเปิดปิดบ่อยหรือมีหลาย Flag ควรพิจารณาใช้ระบบ Feature Flag เฉพาะทางแทนเพราะจัดการง่ายกว่าการแก้ตัวแปรแล้ว Deploy ใหม่ทุกครั้ง
จำกัดสิทธิ์ดู Environment Variables ในทีมเล็กจำเป็นไหม
แม้ทีมเล็กก็ควรพิจารณา โดยเฉพาะถ้ามี Freelance หรือ Contractor ที่เข้าถึง Repository ชั่วคราว ควรจำกัดให้เห็นเฉพาะที่จำเป็นต่องานที่รับผิดชอบจริง
อ่านต่อแบบเจาะลึก
ทดลองวัดผลโฆษณาเข้า LINE ฟรี 14 วัน
สมัครแล้วสร้างโปรเจกต์ เชื่อม LINE ติดตั้ง Tracking แล้วเริ่มเห็น Funnel จาก Click ถึง Purchase ได้ทันที ไม่ต้องใช้บัตรเครดิต
เริ่มทดลองใช้ฟรี 14 วันบทความที่เกี่ยวข้อง

ทำไม Deploy จาก GitHub ไป Vercel สำเร็จ แต่เว็บที่เห็นจริงยังเป็นเวอร์ชันเก่า

เขียนแอปเสร็จแต่ยังไม่มีฐานข้อมูล ทีมเล็กควรเริ่มจาก Supabase ยังไง
