Tài liệu tổng hợp toàn bộ cách setup môi trường debug web trên iOS và Android từ máy Mac — cho cả trường hợp có device thật và không có device (dùng simulator/emulator).

| Phương án | Nền tảng | Cần device? | DevTools đầy đủ | Độ chính xác |
|---|---|---|---|---|
| Safari Web Inspector | iOS | Có | ✅ | Cao nhất |
| iOS Simulator + Safari | iOS | Không | ✅ | Cao (thiếu perf thật) |
Chrome chrome://inspect | Android | Có | ✅ | Cao nhất |
| Android Emulator (AVD) | Android | Không | ✅ | Cao (stock Android) |
| eruda / vConsole | Cả hai | Không quan trọng | ⚠️ Rút gọn | Trung bình |
| BrowserStack / LambdaTest | Cả hai | Không | ✅ | Cao (device thật) |
Nguyên tắc chọn:
Cài đặt (Settings)
→ Apps → Safari (iOS 18+; iOS cũ hơn: Settings → Safari)
→ Nâng cao (Advanced)
→ Bật "Web Inspector"
Safari → Settings (⌘,)
→ Advanced
→ Tích "Show features for web developers"
Sau bước này thanh menu sẽ xuất hiện mục Develop.
Sau khi đã kết nối bằng cáp ít nhất 1 lần:
Safari → Develop → [Tên iPhone] → Connect via Network
Rút cáp ra, thiết bị vẫn hiện trong menu Develop khi cùng mạng WiFi.
SFSafariViewController.Cách 1 — App Store (đơn giản nhất, ~10GB):
Mở App Store → tìm "Xcode" → Install
Cách 2 — Homebrew:
brew install --cask xcode
Sau khi cài, chạy 1 lần để accept license:
sudo xcodebuild -license accept
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
open -a Simulator
Hoặc từ Xcode: Xcode → Open Developer Tool → Simulator.
Chọn model thiết bị: File → Open Simulator → iOS 18.x → iPhone 16 Pro (hoặc model cần test).
Xcode → Settings → Components → iOS Simulator Runtimes → dấu "+"
Rất hữu ích khi cần tái hiện bug chỉ xảy ra trên iOS 15/16.
Simulator được nhận diện y hệt device thật:
Safari → Develop → Simulator (iPhone 16 Pro) → [tab đang mở]
Không cần bật Web Inspector thủ công trên simulator — mặc định đã bật.
| Thao tác | Cách làm |
|---|---|
| Xoay màn hình | ⌘ + ← / ⌘ + → |
| Chụp màn hình | ⌘ + S |
| Quay video | ⌘ + R |
| Về Home | ⌘ + Shift + H |
| Mở URL trực tiếp | Kéo thả URL vào cửa sổ, hoặc xcrun simctl openurl booted "https://..." |
| Xoá dữ liệu Safari | Simulator → Device → Erase All Content and Settings |
| Giả lập vị trí | Features → Location → Custom Location |
# Liệt kê device
xcrun simctl list devices
# Boot 1 device cụ thể
xcrun simctl boot "iPhone 16 Pro"
# Mở URL trong Safari của simulator đang chạy
xcrun simctl openurl booted "http://localhost:5173"
# Chụp màn hình
xcrun simctl io booted screenshot ~/Desktop/shot.png
# Tắt tất cả
xcrun simctl shutdown all
Settings → About phone
→ Bấm "Build number" 7 lần liên tiếp
→ Nhập PIN nếu được hỏi
→ Hiện thông báo "You are now a developer!"
Với Xiaomi/MIUI: bấm vào MIUI version 7 lần. Với Samsung: Settings → About phone → Software information → Build number.
Settings → System → Developer options
→ Bật "USB debugging"
Xiaomi cần bật thêm USB debugging (Security settings).
brew install --cask android-platform-tools
Kiểm tra:
adb devices
Kết quả mong đợi:
List of devices attached
R5CT30XXXXX device
Nếu hiện unauthorized → nhìn màn hình điện thoại, chấp nhận popup “Allow USB debugging?” và tích Always allow from this computer.
chrome://inspect/#devicesDevTools mở ra kèm màn hình mirror của điện thoại (Screencast).
Cách 1 — Wireless debugging (Android 11+, không cần cáp):
Điện thoại: Developer options → Wireless debugging → Bật
→ "Pair device with pairing code"
Trên Mac:
adb pair 192.168.1.50:41234 # nhập IP:port hiện trên điện thoại
# Nhập pairing code 6 số
adb connect 192.168.1.50:5555 # port trong màn hình Wireless debugging
Cách 2 — Qua cáp trước rồi chuyển sang WiFi:
adb tcpip 5555
adb connect 192.168.1.50:5555
# Rút cáp, adb devices vẫn thấy thiết bị
Trong chrome://inspect/#devices → Port forwarding… → thêm:
| Device port | Local address |
|---|---|
| 5173 | localhost:5173 |
| 8080 | localhost:8080 |
Trên điện thoại vào http://localhost:5173 là ra dev server của Mac. Cách này hoạt động qua cáp USB, không phụ thuộc WiFi.
| Trình duyệt | Có inspect được? | Ghi chú |
|---|---|---|
| Chrome Android | ✅ | chrome://inspect |
| Edge Android | ✅ | edge://inspect trên Edge desktop |
| Samsung Internet | ✅ | Hiện trong chrome://inspect (cần bật Developer mode trong app) |
| Firefox Android | ✅ | about:debugging trên Firefox desktop |
| WebView trong app | ✅ | Cần app build debug + setWebContentsDebuggingEnabled(true) |
brew install --cask android-studio
Mở Android Studio → bỏ qua wizard tạo project → More Actions → Virtual Device Manager.
Tạo AVD:
UpsideDownCake / API 34 / arm64-v8ax86_64⚠️ Chọn sai kiến trúc (x86 trên máy M-series) sẽ khiến emulator cực chậm hoặc không boot được.
Nếu không cần IDE:
brew install --cask android-commandlinetools
Thêm biến môi trường vào ~/.zshrc:
export ANDROID_HOME="$HOME/Library/Android/sdk"
export PATH="$PATH:$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin"
source ~/.zshrc
Cài thành phần cần thiết:
# Xem danh sách system image có sẵn
sdkmanager --list | grep system-images
# Cài (Apple Silicon)
sdkmanager "emulator" "platform-tools" "platforms;android-34" \
"system-images;android-34;google_apis;arm64-v8a"
# Cài (Intel Mac)
sdkmanager "emulator" "platform-tools" "platforms;android-34" \
"system-images;android-34;google_apis;x86_64"
# Chấp nhận license
sdkmanager --licenses
Tạo và chạy AVD:
avdmanager create avd -n pixel7 \
-k "system-images;android-34;google_apis;arm64-v8a" \
-d "pixel_7"
emulator -avd pixel7
Liệt kê AVD đã tạo:
emulator -list-avds
avdmanager list avd
Emulator được adb nhận diện tự động:
adb devices
# emulator-5554 device
Mở chrome://inspect/#devices trên Chrome của Mac → tab của emulator hiện ra → inspect.
Không cần bật USB debugging thủ công (emulator đã bật sẵn).
Đây là điểm dễ nhầm nhất:
| Muốn truy cập | Địa chỉ dùng trong emulator |
|---|---|
127.0.0.1 / localhost của Mac | 10.0.2.2 |
localhost của chính emulator | 127.0.0.1 |
| Router / gateway ảo | 10.0.2.1 |
| DNS của emulator | 10.0.2.3 |
Ví dụ dev server chạy http://localhost:5173 trên Mac → trong emulator mở http://10.0.2.2:5173.
Hoặc dùng adb reverse (giống port forwarding, dùng được localhost như bình thường):
adb reverse tcp:5173 tcp:5173
# Giờ trong emulator vào http://localhost:5173 là chạy
Bấm nút ... cạnh cửa sổ emulator:
| Mục | Dùng để |
|---|---|
| Cellular | Giả lập 2G/EDGE/3G/LTE, latency cao → test loading state |
| Location | Set GPS giả, import file GPX/KML |
| Battery | Test hành vi khi pin yếu / charging |
| Display / Rotate | Test responsive, landscape |
| Settings → Screen | Bật “Send keyboard shortcuts to” |
# Cài APK
adb install app.apk
# Copy file vào emulator
adb push ./image.jpg /sdcard/Download/
# Mở URL trực tiếp trong Chrome của emulator
adb shell am start -a android.intent.action.VIEW -d "http://10.0.2.2:5173"
# Chụp màn hình
adb exec-out screencap -p > ~/Desktop/shot.png
# Quay video
adb shell screenrecord /sdcard/demo.mp4 # Ctrl+C để dừng
adb pull /sdcard/demo.mp4 ~/Desktop/
# Xem log hệ thống (lọc log của Chrome/WebView)
adb logcat | grep -i chromium
# Xoá dữ liệu Chrome trong emulator
adb shell pm clear com.android.chrome
Kéo thả file APK hoặc ảnh trực tiếp vào cửa sổ emulator cũng hoạt động.
Dùng khi: không cắm được cáp, test trên máy người khác, kiểm tra nhanh trên staging/production.
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script>
Xuất hiện nút tròn nổi trên màn hình, mở ra panel có: Console, Elements, Network, Resources, Sources, Info.
Chỉ bật khi có query string ?debug=1:
<script>
if (new URLSearchParams(location.search).has('debug')) {
const s = document.createElement('script');
s.src = 'https://cdn.jsdelivr.net/npm/eruda';
s.onload = () => eruda.init();
document.body.appendChild(s);
}
</script>
Cài qua npm:
npm i -D eruda
if (import.meta.env.DEV) {
import('eruda').then(({ default: eruda }) => eruda.init());
}
Plugin hữu ích:
<script src="https://cdn.jsdelivr.net/npm/eruda-dom"></script>
<script src="https://cdn.jsdelivr.net/npm/eruda-timing"></script>
<script>
eruda.init();
eruda.add(erudaDom);
eruda.add(erudaTiming);
</script>
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>new VConsole();</script>
Nhẹ hơn eruda, giao diện đơn giản hơn, mạnh về log network và system info.
| eruda | vConsole | |
|---|---|---|
| Kích thước | ~110KB | ~70KB |
| Elements inspector | ✅ Tốt | ⚠️ Cơ bản |
| Network | ✅ | ✅ |
| Plugin system | ✅ Phong phú | ✅ Có |
| Chỉnh sửa DOM live | ✅ | ❌ |
Lấy IP của Mac:
ipconfig getifaddr en0 # WiFi
ipconfig getifaddr en1 # Ethernet / adapter
Chạy dev server với --host để bind ra ngoài 127.0.0.1:
# Vite
npm run dev -- --host
# Next.js
next dev -H 0.0.0.0
# PHP built-in server
php -S 0.0.0.0:8080
# Python
python3 -m http.server 8080 --bind 0.0.0.0
Trên điện thoại vào http://192.168.1.20:5173.
⚠️ macOS Firewall có thể chặn. Vào System Settings → Network → Firewall → Options và cho phép ứng dụng (node/php) nhận kết nối đến.
Nhiều API web (Service Worker, getUserMedia, Geolocation, Clipboard) bắt buộc secure context. IP LAN dạng http://192.168.x.x không được coi là secure → phải dùng tunnel HTTPS.
Cloudflare Tunnel (miễn phí, không cần đăng ký):
brew install cloudflared
cloudflared tunnel --url http://localhost:8080
# → https://random-words-1234.trycloudflare.com
ngrok:
brew install ngrok
ngrok config add-authtoken <token>
ngrok http 8080
ngrok có sẵn web inspector tại http://localhost:4040 để xem lại toàn bộ request/response.
localtunnel:
npx localtunnel --port 8080
brew install mkcert
mkcert -install
mkcert localhost 192.168.1.20
Với Vite:
// vite.config.js
import fs from 'fs';
export default {
server: {
host: true,
https: {
key: fs.readFileSync('./192.168.1.20-key.pem'),
cert: fs.readFileSync('./192.168.1.20.pem'),
},
},
};
Cần cài root CA của mkcert vào điện thoại (gửi file rootCA.pem qua AirDrop/email rồi cài trong Settings → Profile).
| Môi trường | Địa chỉ trỏ về Mac |
|---|---|
| iOS Simulator | localhost / 127.0.0.1 (chung host) |
| iPhone thật | IP LAN (192.168.x.x) hoặc tunnel |
| Android Emulator | 10.0.2.2 hoặc adb reverse |
| Android thật (cáp) | localhost sau khi adb reverse / port forwarding |
| Android thật (WiFi) | IP LAN hoặc tunnel |
Thêm vào functions.php của theme (hoặc plugin riêng):
add_action( 'wp_enqueue_scripts', function () {
// Chỉ bật khi có ?debug=1 trên URL
if ( ! isset( $_GET['debug'] ) ) {
return;
}
// Chỉ cho user đã đăng nhập có quyền quản trị
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
wp_enqueue_script(
'eruda',
'https://cdn.jsdelivr.net/npm/eruda',
[],
null,
false
);
wp_add_inline_script( 'eruda', 'eruda.init();' );
}, 100 );
Truy cập https://site.com/?debug=1 khi đã đăng nhập admin để bật panel.
Trong wp-config.php:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true ); // ghi ra wp-content/debug.log
define( 'WP_DEBUG_DISPLAY', false ); // không in ra màn hình
define( 'SCRIPT_DEBUG', true ); // dùng file JS/CSS chưa minify
@ini_set( 'display_errors', 0 );
Theo dõi log realtime:
tail -f wp-content/debug.log
LocalWP: có sẵn tính năng Live Link — bấm nút “Enable” trong tab Overview, nhận URL public HTTPS ngay.
Với wp-env / Docker: dùng cloudflared tunnel như mục 7.2.
Lưu ý: WordPress lưu siteurl và home trong database, truy cập bằng IP/domain khác sẽ bị redirect về domain gốc. Khắc phục bằng cách thêm vào wp-config.php:
define( 'WP_HOME', 'http://' . $_SERVER['HTTP_HOST'] );
define( 'WP_SITEURL', 'http://' . $_SERVER['HTTP_HOST'] );
Chỉ dùng dòng trên ở môi trường local/dev, tuyệt đối không đưa lên production.
Dùng khi cần test trên thiết bị/OS mà mình không có, hoặc trình duyệt của hãng (Samsung Internet, MIUI Browser).
| Dịch vụ | Free tier | DevTools thật | Ghi chú |
|---|---|---|---|
| BrowserStack Live | Có (giới hạn phút) | ✅ | Nhiều device nhất, có tunnel cho localhost |
| LambdaTest | Có | ✅ | Rẻ hơn, UI gọn |
| Sauce Labs | Trial | ✅ | Mạnh về automation |
| Firebase Test Lab | Có quota free | ⚠️ | Chủ yếu cho app, không tiện cho web |
Cả BrowserStack và LambdaTest đều có tool tunnel (BrowserStackLocal, LT) để test được localhost của Mac trên device cloud.
| Vấn đề | Cách xử lý |
|---|---|
| Develop menu không hiện | Safari → Settings → Advanced → tích “Show features for web developers” |
| iPhone không hiện trong Develop | Kiểm tra Web Inspector đã bật; rút cắm lại cáp; đổi cáp (cáp sạc rẻ có thể không truyền data) |
| Đã Trust nhưng vẫn không thấy | Reset trust: iPhone → Settings → General → Transfer or Reset → Reset Location & Privacy |
| Simulator không hiện | Chạy xcrun simctl list devices xem có device nào ở trạng thái Booted không |
| Web Inspector trắng / không load | Đóng cửa sổ inspector, restart Safari trên cả 2 máy |
| Vấn đề | Cách xử lý |
|---|---|
adb devices báo unauthorized | Nhìn màn hình điện thoại, chấp nhận popup RSA. Nếu không hiện: adb kill-server && adb start-server |
adb devices trống | Đổi cáp; chọn chế độ USB = File Transfer (MTP) thay vì Charging only |
chrome://inspect trống dù adb devices có | Kiểm tra Chrome trên điện thoại đã mở tab; thử adb kill-server rồi restart Chrome desktop |
| Emulator chạy cực chậm | Sai kiến trúc image (x86 trên Apple Silicon). Tạo lại AVD với arm64-v8a |
| Emulator không boot | Tăng RAM lên 2048MB+; đổi Graphics sang “Software”; xoá ~/.android/avd/<name>.avd/*.lock |
10.0.2.2 không truy cập được | Dev server phải bind 0.0.0.0, không phải 127.0.0.1. Hoặc dùng adb reverse |
sdkmanager báo lỗi Java | brew install --cask temurin rồi set JAVA_HOME |
| Vấn đề | Cách xử lý |
|---|---|
| Truy cập IP LAN bị timeout | macOS Firewall chặn → System Settings → Network → Firewall → Options → allow node/php |
| Service Worker không đăng ký | Cần HTTPS. Dùng cloudflared hoặc mkcert |
| CSS/JS bị cache cũ trên mobile | Trong DevTools bật “Disable cache”; hoặc thêm query ?v=<timestamp> |
| Bug chỉ xảy ra trên device thật | Đúng — dùng device farm hoặc mượn máy để xác nhận |
# Android
brew install --cask android-platform-tools
brew install --cask android-studio # hoặc android-commandlinetools
# iOS
brew install --cask xcode
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
# Tunnel & HTTPS
brew install cloudflared mkcert
mkcert -install
open -a Simulator # mở simulator
xcrun simctl list devices # xem device
xcrun simctl boot "iPhone 16 Pro" # boot device
xcrun simctl openurl booted "http://localhost:5173" # mở URL
xcrun simctl io booted screenshot ~/Desktop/s.png # chụp màn hình
xcrun simctl shutdown all # tắt hết
emulator -list-avds # xem AVD
emulator -avd pixel7 & # chạy emulator nền
adb devices # kiểm tra kết nối
adb reverse tcp:5173 tcp:5173 # map localhost
adb shell am start -a android.intent.action.VIEW -d "http://localhost:5173"
adb logcat | grep -i chromium # xem log
adb kill-server && adb start-server # reset khi lỗi
ipconfig getifaddr en0 # lấy IP Mac
npm run dev -- --host # Vite bind 0.0.0.0
cloudflared tunnel --url http://localhost:5173
chrome://inspect/#devices → Chrome desktop, debug Android
edge://inspect → Edge desktop
about:debugging → Firefox desktop
http://localhost:4040 → ngrok inspector
Khi mới bắt đầu dự án:
--host.?debug=1.Khi phát hiện bug trên mobile:
Tài liệu cập nhật: 07/2026 — macOS Sequoia, Xcode 16, Android Studio Ladybug
Print