Mixed Content คืออะไร ทำไม HTTPS แต่ยังมีไอคอนไม่ปลอดภัย

Mixed Content คืออะไร

Mixed Content (เนื้อหาผสม) คือสถานการณ์ที่เว็บไซต์โหลดผ่าน HTTPS แต่มีทรัพยากรบางส่วนในหน้าเว็บนั้น เช่น รูปภาพ, สคริปต์ JavaScript, ไฟล์ CSS, หรือ iFrame ที่ยังโหลดผ่าน HTTP (ไม่เข้ารหัส) อยู่

ผลลัพธ์คือ แม้ URL หลักของเว็บจะเป็น https:// แต่เบราว์เซอร์จะไม่แสดงไอคอนแม่กุญแจสีเขียว หรืออาจแสดงไอคอนเตือน เพราะการเชื่อมต่อโดยรวมยังไม่ปลอดภัยอย่างสมบูรณ์ ผู้ใช้บางส่วนอาจเห็นคำเตือน "ไม่ปลอดภัย" แม้ว่าคุณจะติดตั้ง SSL ไปแล้วก็ตาม

Mixed Content มี 2 ประเภท

1. Passive Mixed Content (Warning)

ทรัพยากรที่โหลดผ่าน HTTP แต่ไม่สามารถแก้ไขเนื้อหาหน้าเว็บได้โดยตรง เช่น รูปภาพ (<img>), วิดีโอ, ไฟล์เสียง เบราว์เซอร์จะแสดงคำเตือนแต่ยังโหลดทรัพยากรนั้นให้ แม้จะไม่ปลอดภัยเต็มที่ก็ตาม

ตัวอย่างเช่น: หน้า HTTPS ที่มีรูปภาพจากโดเมนอื่นที่โหลดด้วย http://example.com/image.jpg

2. Active Mixed Content (Blocked)

ทรัพยากรที่สามารถแก้ไขหรือควบคุม DOM ของหน้าเว็บได้ เช่น JavaScript (<script>), CSS (<link rel="stylesheet">), iFrame, XMLHttpRequest เบราว์เซอร์จะ บล็อกทรัพยากรเหล่านี้เลยเพราะเป็นความเสี่ยงด้านความปลอดภัยระดับสูง ทำให้ฟังก์ชันบางอย่างของเว็บไม่ทำงาน

สาเหตุที่พบบ่อย: การย้ายเว็บจาก HTTP มาเป็น HTTPS โดยไม่ได้อัปเดต URL ของทรัพยากรทั้งหมดในฐานข้อมูลหรือในโค้ด ทำให้รูปภาพ สคริปต์ และลิงก์ต่างๆ ยังชี้ไปที่ http:// อยู่

Active vs Passive Mixed Content ต่างกันอย่างไร

หลายคนสับสนว่าทำไมบางหน้าที่มี Mixed Content ยังเปิดได้ปกติ (แค่ไม่มีแม่กุญแจ) ในขณะที่บางหน้ากลับฟังก์ชันพังไปเลย คำตอบอยู่ที่ว่าทรัพยากร HTTP นั้นเป็นแบบ Passive หรือ Active เพราะเบราว์เซอร์ปฏิบัติกับสองประเภทนี้ต่างกันโดยสิ้นเชิง การเข้าใจความต่างนี้จะช่วยให้คุณจัดลำดับความสำคัญในการแก้ปัญหาได้ถูกจุด

Passive Mixed Content คือทรัพยากรที่ "แสดงผล" อย่างเดียวแต่ควบคุมหน้าเว็บไม่ได้ เช่น รูปภาพ วิดีโอ และไฟล์เสียง แม้จะโหลดผ่าน HTTP เบราว์เซอร์ก็ยอมให้โหลดต่อเพราะความเสี่ยงจำกัด ผู้โจมตีที่ดักจับการเชื่อมต่อ (Man-in-the-Middle) ทำได้มากสุดเพียงสลับรูปภาพหรือดูว่าผู้ใช้กำลังดูอะไรอยู่ แต่ผลกระทบที่ตามมาคือเบราว์เซอร์จะลดสถานะความปลอดภัยของทั้งหน้า แม่กุญแจหายไปและขึ้นคำเตือน "ไม่ปลอดภัยทั้งหมด"

Active Mixed Content คือทรัพยากรที่ "ควบคุม" หน้าเว็บได้ เช่น JavaScript, CSS, iframe, fetch/XMLHttpRequest และ web font หากผู้โจมตีดักและแทนที่สคริปต์ที่โหลดผ่าน HTTP ได้ เขาจะรันโค้ดอะไรก็ได้ในหน้าเว็บของคุณ ขโมยคุกกี้ เปลี่ยนเนื้อหา หรือ redirect ผู้ใช้ไปหน้าปลอม ด้วยความเสี่ยงระดับสูงนี้ เบราว์เซอร์สมัยใหม่ (Chrome, Firefox, Edge, Safari) จึง บล็อกทิ้งทันที โดยไม่ถาม ผลคือสไตล์หน้าเว็บเพี้ยน ปุ่มกดไม่ทำงาน หรือฟอร์มส่งข้อมูลไม่ได้

หัวข้อ Passive Active
ตัวอย่างทรัพยากร img, video, audio script, link CSS, iframe, fetch
พฤติกรรมเบราว์เซอร์ โหลดต่อ + คำเตือน บล็อกทิ้งทันที
ผลกระทบ แม่กุญแจหาย ฟังก์ชันพัง สไตล์เพี้ยน
ความเร่งด่วน ปานกลาง สูงมาก

สรุปง่ายๆ คือ Active Mixed Content ต้องแก้ก่อนเสมอเพราะทำให้เว็บใช้งานไม่ได้จริง ส่วน Passive ก็ควรแก้ให้หมดเช่นกันเพื่อให้แม่กุญแจกลับมา แต่ไม่ถึงขั้นทำให้เว็บล่ม การไล่แก้จึงควรเริ่มจากสคริปต์และ CSS ก่อน แล้วค่อยตามด้วยรูปภาพและมีเดียทีหลัง

หา Mixed Content ด้วย Browser Console และ Why No Padlock

ตรวจสอบผ่าน Browser DevTools

  1. เปิดหน้าเว็บในเบราว์เซอร์ Chrome
  2. กด F12 หรือคลิกขวา → Inspect เพื่อเปิด DevTools
  3. คลิก Tab Console
  4. มองหาข้อความสีแดงหรือสีเหลืองที่ขึ้นต้นด้วย "Mixed Content:" เช่น "Mixed Content: The page at 'https://...' was loaded over HTTPS, but requested an insecure resource 'http://...'"
  5. URL ที่ปรากฏในข้อความเตือนคือทรัพยากรที่ต้องแก้ไข

ตรวจสอบผ่านเครื่องมือออนไลน์

เว็บไซต์อย่าง "Why No Padlock" (whynopadlock.com) หรือ "SSL Checker" จะสแกนหน้าเว็บของคุณและแสดงรายการทรัพยากร HTTP ทั้งหมดที่พบ สะดวกกว่าการดูใน Console ทีละหน้า เพราะมันไล่ดูทุก resource ในหน้าให้อัตโนมัติ เหมาะกับเว็บที่มีหลายหน้าและคุณไม่อยากเปิด DevTools ทีละหน้า ข้อจำกัดคือเครื่องมือเหล่านี้ตรวจได้เฉพาะหน้าที่เข้าถึงได้จากภายนอก (public URL) เท่านั้น หน้าหลังบ้านหรือหน้าที่ต้องล็อกอินจะตรวจไม่ได้

อีกวิธีที่นักพัฒนานิยมคือดูที่แท็บ Security ใน Chrome DevTools ซึ่งจะสรุปให้ว่าหน้านี้มี mixed content หรือไม่ พร้อมระบุว่าเป็น resource ตัวไหน และคลิกเข้าไปดูรายละเอียดได้ทันที ส่วนแท็บ Network ก็ช่วยกรอง request ที่เป็น http:// ได้ด้วยการดูคอลัมน์ Scheme หรือ Protocol

แก้ Mixed Content ทีละวิธี (DB Search-Replace, Protocol-relative, CSP)

เมื่อรู้แล้วว่ามี resource ตัวไหนที่ยังเป็น http:// ก็ถึงเวลาแก้จริง มีหลายวิธีตั้งแต่แก้ที่ต้นเหตุไปจนถึงวิธีปิดช่องโหว่แบบครอบทั้งหน้า ควรเลือกให้เหมาะกับสถานการณ์ ดังนี้

วิธีที่ 1: Search-Replace URL ในฐานข้อมูล (แก้ต้นเหตุ)

ถ้า URL http:// ถูกบันทึกอยู่ในฐานข้อมูล (เช่นใน CMS ที่เก็บเนื้อหาเป็น HTML) การไล่แก้ที่ฐานข้อมูลคือทางที่ตรงที่สุด สำหรับ MySQL ทั่วไปสามารถใช้คำสั่ง UPDATE แบบนี้ (สำรองข้อมูลก่อนทุกครั้ง):

-- สำรอง table ก่อนเสมอ
-- mysqldump -u user -p dbname wp_posts > backup.sql

UPDATE wp_posts
SET post_content = REPLACE(post_content,
    'http://yourdomain.com',
    'https://yourdomain.com');

UPDATE wp_postmeta
SET meta_value = REPLACE(meta_value,
    'http://yourdomain.com',
    'https://yourdomain.com')
WHERE meta_value LIKE '%http://yourdomain.com%';

ข้อควรระวัง: ถ้าข้อมูลถูกเก็บแบบ serialize (เช่น option หรือ widget บางตัวใน WordPress) การ REPLACE ตรงๆ จะทำให้ตัวนับความยาว string ใน serialized data ผิดเพี้ยนและข้อมูลพัง จึงควรใช้เครื่องมือที่จัดการ serialized data ให้ถูกต้อง เช่น WP-CLI หรือปลั๊กอินเฉพาะทาง แทนการรัน SQL ดิบกับ field เหล่านั้น

# WP-CLI: ปลอดภัยกับ serialized data
wp search-replace 'http://yourdomain.com' 'https://yourdomain.com' \
    --all-tables --dry-run

# ตรวจผลแล้วค่อยรันจริง (เอา --dry-run ออก)
wp search-replace 'http://yourdomain.com' 'https://yourdomain.com' \
    --all-tables

วิธีที่ 2: Protocol-relative URL (//)

ในกรณีที่คุณคุมโค้ดเทมเพลตเอง สามารถเปลี่ยน URL ให้เป็นแบบ protocol-relative โดยตัด http: ออกเหลือแค่ // เบราว์เซอร์จะเลือก protocol ตามหน้าเว็บปัจจุบันให้อัตโนมัติ ถ้าหน้าเป็น HTTPS ก็จะโหลด resource ผ่าน HTTPS ตามไปด้วย:

<!-- ก่อน: บังคับ http เสมอ -->
<script src="http://cdn.example.com/lib.js"></script>

<!-- หลัง: protocol-relative ตามหน้าเว็บ -->
<script src="//cdn.example.com/lib.js"></script>

<!-- ดีที่สุด: ระบุ https ตรงๆ ไปเลย -->
<script src="https://cdn.example.com/lib.js"></script>

หมายเหตุ: ปัจจุบันแนะนำให้ระบุ https:// ตรงๆ มากกว่าใช้ // เพราะเว็บเกือบทั้งหมดเป็น HTTPS หมดแล้ว และ protocol-relative อาจมีปัญหาเวลาเปิดไฟล์ HTML แบบ local (protocol เป็น file://)

วิธีที่ 3: CSP upgrade-insecure-requests (แก้แบบครอบทั้งหน้า)

ถ้ามี resource http:// กระจายอยู่หลายจุดจนไล่แก้ทีละตัวไม่ไหว สามารถสั่งให้เบราว์เซอร์ "อัปเกรด" ทุก request จาก http เป็น https โดยอัตโนมัติด้วย Content Security Policy directive upgrade-insecure-requests ใส่ได้ทั้งใน meta tag หรือ HTTP response header:

<!-- วางใน <head> ของทุกหน้า -->
<meta http-equiv="Content-Security-Policy"
      content="upgrade-insecure-requests">
# หรือใส่เป็น header ใน .htaccess (Apache)
Header always set Content-Security-Policy "upgrade-insecure-requests"

# Nginx
add_header Content-Security-Policy "upgrade-insecure-requests";

directive นี้จะบังคับให้ทุก resource ที่ระบุเป็น http:// ของโดเมนเดียวกันถูกโหลดผ่าน https แทน เป็นวิธีปิดปัญหาแบบเร็วและปลอดภัย แต่ควรถือเป็นตัวช่วย ไม่ใช่ข้ออ้างที่จะไม่แก้ URL ต้นทาง เพราะถ้า resource ต้นทางไม่มี HTTPS จริงๆ มันก็จะโหลดไม่สำเร็จอยู่ดี

วิธีแก้ Mixed Content ใน WordPress

วิธีที่ 1: Plugin Really Simple SSL

Plugin นี้คือทางออกที่ง่ายที่สุดสำหรับผู้ใช้ WordPress ติดตั้งและเปิดใช้งานปุ่มเดียว Plugin จะ:

วิธีที่ 2: เปลี่ยน WordPress URL เป็น HTTPS

  1. เข้าสู่ WordPress Admin → Settings → General
  2. เปลี่ยน WordPress Address (URL) และ Site Address (URL) จาก http:// เป็น https://
  3. กด Save Changes

วิธีที่ 3: Search & Replace URL ในฐานข้อมูล

ใช้ Plugin Better Search Replace เพื่อค้นหาและแทนที่ http://yourdomain.com ด้วย https://yourdomain.com ในฐานข้อมูลทั้งหมด วิธีนี้แก้ปัญหาที่รากเหง้าได้ดีที่สุด ควรสำรองข้อมูลก่อนทำ:

  1. ติดตั้งและเปิดใช้งาน Plugin Better Search Replace
  2. ไปที่ Tools → Better Search Replace
  3. ใส่ http://yourdomain.com ในช่อง Search for
  4. ใส่ https://yourdomain.com ในช่อง Replace with
  5. เลือกตาราง Database ทั้งหมด
  6. ทดสอบด้วย Dry Run ก่อน แล้วจึงรันจริง

วิธีที่ 4: ใช้ Really Simple SSL ร่วมกับ Better Search Replace

สำหรับ WordPress การทำงานร่วมกันของสองปลั๊กอินนี้คือสูตรที่ครอบคลุมที่สุด เริ่มจากใช้ Really Simple SSL เพื่อบังคับ HTTPS, ตั้งค่า redirect และเปิด upgrade-insecure-requests ให้อัตโนมัติ ปลั๊กอินนี้ยังมีฟีเจอร์สแกนหา mixed content ในตัว และจะเตือนถ้ายังมี resource ที่ปลดไม่ได้ จากนั้นใช้ Better Search Replace เพื่อกวาด URL http:// ที่ฝังลึกในฐานข้อมูลออกให้หมด ซึ่งจัดการ serialized data ได้อย่างปลอดภัย ต่างจากการรัน SQL ดิบเอง

ลำดับที่แนะนำคือ: สำรองฐานข้อมูล → รัน Better Search Replace แบบ Dry Run ดูจำนวนที่จะถูกแก้ → รันจริง → เปิด Really Simple SSL เพื่อจัดการ header และ redirect → ล้างแคช (ทั้งปลั๊กอินแคชและ CDN) → เปิด DevTools Console เช็คซ้ำว่าไม่มี mixed content เหลือ การล้างแคชสำคัญมาก เพราะหน้าเก่าที่ถูกแคชไว้ก่อนแก้อาจยังมี URL http:// ค้างอยู่ ทำให้เข้าใจผิดว่าแก้ไม่หาย

วิธีแก้ Mixed Content ในเว็บทั่วไป (ไม่ใช่ WordPress)

ค้นหาในโค้ด HTML, CSS และ JavaScript ทั้งหมด แล้วแทนที่ URL ที่ขึ้นต้นด้วย http:// เป็น https:// หรือใช้ Protocol-relative URL ในรูปแบบ //example.com/resource แทน ซึ่งจะเลือก Protocol ตามหน้าเว็บนั้นเองโดยอัตโนมัติ นอกจากในไฟล์ HTML แล้วอย่าลืมตรวจ URL ที่ฝังอยู่ใน CSS ด้วย โดยเฉพาะ url() ของ background-image และ @import รวมถึง URL ที่ JavaScript สร้างขึ้นแบบ dynamic เช่นการต่อ string เป็น endpoint ของ API ซึ่งมักถูกมองข้ามเพราะไม่ได้อยู่ใน HTML ตรงๆ

หากใช้ระบบ build (เช่น Webpack, Vite) ให้ตรวจค่า config ที่กำหนด base URL หรือ public path ว่าเป็น https หรือ relative ไม่ใช่ http hardcode และถ้าเว็บอยู่หลัง reverse proxy หรือ load balancer ที่ทำ SSL termination ให้ตั้ง header X-Forwarded-Proto ให้ถูก เพื่อให้แอปพลิเคชันรู้ว่า request เข้ามาแบบ https และสร้างลิงก์ภายในเป็น https ตาม

CDN และ Third-Party Resources

บริการภายนอก เช่น Google Fonts, jQuery CDN, Facebook SDK หรือบริการ Chat ส่วนใหญ่รองรับ HTTPS แล้ว ตรวจสอบว่าใน Code ที่คุณใช้ URL เหล่านั้นขึ้นต้นด้วย https:// ไม่ใช่ http:// หากบริการใดยังไม่รองรับ HTTPS ให้พิจารณาเปลี่ยนไปใช้บริการที่รองรับ

คำถามที่พบบ่อยเรื่อง Mixed Content

ติดตั้ง SSL แล้วทำไมเว็บยังขึ้น "ไม่ปลอดภัย"

เพราะ SSL ทำให้ "การเชื่อมต่อหลัก" ของหน้าเป็น HTTPS ก็จริง แต่ถ้าในหน้านั้นยังมี resource (รูป สคริปต์ CSS) ที่โหลดผ่าน http:// อยู่ เบราว์เซอร์จะถือว่าหน้านี้ไม่ปลอดภัยทั้งหมดและถอดแม่กุญแจออก ปัญหานี้ไม่ได้อยู่ที่ใบ SSL แต่อยู่ที่ URL ของ resource ภายในหน้า เมื่อแก้ทุก URL ให้เป็น https แล้วแม่กุญแจจะกลับมาเอง

Mixed Content ส่งผลต่อ SEO และอันดับ Google ไหม

มีผลทางอ้อม Google ให้ HTTPS เป็นปัจจัยจัดอันดับ และคำเตือน "ไม่ปลอดภัย" ทำให้ผู้ใช้ตีกลับ (bounce) สูงขึ้น ซึ่งเป็นสัญญาณลบ นอกจากนี้ Active Mixed Content ที่ถูกบล็อกอาจทำให้สคริปต์วิเคราะห์หรือฟังก์ชันสำคัญพัง กระทบประสบการณ์ผู้ใช้ การแก้ให้หมดจึงดีต่อทั้งความปลอดภัยและ SEO

แก้ Mixed Content แล้วแต่บางหน้ายังเตือนอยู่ ทำไม

สาเหตุที่พบบ่อยที่สุดคือแคช ทั้งแคชเบราว์เซอร์ แคชปลั๊กอิน และแคชที่ CDN หน้าเก่าที่ถูกแคชไว้ก่อนแก้ยังมี URL http:// ค้างอยู่ ให้ล้างแคชทุกชั้นแล้วเปิดแบบ Incognito ทดสอบใหม่ อีกสาเหตุคือ resource ที่ถูกสร้างแบบ dynamic ด้วย JavaScript หรือฝังใน CSS ซึ่งการ search-replace ฐานข้อมูลอาจตกหล่นไป

upgrade-insecure-requests ใช้แทนการแก้ URL ได้เลยไหม

ใช้เป็นตัวช่วยได้ แต่ไม่ควรพึ่งอย่างเดียว directive นี้จะอัปเกรด request จาก http เป็น https ให้อัตโนมัติก็จริง แต่ถ้า resource ต้นทางไม่มีเวอร์ชัน HTTPS จริงๆ มันก็จะโหลดไม่สำเร็จอยู่ดี ทางที่ดีที่สุดคือแก้ URL ต้นทางให้ถูกต้อง แล้วใช้ upgrade-insecure-requests เป็นตาข่ายกันพลาดอีกชั้น

ต้องการ SSL Certificate สำหรับเว็บของคุณ?

DV SSL เริ่มต้น 1,000 บาท/ปี หรือ Let's Encrypt ฟรีสำหรับลูกค้า Hosting AsiaGB

ดู SSL Certificate