SSD Nodes Learn
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-07-24

วิธีติดตั้ง LAMP stack บน Ubuntu 24.04

คู่มือติดตั้ง Apache, MariaDB, PHP 8.3 และ PHP-FPM บน Ubuntu 24.04 พร้อมวิธีตั้งค่า vhost และ Certbot เพื่อใช้งาน HTTPS อย่างถูกต้องและปลอดภัย

สิ่งที่คุณกำลังจะสร้าง

LAMP stack ประกอบด้วยส่วนประกอบ 4 ส่วนบน Ubuntu 24.04 server หนึ่งเครื่อง ได้แก่ Linux เป็นระบบปฏิบัติการพื้นฐาน, Apache สำหรับตอบรับ HTTP, MariaDB สำหรับจัดเก็บข้อมูล และ PHP 8.3 สำหรับรันโค้ด เมื่อเสร็จสิ้นกระบวนการ คุณจะมี name-based virtual host ที่ให้บริการ application directory จริง, ฐานข้อมูลที่มี user แบบ least-privilege เฉพาะเจาะจง, การเชื่อมต่อ PHP เข้ากับ Apache ผ่าน PHP-FPM และการติดตั้ง Let's Encrypt certificate ให้เรียบร้อย

ขั้นตอนการติดตั้งประกอบด้วยคำสั่ง apt จำนวน 4 คำสั่ง เนื้อหาเกือบทั้งหมดในคู่มือนี้คือการเชื่อมต่อส่วนประกอบต่างๆ เข้าด้วยกัน รวมถึงการแก้ไขข้อผิดพลาดเล็กน้อยที่อาจทำให้ stack ใหม่แสดงผลเป็นหน้าว่าง, ส่งไฟล์ source code ให้ browser ดาวน์โหลด หรือไม่สามารถเข้าใช้งาน database ที่เพิ่งติดตั้งได้ ข้อผิดพลาดเหล่านี้มีลักษณะเฉพาะที่คุณสามารถสังเกตได้ และแต่ละกรณีจะถูกระบุไว้ด้านล่างพร้อมข้อความที่จะปรากฏขึ้นจริง

สิ่งที่ต้องเตรียมและข้อควรระวัง

สมมติว่าคุณใช้ Ubuntu 24.04 KVM VPS ที่ติดตั้งใหม่ โดยมีผู้ใช้ที่มีสิทธิ์ sudo หรือ root และมี public IPv4 address ระบบแบบ minimal สามารถทำงานได้บน RAM ขนาด 1 GB อย่างไรก็ตาม ควรเพิ่ม RAM เป็น 2 GB ก่อนติดตั้งแอปพลิเคชันที่ใช้งานฐานข้อมูลจริง เนื่องจาก buffer เริ่มต้นของ MariaDB และ PHP-FPM workers จำนวนหนึ่งจะใช้หน่วยความจำใน 1 GB แรกไปอย่างรวดเร็ว

มีสองเงื่อนไขที่ต้องทำให้สำเร็จเพื่อให้ Certbot ทำงานได้ในตอนท้าย โปรดเตรียมการดังนี้: คุณต้องมี domain name ที่มี A record ชี้ไปยัง public IP ของ VPS เนื่องจาก Let's Encrypt จะตรวจสอบผ่าน HTTP ไปยังชื่อโดเมนนั้น และไม่สามารถขอใบรับรองผ่าน IP address โดยตรงได้ นอกจากนี้ ต้องสามารถเข้าถึง port 80 และ 443 จากอินเทอร์เน็ตได้ ซึ่งสำหรับผู้ให้บริการหลายราย หมายถึงการเปิด port เหล่านี้ใน network firewall บน control panel รวมถึง ใน ufw บนเครื่องด้วย การเปลี่ยนแปลงค่า DNS อาจใช้เวลาในการ propagate สูงสุด 1 ชั่วโมง ดังนั้นควรตั้งค่า A record ไว้ล่วงหน้าเพื่อให้พร้อมใช้งานเมื่อถึงเวลาที่จำเป็น

Step 1 - ติดตั้ง Apache และตรวจสอบหน้าเว็บเริ่มต้น

sudo apt update
sudo apt install -y apache2

apt จะทำการ start และ enable service ให้โดยอัตโนมัติ ตรวจสอบได้ดังนี้:

systemctl status apache2

คุณต้องพบข้อความ active (running) ให้เปิด http://YOUR_SERVER_IP/ ใน web browser หากพบหน้า Apache2 Ubuntu Default Page ที่มีแถบข้อความ "It works!" ขนาดใหญ่ แสดงว่าถูกต้องแล้ว หน้าเว็บนี้เป็นหลักฐานว่า Apache กำลังทำงานอยู่ ไม่ใช่ข้อผิดพลาด หน้าเว็บนี้อยู่ที่ /var/www/html/index.html และถูกให้บริการโดย default virtual host 000-default.conf ที่มาพร้อมกับระบบ คุณจะต้องปิดการใช้งานทั้งสองอย่างในภายหลัง ในขณะนี้การที่พบหน้าเว็บนี้ถือเป็นสิ่งที่ถูกต้องแล้ว

หากหน้าเว็บไม่โหลดแต่ systemctl ระบุว่า process กำลังทำงานอยู่ แสดงว่ามี firewall ขวางกั้นอยู่ ซึ่งเป็นขั้นตอนถัดไป

Step 2 - Open the firewall for HTTP and HTTPS

The apache2 package registers three ufw application profiles. List them:

sudo ufw app list

You will see Apache, Apache Full, and Apache Secure. Apache is port 80 only, Apache Secure is 443 only, and Apache Full is both — that is the one you want, because you are adding TLS at the end.

sudo ufw allow OpenSSH
sudo ufw allow "Apache Full"
sudo ufw enable

Allow OpenSSH before you run ufw enable. ufw defaults to denying all incoming traffic, and enabling it without an SSH rule cuts your own connection the moment it activates — you keep the current session but can never reconnect. Confirm with sudo ufw status; you want OpenSSH, Apache Full, and their v6 equivalents all reading ALLOW.

Step 3 - ติดตั้ง MariaDB และตั้งค่าความปลอดภัย

sudo apt install -y mariadb-server
systemctl status mariadb

Ubuntu 24.04 มาพร้อมกับ MariaDB 10.11 ซึ่งเป็นรุ่น long-term-support ดังนั้นคุณจึงไม่จำเป็นต้องใช้ external repository เมื่อ service ทำงานแล้ว ให้ดำเนินการเพิ่มความปลอดภัยดังนี้:

sudo mysql_secure_installation

ควรอ่านข้อความที่ปรากฏขึ้นแทนที่จะกด Enter ทันที เมื่อระบบถามหา current root password ให้กด Enter เนื่องจากยังไม่มีการตั้งค่าไว้ เมื่อระบบถามว่า "Switch to unix_socket authentication?" คำตอบจะไม่มีผลใดๆ เพราะแพ็กเกจนี้เปิดใช้งานไว้แล้ว ให้กด n เมื่อระบบถามว่า "Change the root password?" ให้ตอบ n ด้วยเหตุผลในย่อหน้าถัดไป จากนั้นให้ตอบ Y สำหรับคำถามที่เหลือ ได้แก่ การลบ anonymous users, การไม่อนุญาตให้ root login จากระยะไกล, การลบ test database และการ reload privilege tables

ส่วนนี้เป็นส่วนที่สร้างความสับสนให้กับผู้ใช้ทุกคน ใน MariaDB บน Ubuntu บัญชีฐานข้อมูล root จะใช้การยืนยันตัวตนแบบ unix_socket แทนการใช้รหัสผ่าน นั่นหมายความว่าฐานข้อมูลจะเชื่อถือ user ของ operating-system ที่คุณได้ยืนยันตัวตนไว้แล้ว ดังนั้นคำสั่งนี้จึงใช้งานได้จาก root shell:

sudo mysql

...และระบบจะพาคุณเข้าสู่ MariaDB [(none)]> prompt โดยไม่ถามรหัสผ่าน หากรันคำสั่งเดียวกันนี้ด้วย unprivileged user ระบบจะปฏิเสธการเข้าถึง ซึ่งนี่คือวัตถุประสงค์หลัก การเข้าถึง database root จะผูกติดกับ sudo บนเครื่อง และไม่มีรหัสผ่านให้ถูกขโมย, ถูก phish หรือถูก brute-force วิธีนี้มีความปลอดภัยมากกว่าการใช้รหัสผ่าน ดังนั้นควรปล่อยไว้ตามเดิม กฎที่ตามมาคือ: ห้าม เชื่อมต่อ application เข้ากับบัญชี root โดยเด็ดขาด ให้สร้าง user แยกเฉพาะสำหรับแต่ละ application (Step 7) เนื่องจาก application ที่เชื่อมต่อผ่าน TCP ด้วย username และ password ไม่สามารถใช้ socket auth ได้ และคุณควรจำกัดให้แต่ละ application เข้าถึงได้เฉพาะฐานข้อมูลของตนเองเท่านั้น

Step 4 - ติดตั้ง PHP 8.3 พร้อม PHP-FPM

PHP เริ่มต้นของ Ubuntu 24.04 คือเวอร์ชัน 8.3 ให้ติดตั้ง FPM process manager และ extension ที่แอปพลิเคชันทั่วไปจำเป็นต้องใช้:

sudo apt install -y php8.3-fpm php8.3-mysql php8.3-cli \
  php8.3-curl php8.3-xml php8.3-mbstring php8.3-zip

สังเกตสิ่งที่ ไม่ได้ อยู่ในรายการนั้น: libapache2-mod-php แพ็กเกจเวอร์ชันเก่านี้จะฝัง PHP interpreter ไว้ในทุก process ของ Apache ซึ่งใช้งานง่ายแต่ไม่เป็นประสิทธิภาพ เนื่องจากทุก worker จะต้องแบกสำเนา PHP ไว้เสมอไม่ว่าจะกำลังประมวลผล script หรือส่งไฟล์รูปภาพแบบ static นอกจากนี้ทั้งสองส่วนยังมี lifecycle ร่วมกัน และใช้งานได้เฉพาะกับ Apache's prefork MPM ซึ่งเป็นรูปแบบที่มีประสิทธิภาพต่ำที่สุดเท่านั้น ในทางกลับกัน PHP-FPM จะรัน PHP เป็น pool ของ process แยกต่างหาก โดย Apache จะสื่อสารผ่าน socket วิธีนี้ทำให้ Apache สามารถใช้ threaded event MPM สำหรับไฟล์ static และส่งเฉพาะ request ของ PHP ไปยัง pool เท่านั้น นอกจากนี้ pool ดังกล่าวยังสามารถปรับแต่งแยกจาก web server ได้ และการตั้งค่า FPM แบบเดียวกันนี้ยังสามารถใช้งานได้หากเปลี่ยนไปใช้ nginx เป็น proxy ด้านหน้าด้วย เหตุผลที่วิธีนี้เป็นค่าเริ่มต้นในปัจจุบันเนื่องจากประสิทธิภาพที่เหนือกว่า

Apache เชื่อมต่อกับ FPM ผ่านโมดูล proxy_fcgi ให้ทำการเปิดใช้งานโมดูลนี้ เปิดใช้งาน configuration ที่ติดตั้งมาพร้อมกับแพ็กเกจ FPM แล้วทำการ restart:

sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2

a2enconf php8.3-fpm จะเปิดใช้งาน /etc/apache2/conf-available/php8.3-fpm.conf ซึ่งมีกฎสำหรับการส่งไฟล์ PHP ไปยัง FPM socket โดยหัวใจสำคัญคือการจับคู่ไฟล์ .php และส่งต่อไปยัง socket ที่ /run/php/php8.3-fpm.sock:

<FilesMatch ".+\.ph(ar|p|tml)$">
    SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>

คุณไม่จำเป็นต้องแก้ไขไฟล์ดังกล่าวเนื่องจากมีการตั้งค่าที่ถูกต้องมาให้แล้ว แต่การทราบ socket path จะช่วยให้คุณวินิจฉัยปัญหา "PHP ดาวน์โหลดแทนที่จะรัน" และ "Primary script unknown" ได้ในภายหลัง ซึ่งทั้งสองปัญหานี้เกิดจากการที่ Apache และ FPM สื่อสารตำแหน่ง socket หรือไฟล์ไม่ตรงกัน

Step 5 - การตั้งค่า name-based virtual host สำหรับแอปพลิเคชันของคุณ

การทำ name-based virtual hosting ช่วยให้ IP เดียวสามารถให้บริการได้หลายเว็บไซต์ โดย Apache จะเลือกเว็บไซต์จาก header Host: ใน request ให้สร้าง directory สำหรับแอปพลิเคชัน โดยแยกออกจาก /var/www/html เริ่มต้น:

sudo mkdir -p /var/www/testapp
sudo chown -R www-data:www-data /var/www/testapp
sudo chmod -R 755 /var/www/testapp

การกำหนดสิทธิ์ความเป็นเจ้าของ (Ownership) มีความสำคัญ เนื่องจาก Apache และ PHP-FPM ทำงานภายใต้ user www-data บน Ubuntu ดังนั้นไฟล์ที่ web server ต้องอ่าน และ directory ที่แอปพลิเคชันต้องเขียนข้อมูลลงไป เช่น folder สำหรับ uploads ควรเป็นของ www-data หากคุณต้องการแก้ไขไฟล์ด้วย user ที่ใช้ login วิธีที่นิยมคือการเป็นเจ้าของไฟล์ด้วยตนเองและเพิ่ม user ของคุณลงใน group www-data แต่สำหรับการติดตั้งแบบทั่วไป การใช้ www-data:www-data จะทำให้เกิดปัญหาที่คาดไม่ถึงน้อยที่สุด

สร้าง virtual host ที่ /etc/apache2/sites-available/testapp.conf:

<VirtualHost *:80>
    ServerName app.example.com
    DocumentRoot /var/www/testapp

    <Directory /var/www/testapp>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/testapp-error.log
    CustomLog ${APACHE_LOG_DIR}/testapp-access.log combined
</VirtualHost>

ตั้งค่า ServerName เป็น domain จริงของคุณ ตั้งค่า Options -Indexes เพื่อป้องกันไม่ให้ Apache แสดงรายการไฟล์ใน directory เมื่อไม่มีไฟล์ index มิฉะนั้นผู้เข้าชมจะสามารถไล่ดูโครงสร้าง source tree ได้ การตั้งค่า AllowOverride All จะทำให้ไฟล์ .htaccess ใช้งานได้ ซึ่งแอปพลิเคชัน PHP ส่วนใหญ่จำเป็นต้องใช้สำหรับ pretty URLs หากแอปพลิเคชันของคุณไม่ได้ใช้งานส่วนนี้ สามารถนำออกที่ None เพื่อเพิ่มความเร็วในการทำงานเล็กน้อย ให้เปิดใช้งาน site นี้ ปิดการใช้งาน default site ตรวจสอบความถูกต้องของ config และ reload:

sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2

apache2ctl configtest ควรแสดงผลลัพธ์เป็น Syntax OK บรรทัด a2dissite 000-default คือส่วนที่ผู้ใช้งานมักลืม ซึ่งเป็นสาเหตุที่ทำให้หน้า default ยังคงแสดงผลอยู่ — รายละเอียดระบุไว้ในส่วนของข้อผิดพลาด (failures section)

Step 6 - ตรวจสอบการทำงานของ PHP และลบไฟล์ทดสอบ

สร้างไฟล์ PHP ขนาดหนึ่งบรรทัดไว้ที่ app root:

echo "<?php phpinfo();" | sudo tee /var/www/testapp/info.php

เข้าชมหน้าเว็บที่ http://app.example.com/info.php ผลลัพธ์ที่ถูกต้องคือตาราง PHP Version 8.3.x สีม่วงสลับเทา ซึ่งแสดงรายการ modules ที่โหลดอยู่ โดยต้องมีบรรทัด Server API ระบุว่า FPM/FastCGI บรรทัดสุดท้ายนี้เป็นการยืนยันว่า request ถูกส่งผ่าน PHP-FPM ไม่ใช่ mod_php

จากนั้นให้ลบไฟล์ทิ้งทันที:

sudo rm /var/www/testapp/info.php

phpinfo() จะแสดงข้อมูล PHP version ที่แน่นอน, extension ทั้งหมดที่โหลดอยู่, path ของไฟล์ และรายละเอียดของ environment ซึ่งเป็นข้อมูลสำคัญสำหรับผู้ที่ต้องการโจมตี server ด้วยช่องโหว่ของเวอร์ชันนั้นๆ ข้อมูลนี้มีไว้เพื่อการทดสอบเท่านั้นไม่ใช่ฟีเจอร์ของระบบ ให้ลบไฟล์ทิ้งทันทีหลังจากตรวจสอบหน้าเว็บเสร็จสิ้น หาก browser แสดงคำสั่งให้ download info.php แทนการแสดงตาราง แสดงว่า PHP ยังไม่ได้เชื่อมต่อกับ Apache ให้ไปที่ส่วนการแก้ไขปัญหา (failures section) ก่อนดำเนินการอื่นใด

Step 7 - สร้าง database ของแอปพลิเคชันและ user ที่มีสิทธิ์จำกัด (least-privilege)

เปิด database ด้วยสิทธิ์ root ที่ยืนยันตัวตนผ่าน socket:

sudo mysql

จากนั้นสร้าง database หนึ่งชุด และสร้าง user หนึ่งรายที่มีสิทธิ์เฉพาะใน database นั้นเท่านั้น:

CREATE DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;

การตั้งค่านี้มีเหตุผลสำคัญ 3 ประการ utf8mb4 คือรูปแบบ four-byte UTF-8 ของจริง เนื่องจาก alias utf8 แบบเดิมจะตัดข้อมูล emoji และตัวอักษร CJK บางตัวทิ้งโดยไม่แจ้งให้ทราบ ดังนั้นควรใช้ utf8mb4 เสมอ การกำหนดสิทธิ์ (grant) จะทำบน appdb.* ไม่ใช่ *.* เพื่อให้ user นี้เข้าถึงได้เฉพาะ database ของตนเองเท่านั้น หากแอปพลิเคชันถูกโจมตีด้วย SQL-injection จะไม่สามารถอ่านตารางของเว็บไซต์อื่นได้ และ 'appuser'@'localhost' จะจำกัดให้บัญชีนี้เชื่อมต่อได้จากภายในเครื่องนี้เท่านั้น

ทดสอบการใช้งานด้วย user ดังกล่าว:

mysql -u appuser -p appdb

ระบบจะถามรหัสผ่านและเข้าสู่หน้าจอ prompt ของ MariaDB [appdb]> สังเกตว่าไม่มี flag -h การไม่ใส่ flag นี้จะทำให้ client เชื่อมต่อผ่าน local Unix socket ซึ่งเป็นสิ่งที่ MariaDB ถือว่าเป็น localhost ข้อควรระวังที่สำคัญคือ สำหรับ MySQL และ MariaDB localhost หมายถึง Unix socket และ 127.0.0.1 หมายถึงการเชื่อมต่อผ่าน TCP ใน MariaDB บน Ubuntu 24.04 มาตรฐาน เซิร์ฟเวอร์จะแปลงการเชื่อมต่อ TCP จาก 127.0.0.1 กลับไปเป็น localhost ทำให้ทั้งสองแบบตรงกับบัญชีที่สร้างไว้ แต่ในเซิร์ฟเวอร์ที่เปิดใช้งาน skip-name-resolve (ซึ่งเป็นเทคนิคเพิ่มประสิทธิภาพที่นิยมใช้และเป็นมาตรฐานใน container images จำนวนมาก) ระบบจะมองว่าทั้งสองแบบเป็น host ที่ต่างกัน หากแอปพลิเคชันเชื่อมต่อผ่าน 127.0.0.1 จะถูกปฏิเสธด้วยข้อผิดพลาด ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES) แม้จะใส่รหัสผ่านถูกต้องก็ตาม

ดังนั้น โปรดตั้งค่าแอปพลิเคชันให้ชี้ไปที่ host localhost, user appuser, database appdb — ห้ามใช้ root โดยเด็ดขาด ทั้ง mysqli ของ PHP และ PDO จะเปลี่ยนไปใช้ Unix socket เมื่อ host คือข้อความ localhost ซึ่งจะตรงกับบัญชีที่คุณเพิ่งสร้าง หาก framework บังคับให้ใช้ host เป็นหมายเลข TCP ให้สร้าง user ให้ตรงกับวิธีการเชื่อมต่อจริง คือใช้ 'appuser'@'127.0.0.1' หรือใช้ @'%' (ร่วมกับกฎของ firewall) เฉพาะในกรณีที่จำเป็นต้องเชื่อมต่อจากเครื่องอื่นเท่านั้น

Step 8 - เพิ่ม HTTPS ด้วย Certbot

การส่งฟอร์มเข้าสู่ระบบผ่าน HTTP ปกติ จะทำให้รหัสผ่านถูกส่งเป็นข้อความธรรมดา (clear text) และเบราว์เซอร์สมัยใหม่ทุกรุ่นจะแจ้งเตือนหน้าเว็บว่า "Not secure" Certbot สามารถแก้ไขปัญหานี้ได้ด้วยคำสั่งเดียว ติดตั้ง Certbot ด้วย Apache plugin ดังนี้:

sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache

Certbot ใช้ plugin สองตัวในที่นี้ apache authenticator ทำหน้าที่ยืนยันว่าคุณเป็นเจ้าของโดเมนโดยการส่งไฟล์ challenge ผ่าน Apache ที่กำลังทำงานอยู่ และ apache installer จะทำการแก้ไข virtual host เพื่อเพิ่ม block 443 โดยจะชี้ไปยังใบรับรองใหม่ และตั้งค่าให้ redirect traffic จาก HTTP ไปยัง HTTPS โดยค่าเริ่มต้น เนื่องจาก Certbot เวอร์ชัน 2.0 จะไม่มีคำถามเรื่องการ redirect หากต้องการใช้งาน HTTP ต่อไปให้ใช้ flag --no-redirect เนื่องจากคุณได้ตั้งค่า ServerName ไว้ใน Step 5 แล้ว Certbot จึงสามารถตรวจพบโดเมนได้โดยอัตโนมัติ ใบรับรองมีอายุการใช้งาน 90 วัน และแพ็กเกจนี้จะติดตั้ง systemd timer สำหรับการต่ออายุอัตโนมัติ ตรวจสอบ timer ได้ด้วยคำสั่ง sudo certbot renew --dry-run ซึ่งควรจะลงท้ายด้วย Congratulations, all simulated renewals succeeded

สำหรับรายละเอียดทั้งหมดเกี่ยวกับ challenge, การต่ออายุผ่าน timer รวมถึงข้อกำหนดด้าน DNS และ firewall สามารถอ่านเพิ่มเติมได้ที่คู่มือ issuing free Let's Encrypt TLS certificates with Certbot on Apache

Backups, upgrades, and hardening

สำรองข้อมูลสองส่วนที่เก็บสถานะของระบบ ได้แก่ databases และ web root วิธีที่ง่ายและเชื่อถือได้ที่สุดคือการทำ nightly logical dump โดยใช้ sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz' แล้วจึงคัดลอกไฟล์ออกนอกเครื่อง การใช้ sudo sh -c ครอบคลุมทั้ง pipeline เป็นสิ่งสำคัญ เนื่องจากหากไม่ใช้ shell จะรัน > /root/... redirect ด้วย user ของคุณเอง และจะเกิดข้อผิดพลาด Permission denied เพราะมีเพียง mysqldump เท่านั้นที่ได้รับสิทธิ์ sudo การใช้ --single-transaction จะช่วยให้ได้ snapshot ของ InnoDB tables ที่สอดคล้องกันโดยไม่ต้องทำการ lock tables เมื่อใช้ร่วมกับ tar ของ /var/www และ /etc/apache2/sites-available คุณจะสามารถติดตั้ง stack ทั้งหมดใหม่บน VPS เครื่องใหม่จากไฟล์เหล่านี้ได้

การอัปเกรดเป็นขั้นตอน sudo apt update && sudo apt upgrade ปกติ แต่สิ่งที่อาจสร้างปัญหาคือ การเปลี่ยนเวอร์ชัน PHP เมื่อ Ubuntu เวอร์ชันในอนาคตเปลี่ยนค่าเริ่มต้นเป็น PHP 8.4 ตัว apt อาจติดตั้ง php8.4-fpm ควบคู่ไปกับ 8.3 ทำให้ socket เปลี่ยนเป็น /run/php/php8.4-fpm.sock ในขณะที่ Apache config ยังคงชี้ไปที่ socket ของ 8.3 คุณต้องเปิดใช้งาน conf ใหม่ (sudo a2enconf php8.4-fpm) และปิดการใช้งานอันเก่า มิฉะนั้นเว็บไซต์จะส่งคืนค่า Primary script unknown หลังจากอัปเกรดตามปกติ เนื่องจาก PHP มีการออกเวอร์ชันใหม่เร็วกว่า distro แบบ LTS จึงควรตรวจสอบบันทึกการออกเวอร์ชัน PHP ปัจจุบัน แทนการระบุ patch version แบบตายตัว

ขั้นตอนการทำ hardening สองขั้นตอนที่ควรทำตั้งแต่วันแรก คือ หนึ่ง ให้ติดตั้ง Fail2Ban เพื่อเฝ้าระวัง SSH เนื่องจาก VPS ที่เปิดสาธารณะจะถูกสุ่มล็อกอินภายในไม่กี่นาที การใช้ jail ขนาดเล็กจะช่วยเปลี่ยนความพยายามล็อกอินหลายพันครั้งให้เหลือเพียงไม่กี่ครั้งก่อนจะทำการ ban สอง หากคุณต้องการจัดการ Apache virtual hosts, MariaDB databases และ users ผ่าน browser แทนการแก้ไขไฟล์ด้วยตนเอง Webmin web-based control panel สามารถทำงานบน stack ชุดนี้และจัดการไฟล์ config ชุดเดียวกับที่คุณเพิ่งเขียนขึ้นมา ทั้งสองเครื่องมือไม่ได้มาแทนที่การทำความเข้าใจระบบ แต่ช่วยลดความยุ่งยากในการทำงานประจำวัน

Failure modes, with the strings you will see

The default page will not go away. You edited your virtual host, reloaded, and the browser still shows "Apache2 Ubuntu Default Page" and its "It works!" banner. Apache serves the first matching virtual host, and when no ServerName matches the request, the alphabetically first config wins — 000-default.conf sorts before testapp.conf. Either the request's host name does not match your ServerName, or you never ran sudo a2dissite 000-default. Disable the default, sudo systemctl reload apache2, and confirm with apache2ctl -S, which prints the vhost map and shows which config owns the default. Clear the browser cache too; a cached 200 from the old page happily persists.

A .php file downloads instead of running. You open info.php and the browser downloads a file containing the raw <?php source, or shows it as plain text, instead of running it. Apache is serving the file as a static asset because the PHP handler is not attached — you skipped sudo a2enmod proxy_fcgi, or sudo a2enconf php8.3-fpm, or did not restart Apache afterwards. Run all three (Step 4) and reload. Confirm the module is loaded with apache2ctl -M | grep fcgi, which should list proxy_fcgi_module. This is a source-code leak, not a cosmetic bug, so fix it before putting anything real on the server.

ERROR 1698 (28000): Access denied for user 'root'@'localhost'. You ran mysql -u root or mariadb -u root without sudo. The root account uses unix_socket auth, so it only accepts you when your OS user is actually root. The fix is sudo mysql — no -u root, no password. This message is the expected behaviour of socket auth working correctly, not a broken install.

ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' from the application, with the right password. The account exists as 'appuser'@'localhost', but your app is connecting over TCP to 127.0.0.1 on a server where host-name resolution is disabled (skip-name-resolve), so MariaDB matches the two as different hosts — localhost is the Unix socket, 127.0.0.1 is TCP. Point the app at host localhost so it uses the socket and matches the account, or create a second account 'appuser'@'127.0.0.1' if the framework will only speak TCP.

AH01071: Got error 'Primary script unknown' in /var/log/apache2/testapp-error.log, with the browser showing File not found.. Apache handed the request to PHP-FPM, but FPM could not find the script at the path Apache gave it. Two usual causes: the FPM socket in your config points at a PHP version that is not installed (a php8.4 socket after an upgrade while only 8.3 runs), or the file genuinely is not there because DocumentRoot and the real directory disagree. Check the socket exists with ls -l /run/php/, confirm DocumentRoot matches where the file lives, and restart both php8.3-fpm and apache2.

AH00558: apache2: Could not reliably determine the server's fully qualified domain name on every restart. This is a harmless warning, not an error — Apache is telling you no global ServerName is set. Silence it by writing ServerName your.domain into /etc/apache2/conf-available/servername.conf and running sudo a2enconf servername.

(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80 when Apache starts. Another web server already holds port 80 — often a stray nginx from a previous experiment. Find it with sudo ss -ltnp | grep :80, then stop and disable the other service before starting Apache.

FAQ

mod_php หรือ PHP-FPM - ควรเลือกใช้ตัวไหน?

ควรใช้ PHP-FPM เนื่องจาก mod_php จะฝังตัวแปลคำสั่งไว้ในทุก process ของ Apache และบังคับให้ใช้ prefork MPM ซึ่งทำงานช้า ส่งผลให้ Apache ต้องรับภาระจาก PHP แม้ในขณะที่กำลังส่งไฟล์รูปภาพแบบ static ก็ตาม ส่วน PHP-FPM จะรัน PHP แยกเป็น pool ต่างหากที่สามารถปรับแต่งค่าได้อิสระ โดย Apache จะเชื่อมต่อผ่าน socket ซึ่งช่วยให้ทำงานร่วมกับ event MPM ที่ทำงานแบบ threaded ได้รวดเร็วกว่า และสามารถย้ายไปใช้ nginx ได้ทันทีในภายหลัง PHP-FPM คือค่าเริ่มต้นมาตรฐานในปัจจุบัน ส่วน mod_php เหมาะสำหรับแอปพลิเคชันรุ่นเก่าที่จำเป็นต้องใช้พฤติกรรมแบบ in-process เท่านั้น

ทำไมเบราว์เซอร์ถึงดาวน์โหลดไฟล์ PHP แทนที่จะรันไฟล์?

Apache กำลังจัดการไฟล์ .php ในรูปแบบการดาวน์โหลดไฟล์ static เนื่องจากไม่มี PHP handler ผูกไว้กับไฟล์ดังกล่าว สำหรับ Ubuntu 24.04 ที่ใช้ FPM ปัญหานี้เกิดจากการที่ไม่ได้ทำ sudo a2enmod proxy_fcgi, sudo a2enconf php8.3-fpm หรือไม่ได้ restart Apache ให้เรียบร้อย ให้รันทั้ง 3 คำสั่งแล้ว reload จากนั้นตรวจสอบด้วย apache2ctl -M | grep fcgi ว่ามี proxy_fcgi_module ปรากฏอยู่หรือไม่ หากยังไม่แก้ไข เซิร์ฟเวอร์จะทำให้ source code รั่วไหล จึงควรดำเนินการแก้ไขโดยด่วน

ทำไมถึงถูกปฏิเสธการเข้าถึง root ใน MariaDB แม้จะใช้รหัสผ่านที่ถูกต้องแล้ว?

เนื่องจากไม่มีการใช้รหัสผ่าน MariaDB บน Ubuntu จะตรวจสอบสิทธิ์บัญชี root ผ่าน unix_socket ซึ่งเชื่อมโยงกับ user root ของระบบปฏิบัติการ การใช้ mysql -u root จาก shell ปกติจะคืนค่า ERROR 1698 (28000): Access denied for user 'root'@'localhost' ตามการออกแบบของระบบ ให้เชื่อมต่อด้วย sudo mysql แทน และควรสร้าง user ใหม่ที่ใช้การตรวจสอบสิทธิ์ด้วยรหัสผ่านสำหรับแอปพลิเคชัน แทนการใช้ root

ฉันจะเพิ่ม HTTPS ให้กับไซต์ LAMP ได้อย่างไร?

ติดตั้ง certbot และ python3-certbot-apache จากนั้นชี้ A record ของโดเมนมาที่เซิร์ฟเวอร์ แล้วรัน sudo certbot --apache ตัวยืนยันตัวตนของ apache จะพิสูจน์ความเป็นเจ้าของโดเมนผ่าน Apache ที่กำลังทำงานอยู่ และตัวติดตั้งจะเขียน virtual host ใหม่สำหรับ port 443 พร้อมตั้งค่าการต่ออายุอัตโนมัติ สามารถอ่านรายละเอียดเพิ่มเติมได้ที่ คู่มือการใช้งาน Certbot และ Apache แบบเต็ม ซึ่งจะครอบคลุมทั้งขั้นตอนการยืนยันตัวตน, การตั้งเวลาต่ออายุ และสาเหตุที่ทำให้เกิดข้อผิดพลาดทั่วไป

#lamp#apache#mariadb#php-fpm#ubuntu