#Deploy เว็บ PHP บน Vercel ด้วย vercel-php

Vercel เหมาะกับการ Deploy Frontend และ Serverless Functions แต่ PHP ไม่ใช่ Official Runtime ของ Vercel หากต้องการนำเว็บ PHP ไปทำงานบน Vercel จึงต้องใช้ Community Runtime ชื่อ vercel-php

ณ วันที่ปรับปรุงบทความนี้ Community Runtime รุ่น vercel-php@0.9.0 รองรับ PHP 8.5 ส่วน Vercel อนุญาตให้กำหนด Runtime ที่ไม่ได้รองรับอย่างเป็นทางการผ่านพร็อพเพอร์ตี functions.runtime ในไฟล์ vercel.json

แนวทางนี้เหมาะกับเว็บ PHP ขนาดเล็ก API, Webhook, Prototype และระบบที่ออกแบบให้ทำงานแบบ Stateless หากเป็น Laravel หรือระบบขนาดใหญ่ที่ต้องใช้ Queue Worker, Scheduler, Local Session หรือเขียนไฟล์ถาวร ควรพิจารณาใช้บริการที่รองรับ PHP หรือ Container โดยตรง

info-deploy-php-vercel-vercel-php

#ภาพรวมสถาปัตยกรรม

ผู้ใช้ → Vercel CDN → PHP Serverless Function → ฐานข้อมูล/ที่เก็บไฟล์ภายนอก

ทุก Request ที่ต้องประมวลผลด้วย PHP จะถูกส่งไปยัง Vercel Function ส่วนฐานข้อมูล Session และไฟล์ที่ต้องเก็บถาวรควรใช้บริการภายนอก เช่น Managed MySQL/PostgreSQL, Redis หรือ Object Storage

#สิ่งที่ต้องเตรียม

  • บัญชี Vercel
  • Git และบัญชี GitHub, GitLab หรือ Bitbucket หากต้องการ Deploy ผ่าน Git
  • Node.js และ npm สำหรับติดตั้ง Vercel CLI
  • PHP ในเครื่องสำหรับทดสอบแบบ Local

ตรวจสอบเครื่องมือด้วยคำสั่งต่อไปนี้

php -v
node -v
npm -v
git --version

#1. สร้างโปรเจกต์ PHP

สร้างโครงสร้างโปรเจกต์ดังนี้

php-vercel-demo/
├── api/
│   └── index.php
├── .gitignore
└── vercel.json

Vercel ใช้ไดเรกทอรี api เป็นตำแหน่งของ Function ส่วน api/index.php จะเป็น Front Controller ของเว็บ

สร้างไฟล์ api/index.php

<?php

declare(strict_types=1);

$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
$appName = getenv('APP_NAME') ?: 'PHP on Vercel';

if ($path === '/api/status') {
    header('Content-Type: application/json; charset=utf-8');

    echo json_encode([
        'status' => 'ok',
        'application' => $appName,
        'php_version' => PHP_VERSION,
        'time' => gmdate(DATE_ATOM),
    ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

    exit;
}

http_response_code($path === '/' ? 200 : 404);
$title = $path === '/' ? $appName : '404 - Page Not Found';
$message = $path === '/'
    ? 'Deploy เว็บ PHP บน Vercel สำเร็จแล้ว'
    : 'ไม่พบหน้าที่ต้องการ';
?>
<!doctype html>
<html lang="th">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title><?= htmlspecialchars($title, ENT_QUOTES, 'UTF-8') ?></title>
    <style>
        body {
            min-height: 100vh;
            margin: 0;
            display: grid;
            place-items: center;
            font-family: system-ui, sans-serif;
            color: #f8fafc;
            background: linear-gradient(135deg, #111827, #4f46e5);
        }
        main {
            width: min(680px, calc(100% - 48px));
            padding: 48px;
            border: 1px solid rgba(255, 255, 255, .18);
            border-radius: 24px;
            background: rgba(15, 23, 42, .72);
            box-shadow: 0 24px 80px rgba(0, 0, 0, .3);
        }
        a { color: #c4b5fd; }
        code { color: #93c5fd; }
    </style>
</head>
<body>
    <main>
        <p>PHP <?= htmlspecialchars(PHP_VERSION, ENT_QUOTES, 'UTF-8') ?></p>
        <h1><?= htmlspecialchars($message, ENT_QUOTES, 'UTF-8') ?></h1>
        <p>ทดสอบ JSON API ได้ที่ <a href="/api/status"><code>/api/status</code></a></p>
    </main>
</body>
</html>

ตัวอย่างนี้มีสองเส้นทาง

  • / แสดงหน้าเว็บ PHP
  • /api/status ส่งผลลัพธ์รูปแบบ JSON

#2. ตั้งค่า vercel.json

สร้างไฟล์ vercel.json ที่ Root ของโปรเจกต์

{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "functions": {
    "api/index.php": {
      "runtime": "vercel-php@0.9.0"
    }
  },
  "rewrites": [
    {
      "source": "/(.*)",
      "destination": "/api/index.php"
    }
  ]
}

ส่วนประกอบสำคัญมีดังนี้

การตั้งค่า หน้าที่
functions ระบุไฟล์ PHP ที่จะถูกสร้างเป็น Vercel Function
runtime กำหนด Community Runtime และตรึงเวอร์ชันที่ต้องการ
rewrites ส่ง URL ของเว็บไปยัง api/index.php

Vercel ตรวจสอบไฟล์ที่มีอยู่จริงก่อนใช้ Rewrite ดังนั้นสามารถสร้างโฟลเดอร์ assets ที่ Root เพื่อเก็บ CSS, JavaScript และรูปภาพแบบ Static ได้

#3. สร้าง .gitignore

.vercel
.env
.env.*
vendor/

ไม่ควร Commit Secret, รหัสผ่านฐานข้อมูล หรือ Token ลง Git Repository

#4. ทดสอบในเครื่อง

วิธีที่ง่ายที่สุดคือใช้ PHP Built-in Server

php -S localhost:8000 api/index.php

เปิดเว็บที่ http://localhost:8000 และทดสอบ API ที่ http://localhost:8000/api/status

หากติดตั้ง Vercel CLI แล้ว สามารถทดสอบสภาพแวดล้อมที่ใกล้เคียงกับ Vercel ด้วยคำสั่ง

vercel dev

เครื่องที่รัน vercel dev ต้องติดตั้ง PHP ไว้ด้วย

#5. Deploy ด้วย Vercel CLI

ติดตั้ง Vercel CLI

npm install --global vercel

เข้าสู่ระบบ

vercel login

เข้าไปในไดเรกทอรีโปรเจกต์และสร้าง Preview Deployment

cd php-vercel-demo
vercel

ในครั้งแรก CLI จะถามชื่อทีม ชื่อโปรเจกต์ และการเชื่อมต่อกับโปรเจกต์เดิม เมื่อ Deploy สำเร็จจะได้รับ URL สำหรับทดสอบ

Deploy เป็น Production ด้วยคำสั่ง

vercel --prod

#6. Deploy อัตโนมัติผ่าน Git

เริ่มต้น Git Repository และ Push ขึ้น GitHub

git init
git add .
git commit -m "Deploy PHP web to Vercel"
git branch -M main
git remote add origin https://github.com/USERNAME/php-vercel-demo.git
git push -u origin main

จากนั้นดำเนินการดังนี้

  1. เปิด Vercel Dashboard แล้วเลือก Add New → Project
  2. Import Repository ที่สร้างไว้
  3. ตรวจสอบ Root Directory ให้ตรงกับตำแหน่ง vercel.json
  4. เพิ่ม Environment Variables หากระบบต้องใช้
  5. กด Deploy

เมื่อเชื่อมต่อ Git แล้ว การ Push ครั้งต่อไปจะสร้าง Deployment ใหม่ให้อัตโนมัติ Branch หลักใช้สำหรับ Production ส่วน Branch หรือ Pull Request อื่นใช้สร้าง Preview Deployment ได้

#7. ใช้ Environment Variables

เพิ่มตัวแปรที่ Project → Settings → Environment Variables เช่น

APP_NAME=My PHP Application
DB_HOST=database.example.com
DB_PORT=3306
DB_NAME=app
DB_USER=app_user
DB_PASSWORD=change-me

อ่านค่าจาก PHP ด้วย getenv()

<?php

$appName = getenv('APP_NAME') ?: 'PHP on Vercel';

หากต้องเชื่อมต่อ MySQL สามารถใช้ PDO ได้ดังนี้

<?php

$host = getenv('DB_HOST');
$port = getenv('DB_PORT') ?: '3306';
$name = getenv('DB_NAME');
$user = getenv('DB_USER');
$password = getenv('DB_PASSWORD');

$pdo = new PDO(
    "mysql:host={$host};port={$port};dbname={$name};charset=utf8mb4",
    $user,
    $password,
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
    ]
);

ควรเลือก Region ของ Function ให้ใกล้กับ Region ของฐานข้อมูลเพื่อลด Latency และควรใช้ฐานข้อมูลที่รองรับการเชื่อมต่อจาก Serverless พร้อมจำกัดจำนวน Connection อย่างเหมาะสม

#8. การจัดการไฟล์และ Session

Vercel Function ไม่ควรถูกมองว่าเป็น Web Server ที่มีดิสก์ถาวร ไฟล์ที่สร้างระหว่างการทำงานอาจไม่อยู่ในการเรียกครั้งถัดไป ดังนั้นควรออกแบบดังนี้

  • เก็บข้อมูลถาวรใน MySQL, PostgreSQL หรือฐานข้อมูลภายนอก
  • เก็บ Session ใน Redis หรือฐานข้อมูล แทน PHP File Session
  • เก็บรูปภาพและเอกสารใน Object Storage
  • อัปโหลดไฟล์ขนาดใหญ่จาก Browser ไปยัง Object Storage โดยตรง
  • ไม่ใช้ Local Disk เป็นแหล่งข้อมูลหลักของระบบ

Vercel ระบุว่า Request หรือ Response ของ Function มี Payload สูงสุด 4.5 MB จึงไม่ควรส่งไฟล์ขนาดใหญ่ผ่าน PHP Function โดยตรง

#9. ตรวจสอบ Log และแก้ปัญหา

ดู Deployment และ Runtime Logs ผ่าน Vercel Dashboard หรือใช้ CLI

vercel logs YOUR_DEPLOYMENT_URL

ปัญหาที่พบบ่อยมีดังนี้

#พบข้อความว่า Runtime ไม่รองรับ

ตรวจสอบว่า Runtime ระบุเวอร์ชันครบถ้วน

"runtime": "vercel-php@0.9.0"

และตรวจสอบว่าไฟล์ PHP อยู่ภายใต้ api และตรงกับ Key ใน functions

#เปิดหน้าเว็บแล้วพบ 404

ตรวจสอบ Destination ใน rewrites

"destination": "/api/index.php"

#ระบบทำงานในเครื่องแต่เกิด 500 บน Vercel

  • เปิด Runtime Logs เพื่อดู Exception จริง
  • ตรวจสอบชื่อ Environment Variables
  • ตรวจสอบ PHP Extension ที่ระบบต้องใช้
  • อย่าแสดง Secret หรือค่าจาก Environment Variables ใน Log
  • ตรวจสอบว่าขนาด Function รวม Dependency ไม่เกินข้อจำกัดของแพลตฟอร์ม

#Session หรือไฟล์หาย

ย้าย Session ไป Redis/Database และย้ายไฟล์ไป Object Storage เพราะ Function ไม่เหมาะกับการเก็บ State แบบถาวรใน Local Filesystem

#10. เหมาะกับงานประเภทใด

เหมาะกับ Vercel + PHP ควรใช้ PHP Hosting หรือ Container มากกว่า
เว็บ PHP ขนาดเล็ก Laravel ขนาดใหญ่ที่มี Worker หลายตัว
REST API หรือ Webhook Queue Worker ที่ต้องทำงานตลอดเวลา
Prototype และงานสาธิต Scheduler หรือ Cron ที่ซับซ้อน
Form Handler ระบบที่พึ่งพา Local File Session
Stateless Microservice ระบบอัปโหลดไฟล์ขนาดใหญ่ผ่าน Backend

สำหรับระบบขนาดใหญ่ แนวทางที่ดูแลได้ง่ายกว่าคือ Deploy Frontend บน Vercel และ Deploy PHP/Laravel API บนบริการที่รองรับ Container หรือ PHP โดยตรง จากนั้นเชื่อมต่อกันผ่าน HTTPS API

#สรุป

การ Deploy เว็บ PHP บน Vercel ทำได้ผ่าน Community Runtime vercel-php โดยมีขั้นตอนหลักดังนี้

  1. วาง Entry Point ไว้ที่ api/index.php
  2. กำหนด vercel-php@0.9.0 ใน vercel.json
  3. ใช้ rewrites ส่ง Request ไปยัง PHP Front Controller
  4. ทดสอบด้วย PHP Built-in Server หรือ vercel dev
  5. Deploy ด้วย vercel --prod หรือเชื่อมต่อ Git Repository
  6. เก็บฐานข้อมูล Session และไฟล์ถาวรไว้ในบริการภายนอก

หัวใจสำคัญคือการออกแบบระบบให้เป็น Stateless และเข้าใจว่า PHP ทำงานในรูปแบบ Function ไม่ใช่ Web Server ที่ทำงานต่อเนื่อง หากระบบสอดคล้องกับรูปแบบนี้ Vercel จะช่วยให้ Deploy ได้รวดเร็ว มี Preview Deployment และเชื่อมต่อ CI/CD ผ่าน Git ได้สะดวก

#แหล่งอ้างอิง