ปัญหาของการเปิดพอร์ตตรง ๆ
ลองนึกภาพว่าเราทำเว็บเสร็จแล้ว บนเซิร์ฟเวอร์มีโปรแกรม 2 ตัว รันอยู่คนละพอร์ต
- Frontend (หน้าเว็บ) รันที่พอร์ต 3000
- Backend (API) รันที่พอร์ต 8000
วิธีที่ง่ายที่สุดคือเปิดพอร์ตทั้งสองให้คนนอกเข้าได้ตรง ๆ ผู้ใช้ก็เข้าเว็บได้ทันที แต่ทำแบบนี้ก็เหมือนบ้านที่เปิดประตูทุกห้องออกสู่ถนน โดยไม่มียามเฝ้าสักคน
ปัญหาที่จะตามมา
-
URL จำยาก ดูไม่น่าเชื่อถือ ผู้ใช้ต้องพิมพ์
http://203.0.113.10:3000หรือhttp://203.0.113.10:8000ถึงจะมีโดเมนก็ยังต้องต่อท้ายด้วยพอร์ต เช่นhttp://example.com:3000แถมยังไม่มี HTTPS เบราว์เซอร์จะขึ้นเตือนว่า "ไม่ปลอดภัย" - โดนบอตสแกนพอร์ต เซิร์ฟเวอร์ที่เพิ่งเปิดอาจโดนสแกนภายในไม่กี่นาที ยิ่งเปิดพอร์ตไว้มาก ก็ยิ่งมีประตูให้ลองงัดมาก
- ไม่มีการจำกัดจำนวน request (Rate Limit) ใครจะยิงเข้ามาเป็นหมื่นครั้ง หรือเดารหัสผ่านซ้ำไปเรื่อย ๆ ก็ทำได้ จนเซิร์ฟเวอร์รับไม่ไหว
-
หน้าผู้ดูแลระบบเปิดโล่ง path อย่าง
/api/admin/usersใครรู้ URL ก็ลองเข้าได้ ไม่มีด่านกั้นก่อนถึงตัว API - อัปโหลดไฟล์ใหญ่แค่ไหนก็ได้ ถ้าไม่จำกัดขนาด มีคนส่งไฟล์ใหญ่ ๆ เข้ามาไม่กี่ครั้ง ดิสก์หรือหน่วยความจำก็เต็มได้
แก้ปัญหาเหล่านี้ด้วย Nginx
Nginx คืออะไร?
Nginx คือโปรแกรมเว็บเซิร์ฟเวอร์ที่ติดตั้งบนเครื่องเซิร์ฟเวอร์ คอยรับคำขอ (request) จากเบราว์เซอร์ แล้วส่งข้อมูลกลับไป (response) ผ่าน HTTP หรือ HTTPS
จริง ๆ แล้ว Nginx ทำได้หลายหน้าที่
- Web Server เสิร์ฟไฟล์หน้าเว็บ (HTML, CSS, JS, รูปภาพ) ให้ผู้ใช้โดยตรง
-
Reverse Proxy ยืนอยู่หน้าแอปของเรา รับคำขอแทน แล้วส่งต่อไปยังแอปที่รันอยู่ข้างใน เช่น API ที่
localhost:8000พอได้ผลลัพธ์ก็ส่งกลับให้ผู้ใช้- รวมหลายบริการไว้ใต้โดเมนเดียวได้ เช่น
/apiไปที่ API ส่วน/ไปที่หน้าเว็บ
- รวมหลายบริการไว้ใต้โดเมนเดียวได้ เช่น
-
Load Balancer แบ่งคำขอไปให้เซิร์ฟเวอร์หลายตัวที่ทำงานเหมือนกัน จะได้ไม่มีตัวไหนรับงานหนักเกินไป วิธีแบ่งที่ใช้บ่อย
- Round Robin: แจกวนไปทีละตัวตามลำดับ
- Least Connections: ส่งให้ตัวที่มีงานค้างน้อยที่สุด
- IP Hash: ผู้ใช้คนเดิมได้ไปที่ตัวเดิมทุกครั้ง
- อื่น ๆ เช่น ทำ HTTPS, จำกัดจำนวน request, จำกัดขนาดไฟล์อัปโหลด, บล็อกบาง path หรือบาง IP และเก็บ cache
จะเห็นว่าผู้ใช้เข้ามาทางประตูเดียว (พอร์ต 80/443) แล้ว Nginx จะพาไปที่ไหนก็ได้ตามที่เราตั้งไว้ ถ้ามีโดเมนเดียวก็แบ่งตาม path
-
example.com/→localhost:3000(หน้าเว็บ) -
example.com/api→localhost:8000(API)
ถ้ามีหลายโดเมนหรือ subdomain ก็แยกตามชื่อได้ เช่น
-
n8n.example.com→localhost:5678(n8n)
จากนั้นใช้ firewall (เช่น ufw) ปิดพอร์ต 3000, 8000, 5678 ไม่ให้คนนอกเข้าตรง ๆ เหลือเปิดแค่ 80 กับ 443 ทุกคำขอจึงต้องผ่าน Nginx ก่อนเสมอ
ถึงตรงนี้หลายคนอาจสงสัยว่า ทำไม Nginx ต้องเปิดถึง 2 พอร์ต?
- พอร์ต 80 ใช้กับ HTTP (ไม่เข้ารหัส)
- พอร์ต 443 ใช้กับ HTTPS (เข้ารหัส มีรูปแม่กุญแจ)
การจะใช้ HTTPS ได้ต้องมีใบรับรอง SSL ซึ่งใบฟรีอย่าง Let's Encrypt ปกติจะออกให้กับโดเมน ถ้ายังไม่มีโดเมนก็ใช้ HTTP ผ่านพอร์ต 80 ไปก่อน
พอมีโดเมนและได้ใบรับรองแล้ว ก็ตั้งค่าให้ใครที่เข้ามาทาง HTTP (พอร์ต 80) ถูกส่งเตะไปที่ HTTPS (พอร์ต 443) ทันที แต่ยังต้องเปิดพอร์ต 80 ไว้นะ เพราะต้องใช้รับคนที่พิมพ์ http:// เข้ามา ก็ใช้พอร์ต 80 ตรวจสอบโดเมนตอนต่ออายุใบรับรองด้วย
สรุปคือ ทุกคนต้องเข้ามาทาง 80/443 เท่านั้น แล้วให้ Nginx เป็นด่านตรวจคำขอตามกฎที่เราตั้งไว้ เช่น จำกัดจำนวน request จำกัดขนาดไฟล์ หรือบล็อกบาง path
ส่วนพอร์ต 3000, 8000, 5678 ที่บอกว่า "ปิด" ไม่ได้แปลว่าปิดแอปนะ แค่ปิดที่ firewall ไม่ให้คนข้างนอกเข้าตรง ๆ ได้ แต่ในเครื่องเดียวกันยังใช้งานได้ปกติ Nginx จึงยังส่งต่อไปที่ localhost:3000 ได้เหมือนเดิม
ในบทความนี้จะเน้นเรื่อง Reverse Proxy เป็นหลักน่ะ บางคนอาจไม่เห็นภาพ เรามาทำ Reverse Proxy ง่ายๆ โดยยังไม่มี Domain น่ะ hahaha ว่าทำยังไง จะได้เห็นภาพมากขึ้น อ่านต่อเลิกให้ API นอนแก้ผ้ารอหน้าบ้าน! ซ่อนหลัง Nginx ซ่ะ





Top comments (0)