แคชหลายภาษาของ WP Rocket: สาเหตุที่แคชของหน้าแปลหายไปเมื่อบันทึกฉบับร่าง และวิธีแก้

แคชหลายภาษาของ WP Rocket คือแคชหน้าเว็บที่บันทึกแยกไว้ใต้โฟลเดอร์ภาษาสำหรับ URL หน้าแปลของ TranslatePress แต่ละ URL บนเซิร์ฟเวอร์ WordPress แบบ Nginx + Apache ได้ไล่ดูโค้ดของ WP Rocket เพื่อหาสาเหตุที่แคชของหน้าแปลหายไปทั้งโฟลเดอร์ภาษาทุกครั้งที่บันทึกฉบับร่าง และแก้ได้ด้วยโค้ดที่ยกเว้นโพสต์ที่ยังไม่เผยแพร่จากการลบแคชของหน้าแปล พร้อมแจ้ง preload อีกครั้งสำหรับ URL ที่ลบเอง
ไบต์แรกที่วัดภายในเซิร์ฟเวอร์เดียวกันคือ 0.015 วินาทีเมื่อมีแคชของหน้าแปล และ 2.492 วินาทีเมื่อไม่มี เมื่อโฟลเดอร์ภาษาว่างลง ผู้เข้าชมหน้าแปลจะได้รับการตอบสนองที่ช้าแบบนี้ไปจนกว่าจะมีคนเปิด URL นั้นอีกครั้งและแคชถูกสร้างใหม่
1️⃣ โครงสร้างแคชหลายภาษาของ WP Rocket: โฟลเดอร์แยกตามภาษาและการล้างหน้าแปล
หน้าแปลคือหน้าที่แสดงโพสต์ต้นฉบับเป็นภาษาอื่น จึงเข้าใจได้ง่ายว่าแคชก็น่าจะรวมอยู่กับต้นฉบับ แต่ WP Rocket แคช URL ของหน้าแปลเป็นหน้าแยกต่างหาก ใต้โฟลเดอร์แคชจะมีโฟลเดอร์ภาษาอย่าง [en] และ [ja] อยู่ข้างโฟลเดอร์โพสต์ต้นฉบับ และโพสต์หน้าแปลจะถูกบันทึกไว้ข้างในด้วยชื่อสลักที่แปลแล้ว
เมื่อแก้ไขโพสต์ การเชื่อมต่อ TranslatePress ของ WP Rocket จะลบแคชของหน้าแปลของโพสต์นั้นไปด้วย เอกสารทางการก็ระบุว่าการแก้ไขทั่วไปจะล้างหน้าแปลทุกภาษา ส่วนการแก้ในตัวแก้ไขคำแปลของ TranslatePress จะล้างเฉพาะภาษาที่เปลี่ยน
// wp-rocket/inc/ThirdParty/Plugins/I18n/TranslatePress.php (3.23.5.1)
'post_updated' => 'clear_post_languages', // ทำงานทุกครั้งที่บันทึกโพสต์
public function clear_post_languages( $post_id ) {
// …
$default_permalink = get_permalink( $post_id ); // ไม่ตรวจสอบสถานะโพสต์
foreach ( $trp_settings['translation-languages'] as $language ) {
if ( $language === $trp_settings['default-language'] ) {
continue;
}
$clear_urls[] = $converter->get_url_for_language( $language, $default_permalink, '' );
}
// …
rocket_clean_files( $clear_urls );
}ฟังก์ชันเชื่อมต่อจะแปลง URL ของโพสต์เป็น URL หน้าแปลของแต่ละภาษา แล้วส่งต่อให้ฟังก์ชันล้างไฟล์แคช [rocket_clean_files] ในขั้นตอนนี้ไม่มีบรรทัดที่ตรวจสอบสถานะโพสต์ ไม่ว่าโพสต์จะเผยแพร่แล้วหรือเป็นฉบับร่าง URL หน้าแปลจึงถูกสร้างและลบด้วยลำดับเดียวกัน
2️⃣ บันทึกฉบับร่าง: โฟลเดอร์ภาษาถูกลบทั้งโฟลเดอร์
การบันทึกฉบับร่างไม่ได้ไม่เกี่ยวกับแคชของหน้าแปล ก่อนเผยแพร่ ลิงก์ถาวรของโพสต์อยู่ในรูป [?p=หมายเลขโพสต์] ไม่ใช่สลัก และ TranslatePress แปลง URL นี้เป็น URL ที่เติมเส้นทางภาษาอย่าง [/en/?p=หมายเลขโพสต์]
[rocket_clean_files] ตัดคิวรีออกจาก URL แล้วหาตำแหน่งแคชจากเส้นทางเพียงอย่างเดียว ถ้าเส้นทางที่เหลือคือ [/en/] ก็หมายถึงโฟลเดอร์แคชทั้งโฟลเดอร์ของภาษานั้น และถ้าเป้าหมายเป็นโฟลเดอร์ ก็จะลบแบบเวียนซ้ำไปถึงโฟลเดอร์ย่อย
// wp-rocket/inc/functions/files.php (3.23.5.1) — ภายใน rocket_clean_files()
$parsed_url = get_rocket_parse_url( $url ); // แยกเส้นทางกับคิวรี
// …
$entry = $dir . $parsed_url['path']; // …/example.com/en/
// …
if ( $filesystem->is_dir( $entry ) ) {
rocket_rrmdir( $entry, [], $filesystem ); // ลบแบบเวียนซ้ำทั้งโฟลเดอร์
} else {
$filesystem->delete( $entry );
}หน้าแรกก็ผ่านเส้นทางเดียวกัน URL หน้าแรก [/] ถูกแปลงเป็น [/en/] แม้บันทึกหน้าแรกที่เผยแพร่แล้วก็ลบโฟลเดอร์ภาษาทั้งโฟลเดอร์ แค่แก้ชื่อเรื่องของฉบับร่างแล้วบันทึกอีกครั้ง โฟลเดอร์แคชของทั้งสี่ภาษาก็ว่าง และจากเวลาบันทึกกับบันทึกการลบแคช ยืนยันได้ว่าการลบแบบเดียวกันเกิดซ้ำมากกว่า 30 ครั้งในคืนเดียวที่มีการบันทึกฉบับร่างหนาแน่น
ตามโค้ด การบันทึกอัตโนมัติในตัวแก้ไขก็ผ่านเส้นทางเดียวกัน รุ่นแก้ไขที่บันทึกระหว่างที่คุณแก้โพสต์ที่เผยแพร่ก็ใช้ URL รูปแบบ [?p=] เช่นกัน
บนเว็บไซต์ทดสอบแบบ Nginx + Apache ได้แก้โพสต์ที่เผยแพร่ในตัวแก้ไขพร้อมนับจำนวนไฟล์ในโฟลเดอร์ภาษา หลังบันทึกอัตโนมัติครั้งแรก โฟลเดอร์ภาษาอังกฤษและญี่ปุ่นยังมีอย่างละ 20 ไฟล์ และหลังบันทึกอัตโนมัติครั้งที่สองในอีก 1 นาทีต่อมา ทั้งสองโฟลเดอร์เหลือ 0 ไฟล์
การบันทึกอัตโนมัติครั้งแรกจะสร้างรุ่นแก้ไขใหม่ ตั้งแต่ครั้งที่สองจะแก้รุ่นแก้ไขเดิมแล้วบันทึก [post_updated] จึงทำงาน ถ้าคุณแก้เนื้อหาต่อหลังบันทึกอัตโนมัติครั้งแรก แคชของหน้าแปลจะถูกลบทั้งโฟลเดอร์ภาษาแม้ไม่ได้กดปุ่มบันทึก
อาการเดียวกันนี้เปิดอยู่ในคลังทางการของ WP Rocket เป็น issue #6130 ตั้งแต่วันที่ 31 สิงหาคม 2023 pull request #7616 ที่แก้ปัญหาถูกส่งขึ้นมาในเดือนกันยายน 2025 แต่ยังไม่ถูกรวม และบันทึกการเปลี่ยนแปลงของ 3.23.5.1 ที่ออกเมื่อวันที่ 2 ตุลาคม 2026 ก็ไม่มีการแก้ไขนี้
3️⃣ โค้ดแก้ไข: ลบแคชของหน้าแปลเฉพาะโพสต์ที่เผยแพร่
ระหว่างที่ยังไม่มีการแก้ไขทางการ จะถอดฟังก์ชันเชื่อมต่อออกแล้วใช้ฟังก์ชันที่ลบหน้าแปลเฉพาะเมื่อ URL แน่นอนแทน วิธีเลี่ยงที่โพสต์ไว้ในคอมเมนต์ของ issue ก็ไปในทางเดียวกัน คือออกจากฟังก์ชันเชื่อมต่อทันทีถ้าโพสต์ยังไม่เผยแพร่
แต่เงื่อนไขที่ตรวจแค่สถานะโพสต์ยังเหลือสองกรณี หน้าแรกแม้อยู่ในสถานะเผยแพร่ก็ลบโฟลเดอร์ภาษาทั้งโฟลเดอร์เมื่อบันทึก และถ้าลดโพสต์ที่เผยแพร่เป็นฉบับร่าง แคชของหน้าแปลจะไม่ถูกลบ โพสต์ที่ถอดออกจึงยังเปิดได้ที่ URL หน้าแปล
<?php
/**
* Plugin Name: Rocket TranslatePress Draft Fix
* Description: จำกัดการลบแคชหน้าแปลของการเชื่อมต่อ TranslatePress ใน WP Rocket ให้เหลือเฉพาะกรณีที่ URL แน่นอน
*/
// 1. ถอดฟังก์ชันเชื่อมต่อของ WP Rocket ออกจาก post_updated แล้วเก็บไว้
add_action( 'init', function () {
global $wp_filter;
if ( empty( $wp_filter['post_updated'] ) ) {
return;
}
foreach ( $wp_filter['post_updated']->callbacks as $priority => $callbacks ) {
foreach ( $callbacks as $cb ) {
$fn = $cb['function'];
if ( is_array( $fn ) && is_object( $fn[0] )
&& 0 === strpos( get_class( $fn[0] ), 'WP_Rocket\\' )
&& false !== stripos( get_class( $fn[0] ), 'TranslatePress' ) ) {
remove_action( 'post_updated', $fn, $priority );
$GLOBALS['rtdf_original'] = $fn;
}
}
}
}, 9999 );
// 2. ฟังก์ชันที่ใช้แทน
add_action( 'post_updated', function ( $post_id, $after, $before ) {
if ( empty( $GLOBALS['rtdf_original'] ) || ! class_exists( 'TRP_Translate_Press' ) ) {
return;
}
$trp = TRP_Translate_Press::get_trp_instance();
$converter = $trp->get_component( 'url_converter' );
$settings = $trp->get_component( 'settings' )->get_settings();
$languages = array_diff( $settings['translation-languages'], [ $settings['default-language'] ] );
// หน้าแรก — เฉพาะไฟล์แคชของหน้าแรกของภาษา ไม่ใช่โฟลเดอร์ภาษา
if ( 'page' === get_option( 'show_on_front' ) && (int) get_option( 'page_on_front' ) === (int) $post_id ) {
$urls = [];
add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
foreach ( $languages as $lang ) {
$home = $converter->get_url_for_language( $lang, home_url( '/' ), '' );
$dir = WP_ROCKET_CACHE_PATH . wp_parse_url( $home, PHP_URL_HOST ) . wp_parse_url( $home, PHP_URL_PATH );
array_map( 'unlink', glob( $dir . 'index*.html*' ) ?: [] );
$urls[] = $home;
}
remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
do_action( 'rocket_after_clean_terms', $urls ); // แจ้ง preload (5️⃣)
return;
}
// บันทึกโพสต์ที่เผยแพร่ — ใช้ฟังก์ชันเดิมของ WP Rocket ตามเดิม
if ( 'publish' === $after->post_status ) {
call_user_func( $GLOBALS['rtdf_original'], $post_id );
return;
}
// เผยแพร่ → ฉบับร่าง·ส่วนตัว·ถังขยะ — เฉพาะหน้าแปลของ URL ก่อนถอดออก
if ( 'publish' === $before->post_status ) {
$old = get_permalink( $before );
$urls = [];
add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
foreach ( $languages as $lang ) {
$urls[] = $converter->get_url_for_language( $lang, $old, '' );
}
remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
rocket_clean_files( $urls );
}
// นอกนั้น (ฉบับร่าง·รุ่นแก้ไข·บันทึกอัตโนมัติ) ไม่มี URL หน้าแปลจึงไม่ลบ
}, 10, 3 );ส่วนแรกจะหาฟังก์ชันเชื่อมต่อที่ WP Rocket ลงทะเบียนไว้ ถอดออกจาก [post_updated] และเก็บฟังก์ชันเดิมไว้ ฟังก์ชันที่ใช้แทนจะลบเฉพาะไฟล์แคชของหน้าแรกของภาษาเมื่อเป็นหน้าแรก เรียกฟังก์ชันเดิมที่เก็บไว้ตามเดิมเมื่อเป็นโพสต์ที่เผยแพร่ และเมื่อถอดโพสต์ที่เผยแพร่ออก จะหาหน้าแปลจาก URL ก่อนถอดออกแล้วลบ
ถ้าบันทึกเป็นไฟล์ PHP ในโฟลเดอร์ [wp-content/mu-plugins/] จะมีผลทันทีโดยไม่ต้องเปิดใช้งาน และถ้าลบไฟล์ WP Rocket จะกลับไปทำงานแบบเดิม ผลการทดสอบด้วยโค้ดที่ทำงานแบบเดียวกัน บันทึกฉบับร่างซ้ำแล้วจำนวนไฟล์ในโฟลเดอร์ภาษายังเท่าเดิม และบันทึกโพสต์ที่เผยแพร่ซ้ำแล้วลบเฉพาะโฟลเดอร์ต้นฉบับและหน้าแปลของโพสต์นั้น
ก่อนและหลังใส่โค้ดข้างบน ได้เทียบจำนวนไฟล์ในโฟลเดอร์ภาษาของสี่กรณีในสภาพแวดล้อมเดียวกัน โฟลเดอร์ภาษาอังกฤษและญี่ปุ่นมีไฟล์แคช PC·มือถือของหน้าแรกของภาษาและโพสต์สี่โพสต์อย่างละ 20 ไฟล์
ก่อนใส่โค้ด การบันทึกฉบับร่าง การบันทึกหน้าแรก และการยกเลิกการเผยแพร่ ทั้งสามกรณีทำให้ทั้งสองโฟลเดอร์เหลือ 0 ไฟล์ และมีเพียงการบันทึกโพสต์ที่เผยแพร่ที่ลบเฉพาะแคชหน้าแปลของโพสต์นั้น
หลังใส่โค้ด การบันทึกฉบับร่างและการบันทึกอัตโนมัติในตัวแก้ไขยังเหลือ 20 ไฟล์ครบ การบันทึกหน้าแรกลบเฉพาะไฟล์แคชของหน้าแรกของภาษา 4 ไฟล์ ส่วนการบันทึกโพสต์ที่เผยแพร่และการยกเลิกการเผยแพร่ลบเฉพาะหน้าแปลของโพสต์นั้น 4 ไฟล์จนเหลือ 16 ไฟล์ และ URL หน้าแปลภาษาอังกฤษของโพสต์ที่ถอดออกตอบกลับ 404
| กรณี | ก่อนแก้ | หลังใส่โค้ด | แคชที่ถูกลบหลังใส่โค้ด |
|---|---|---|---|
| บันทึกฉบับร่าง | 20 → 0 | 20 → 20 | ไม่มี |
| บันทึกโพสต์ที่เผยแพร่ | ลบเฉพาะหน้าแปลของโพสต์นั้น | 20 → 16 | หน้าแปลของโพสต์นั้น 4 ไฟล์ |
| บันทึกหน้าแรก | 20 → 0 | 20 → 16 | แคชหน้าแรกของภาษา 4 ไฟล์ |
| ยกเลิกการเผยแพร่ | 20 → 0 | 20 → 16 | หน้าแปลของโพสต์นั้น 4 ไฟล์ · URL หน้าแปล 404 |
4️⃣ preload ไม่สร้างใหม่: สถานะเสร็จที่ค้างอยู่ในคิว
แม้จะกันการบันทึกฉบับร่างแล้ว ไฟล์แคชที่ถูกลบโดยไม่มีสัญญาณการลบจากฟังก์ชันของ WP Rocket จะไม่ถูก preload เติมกลับ preload ของ WP Rocket ใช้ตารางคิวที่บันทึกสถานะของแต่ละ URL โดย URL ที่รอส่งคำขอเป็นสถานะรอ URL ที่ส่งคำขอแล้วเป็นกำลังดำเนินการ URL ที่สร้างแคชแล้วเป็นเสร็จ และถ้าไม่เสร็จภายใน 3 นาทีจะเป็นล้มเหลว
URL ที่เสร็จแล้วจะกลับไปเป็นสถานะรอในสามกรณี คือไฟล์ถูกลบเพราะหมดอายุแคช WP Rocket ส่งสัญญาณการลบขณะล้าง หรือผ่านไปเกิน 12 ชั่วโมงหลังล้มเหลว ถ้าส่ง false เป็นอาร์กิวเมนต์ที่สามของ [rocket_clean_files] หรือลบไฟล์โดยตรง สัญญาณนี้จะไม่ถูกส่ง ไฟล์แคชจึงหายไปทั้งที่ในคิวยังเป็นสถานะเสร็จ
โค้ดที่รับสัญญาณการลบแล้วล้างแคชอื่นต่อจะส่ง false เป็นอาร์กิวเมนต์ที่สามเพื่อกันการเรียกซ้ำ ตอนตรวจเทียบแคชของหน้าแปล หน้าแปลภาษาอังกฤษ 3 จาก 65 หน้า และภาษาญี่ปุ่น 7 จาก 65 หน้า ยังเป็นสถานะเสร็จในคิวทั้งที่ไม่มีไฟล์แคช
ผู้ร่วมพัฒนาจากทีมซัพพอร์ตของ WP Rocket ก็ยอมรับในคอมเมนต์ของ issue #6130 ว่าหลังล้างแคช URL ในตารางยังคงเป็นสถานะเสร็จทำให้สถานะไม่ตรงกัน ข้อเสนอให้เทียบตารางกับไฟล์แคชเป็นระยะ (#2939) ถูกปิดไปในปี 2020 โดยไม่ได้นำไปใช้
5️⃣ ลงทะเบียน preload ใหม่: แจ้งคิวเรื่อง URL ที่ลบเอง
URL ที่ลบเองแก้ได้ด้วยบรรทัดเดียวที่แจ้ง preload ทันทีหลังลบ เมื่อ preload ได้รับสัญญาณ [rocket_after_clean_terms] จะคืน URL ที่รับมาเป็นสถานะรอ และเพิ่ม URL ที่ยังไม่มีในตาราง
// หลังลบโดยไม่มีสัญญาณการลบ — ลงทะเบียนอาร์เรย์ URL ที่ลบไว้ในคิว preload อีกครั้ง
rocket_clean_files( $urls, null, false ); // อาร์กิวเมนต์ที่สาม false = ไม่มีสัญญาณการลบ
do_action( 'rocket_after_clean_terms', $urls ); // preload คืน URL เหล่านี้เป็นสถานะรอ (pending)ภายใน WP Rocket ส่วนที่รับสัญญาณนี้มีแค่ฟังก์ชันคิว preload และการเชื่อมต่อโฮสติ้ง SpinUpWP และฟังก์ชัน preload แก้เฉพาะแถวในคิว ไม่ได้ลบไฟล์แคชของ WP Rocket หรือส่งสัญญาณการลบซ้ำ จึงไม่เกิดการเรียกซ้ำ เว็บไซต์ที่ล้างแคชผ่านฟังก์ชันของ WP Rocket อย่างเดียวและปล่อยอาร์กิวเมนต์ที่สามเป็นค่าเริ่มต้นจะส่งสัญญาณอยู่แล้ว จึงไม่ต้องใช้บรรทัดนี้
หลังใส่บรรทัดนี้ เมื่อล้างแคชของโพสต์หนึ่งที่มีหน้าแปลสี่ภาษา แถวในคิวสี่แถวเปลี่ยนเป็นสถานะรอภายใน 2 วินาที ทดสอบสองครั้งบนเซิร์ฟเวอร์ที่รัน cron ทุกนาที ไฟล์แคช PC·มือถือของหน้าแปลทั้ง 8 ไฟล์ถูกสร้างใหม่หลังผ่านไป 101 วินาทีและ 153 วินาที ตรวจได้จากสถานะในตารางคิวและไฟล์ในโฟลเดอร์แคช
# สถานะของ URL นั้นในตารางคิว (wp_ หน้าชื่อตารางคือคำนำหน้าของเว็บไซต์)
wp db query <<'SQL'
SELECT url, status, modified FROM wp_wpr_rocket_cache WHERE url LIKE '%/en/sample-post-en'
SQLurl status modified
https://example.com/en/sample-post-en pending 2026-10-17 09:10:48# ไฟล์แคชกลับมาหรือยังหลังผ่านไปราว 1 นาที
ls -l wp-content/cache/wp-rocket/example.com/en/sample-post-en/total 240
-rw-r--r-- 1 example nobody 95084 Oct 17 18:12 index-https.html
-rw-r--r-- 1 example nobody 22352 Oct 17 18:12 index-https.html_gzip
-rw-r--r-- 1 example nobody 95083 Oct 17 18:12 index-mobile-https.html
-rw-r--r-- 1 example nobody 22348 Oct 17 18:12 index-mobile-https.html_gzipทันทีหลังลบ สถานะจะเปลี่ยนเป็น [pending] และเมื่อ preload ส่งคำขอแล้วไฟล์แคชถูกสร้าง ก็จะเป็น [completed] ถ้าสถานะเป็น [completed] แต่ไม่มีไฟล์แคช แสดงว่า URL นั้นถูกลบโดยไม่มีสัญญาณ
เมื่อบันทึกหน้าแรกแล้วโค้ดข้างบนลบไฟล์แคชของหน้าแรกของภาษาและส่งสัญญาณ URL หน้าแรกภาษาอังกฤษและญี่ปุ่นก็กลับเข้าคิว แคช PC·มือถือของหน้าแรกของภาษาถูกสร้างใหม่ช้าที่สุดในการรัน cron ครั้งที่สอง
ทดสอบสองครั้ง ตั้งแต่บันทึกจนถึงไฟล์แคชไฟล์สุดท้ายใช้เวลา 47 วินาทีและ 91 วินาที
🔢 FAQ & เนื้อหาแนะนำ














ℹ️ ประกาศเรื่องลิงก์แอฟฟิลิเอต
เนื้อหาของเว็บไซต์นี้มีลิงก์แอฟฟิลิเอต หากผู้เข้าชมซื้อสินค้าหรือบริการผ่านลิงก์เหล่านี้ เว็บไซต์นี้จะได้รับค่าคอมมิชชันจากผู้ขาย ในกระบวนการนี้ ราคาที่ผู้ซื้อจ่าย(ราคาจะลดลงเมื่อมีโปรโมชัน↓)จะไม่สูงขึ้นแต่อย่างใด ราคา ส่วนลด และข้อมูลสต็อกที่แสดงเป็นข้อมูล ณ เวลาที่เขียนและอาจต่างจากความเป็นจริง โปรดตรวจสอบกับผู้ขายอีกครั้งก่อนซื้อ การเลือกและการประเมินสินค้าเขียนตามเกณฑ์ของเราเอง และการได้รับค่าคอมมิชชันหรือไม่ไม่มีผลต่อลำดับการแนะนำหรือเนื้อหาการประเมิน

