← Blog Home

Gửi rồi mà không nhận được: Cách debug “Sent but Not Received” theo góc nhìn Product Team

vn 2026-02-08 13:16:44

Gửi rồi mà không nhận được: Cách debug “Sent but Not Received” theo góc nhìn Product Team

“Email đã gửi thành công” nhưng người dùng khăng khăng không thấy trong hộp thư đến. Với Product Team, đây không chỉ là lỗi kỹ thuật: nó kéo theo rủi ro mất chuyển đổi (OTP không đến), tăng ticket hỗ trợ, ảnh hưởng uy tín thương hiệu, và tệ hơn là khiến người dùng bỏ luôn. Vấn đề khó ở chỗ: “Sent” có thể chỉ là “đã rời khỏi ứng dụng”, không đồng nghĩa “đã vào Inbox”.

Bài viết này là một playbook debug theo góc nhìn Product Team: cách phân loại sự cố, đặt giả thuyết đúng, dựng timeline theo sự kiện, và phối hợp với Dev/Infra/ESP để tìm nguyên nhân gốc. Mục tiêu là biến một ticket mơ hồ thành một quy trình điều tra có thể lặp lại.

1) Định nghĩa rõ “Sent” đang nghĩa là gì trong hệ thống của bạn

Điều đầu tiên Product cần làm là thống nhất ngôn ngữ giữa Product – Engineering – Support. Nhiều hệ thống hiển thị “Sent” khi: (1) request tạo email được ghi nhận, hoặc (2) job vào hàng đợi, hoặc (3) ESP trả về 202 Accepted. Nhưng trong deliverability, “đã gửi” còn nhiều trạng thái khác: bounced, deferred, blocked, spam, dropped…

  • App Sent: người dùng bấm gửi / hệ thống tạo request.
  • Queued: job được đưa vào queue (SQS/Rabbit/Redis queue...).
  • ESP Accepted: nhà cung cấp gửi mail (ESP) chấp nhận request và trả message-id.
  • Delivered: ESP báo đã giao cho server nhận (provider) thành công.
  • Inboxed/Placed: thư xuất hiện ở Inbox (thực tế có thể rơi vào Spam/Promotions/Updates).

Nếu đội của bạn đang gọi “ESP Accepted” là “Sent”, hãy hiểu rằng lúc này bạn mới chỉ đi được nửa đường. Bất kỳ bước nào phía sau đều có thể là điểm rơi lỗi.

2) Phân loại sự cố: đây là lỗi cá nhân hay lỗi hệ thống?

Product Team cần quyết định nhanh: đây là một trường hợp đơn lẻ (tài khoản/địa chỉ cụ thể) hay là dấu hiệu của sự cố rộng (toàn hệ thống). Cách phân loại tốt nhất là dựa trên “mẫu hình”:

  • Chỉ 1 người dùng: thường liên quan hộp thư người dùng (spam, chặn domain, inbox full), typo email, alias, hoặc provider đặc thù.
  • Chỉ 1 provider (Gmail/Outlook/Yahoo...): thường liên quan reputation, DMARC, nội dung mail, hoặc throttling.
  • Chỉ 1 loại email: OTP/Reset/Welcome có thể khác template, khác domain, khác pipeline.
  • Đột ngột tăng hàng loạt: thường liên quan DNS thay đổi, ESP incident, code deploy, queue backlog, rate limit.

Cách bạn phân loại sẽ quyết định hướng debug: nếu là “provider-specific”, bạn phải nhìn vào deliverability và DNS; nếu là “system-wide”, hãy nhìn vào queue, retry logic, và sự kiện deploy.

3) Thu thập dữ liệu đúng: biến một complaint thành một timeline

Support thường nhận câu: “Tôi không nhận được email”. Thông tin này gần như không đủ. Product nên chuẩn hóa một bộ câu hỏi tối thiểu để dựng timeline và tránh ping-pong nội bộ.

Thông tin tối thiểu cần có

  • Email người nhận (đúng chính tả, có dấu cách/ký tự lạ không).
  • Loại email: OTP, reset password, verify email, invoice…
  • Thời điểm người dùng thao tác (kèm múi giờ hoặc timestamp).
  • Thiết bị / mạng (đôi khi user bấm nhiều lần, tạo nhiều request).
  • Ảnh chụp màn hình (nếu có): trạng thái “sent”, mã lỗi, hoặc thông báo.

Thông tin lý tưởng (nếu hệ thống đã instrument tốt)

  • request_id (mỗi lần gửi có một id).
  • message_id từ ESP (SendGrid Message ID, SES message id...).
  • event stream gồm queued/processed/delivered/bounced/deferred/spamreport.

Nếu hiện tại bạn chưa có request_id/message_id hiển thị trong admin/support panel, đây là một “điểm nợ kỹ thuật” đáng trả. Vì không có ID, debug sẽ giống như mò kim trong đống rơm: ai cũng đoán, không ai chắc.

4) Checklist theo pipeline: debug từ trong ra ngoài

4.1) Ứng dụng có thật sự tạo request gửi mail không?

Bước đầu tiên luôn là xác nhận request có tồn tại. Nhiều lỗi “không nhận” thực ra là “không hề gửi”: người dùng bấm nhưng API timeout, validation fail, hoặc bị rate limit phía server.

  • Kiểm tra log API: request có vào server không? status code là gì?
  • Kiểm tra rate limit: user bấm nhiều lần có bị chặn không?
  • Kiểm tra logic điều kiện: chỉ gửi khi user chưa verify? có nhầm trạng thái không?
  • Kiểm tra typo email / normalization: trim khoảng trắng, lower-case domain, xử lý ký tự unicode.

4.2) Queue / Worker có xử lý job đúng không?

Với hệ thống dùng queue, “Sent” trên UI đôi khi chỉ là “đã enqueue”. Nếu worker chết, backlog phình ra, hoặc retry sai, email sẽ không bao giờ rời khỏi hệ thống.

  • Queue depth tăng bất thường? worker lag? CPU/RAM?
  • Job có bị “poison message” làm fail liên tục?
  • Retry policy: exponential backoff có hợp lý? có dead-letter queue không?
  • Template render có lỗi (missing variable, invalid HTML) khiến job fail?

4.3) ESP có chấp nhận và trả message-id không?

Khi email đi ra ngoài qua ESP, điểm then chốt là: ESP accepted hay bị rejected. Nếu rejected, thường có lý do rõ (API key, quota, payload invalid, policy).

  • Log response từ ESP: status code, error message.
  • API key/secret rotate gần đây? quyền bị hạn chế?
  • Quota/limit: gửi nhiều quá bị throttling hoặc bị pause account?
  • From domain/From address có hợp lệ và được verify không?

4.4) Deliverability: delivered rồi nhưng người dùng vẫn “không thấy”

Đây là vùng gây tranh cãi nhất: hệ thống nói “delivered”, người dùng nói “không có”. Trường hợp này phần lớn rơi vào 3 nhóm: spam placement, provider filtering, hoặc user-side rules.

  • Spam/Promotions/Updates: người dùng chỉ nhìn Inbox chính, không nhìn thư mục khác.
  • Provider block / filtering: Gmail/Outlook có thể chặn hoặc trì hoãn nếu thấy rủi ro.
  • User rules: Outlook rules, Gmail filters, forwarding, hoặc “blocked sender”.

5) Phần kỹ thuật Product nên hiểu: DNS & xác thực (SPF, DKIM, DMARC)

Dù bạn không phải engineer, Product vẫn nên biết 3 từ khóa này vì chúng quyết định mail có được tin không. Một thay đổi DNS nhỏ cũng có thể khiến tỷ lệ vào Inbox tụt mạnh.

SPF: ai được phép gửi thay mặt domain?

SPF là bản ghi cho phép các server được phép gửi mail thay domain của bạn. Nếu thiếu hoặc sai, provider có thể nghi ngờ và tăng điểm spam. Khi dùng ESP, bạn phải đảm bảo SPF record bao gồm hệ thống gửi của ESP.

DKIM: chữ ký để chứng minh mail không bị sửa

DKIM ký vào email bằng khóa riêng, provider dùng khóa công khai trong DNS để xác minh. DKIM tốt giúp tăng trust. DKIM fail hàng loạt thường khiến email rớt spam hoặc bị từ chối.

DMARC: chính sách xử lý khi SPF/DKIM fail

DMARC cho provider biết bạn muốn làm gì khi SPF/DKIM không pass: none/quarantine/reject. Nhiều team gặp sự cố “đột nhiên không nhận được” sau khi chuyển DMARC sang quarantine hoặc reject mà chưa chuẩn hóa toàn bộ luồng gửi.

Từ góc Product, bạn không cần tự cấu hình DNS. Nhưng bạn cần biết: mỗi lần đổi domain gửi, đổi ESP, đổi subdomain, đổi template là phải xem lại xác thực và theo dõi metric deliverability.

6) Những nguyên nhân phổ biến nhất (theo tần suất thực tế)

6.1) Email vào Spam/Promotions

Đây là nguyên nhân “đau mà phổ biến”. Người dùng nói “không nhận”, nhưng thực ra email nằm ở Spam hoặc Promotions. Điều này thường đến từ domain reputation, nội dung mail, hoặc thiếu xác thực.

  • Nội dung quá “salesy”: nhiều từ khóa khuyến mãi, nhiều dấu chấm than, nhiều link tracking.
  • From name / subject thay đổi thất thường, giống hành vi spam.
  • HTML nặng, hình ảnh nhiều, text ít, hoặc lỗi HTML.
  • Domain mới, chưa warm-up, gửi tăng đột biến.

6.2) Bounced/Blocked/Deferred

Bounce là bị trả lại. Blocked có thể là do policy hoặc reputation. Deferred thường là “tạm hoãn” (throttling), email có thể đến trễ vài phút đến vài giờ.

  • Mailbox full, địa chỉ không tồn tại, domain người nhận lỗi.
  • Provider throttling vì gửi nhiều hoặc bị nghi ngờ.
  • IP/domain bị liệt vào danh sách chặn hoặc bị ESP hạn chế.

6.3) Lỗi nội bộ: queue backlog, worker crash, retry sai

Nếu bạn deploy một phiên bản mới và thấy “Sent nhưng không nhận” tăng, hãy nghi ngờ pipeline: job không chạy, template render fail, hoặc retry tạo bão request.

6.4) Nhầm domain gửi / From không nhất quán

Một số team dùng nhiều domain/subdomain: transactional, marketing, notifications. Nếu luồng OTP lỡ đi qua domain “marketing”, tỷ lệ vào spam sẽ khác hẳn. Product cần nắm sơ đồ domain gửi để không vô tình trộn luồng.

7) Playbook xử lý theo tình huống (Product có thể áp dụng ngay)

Tình huống A: Chỉ 1 người dùng không nhận OTP

  1. Xác nhận email chính xác (copy/paste, không có khoảng trắng).
  2. Kiểm tra event: request_id → message_id → delivered/bounce/defer.
  3. Nếu delivered: hướng dẫn user kiểm tra Spam/All Mail/Promotions và search theo subject/from.
  4. Nếu deferred: thông báo “có thể đến trễ” + cho phép resend với cooldown hợp lý.
  5. Nếu bounced: hiển thị thông báo rõ ràng (địa chỉ sai/không tồn tại) thay vì “Sent”.

Tình huống B: Cả Gmail không nhận

  1. Xem dashboard ESP: bounce/blocks tăng? complaint tăng?
  2. Kiểm tra SPF/DKIM/DMARC có thay đổi gần đây không.
  3. So sánh template/email type: OTP vs verify có khác nội dung/link không.
  4. Giảm tốc độ gửi, tách luồng transactional khỏi marketing nếu đang dùng chung.
  5. Thử gửi từ subdomain transactional (ví dụ notify.) và warm-up dần.

Tình huống C: “Sent” tăng nhưng ticket cũng tăng sau một lần deploy

  1. So sánh before/after deploy: queue depth, worker errors, template errors.
  2. Tìm lỗi render template hoặc biến bị thiếu (hay xảy ra khi đổi localization).
  3. Kiểm tra retry logic: có tạo vòng lặp gửi lại vô hạn không.
  4. Nếu cần, rollback nhanh cho luồng OTP (ưu tiên funnel critical).

8) Tối ưu sản phẩm để giảm “Sent but Not Received” ngay từ UX

Nhiều team chỉ chữa ở backend, nhưng UX mới là nơi giảm ticket mạnh nhất. Product có thể làm vài thứ nhỏ mà hiệu quả lớn:

  • Thông báo trạng thái rõ hơn: thay “Đã gửi” bằng “Đang gửi / Đã chuyển tới nhà cung cấp / Có thể đến trong 1–2 phút”.
  • Resend có kiểm soát: cooldown 30–60 giây, hiển thị countdown, tránh user bấm liên tục tạo nhiều email và tự rối.
  • Gợi ý kiểm tra Spam/All Mail: một dòng hướng dẫn ngay dưới ô OTP, kèm mẹo search theo từ khóa.
  • Đổi kênh dự phòng: nếu OTP email không đến, cho phép OTP qua SMS hoặc app code (tùy sản phẩm).
  • Admin panel cho Support: tra request_id/message_id, xem trạng thái delivered/bounced ngay, giảm vòng hỏi đáp.

9) Logging & Metric: thứ giúp bạn “debug trong 5 phút” thay vì 2 ngày

Một Product Team mạnh là team có quan sát được (observability). Với email, tối thiểu bạn nên có:

  • Email send attempts theo loại (OTP/verify/reset) và theo provider người nhận (gmail/outlook/yahoo).
  • Time-to-deliver (từ request đến delivered) để phát hiện deferred/throttling.
  • Bounce rate, block rate, spam complaint, open rate (tùy loại email).
  • Queue lag và worker error rate.
  • Top error reasons từ ESP (invalid recipient, blocked, policy…).

Và quan trọng: mọi sự kiện gửi nên gắn với một correlation id (request_id) để khi có ticket, bạn truy ngược được toàn bộ hành trình.

10) Kết: góc nhìn Product Team để xử lý tận gốc

“Sent but Not Received” hiếm khi là một lỗi đơn giản. Nó là giao điểm của UX, hệ thống hàng đợi, ESP, DNS, và chính sách của nhà cung cấp hộp thư. Product Team không cần tự cấu hình SPF hay đọc log server cả ngày, nhưng cần: đặt câu hỏi đúng, chuẩn hóa dữ liệu ticket, và điều phối quy trình debug theo pipeline.

Khi bạn có request_id/message_id, có event timeline, có metric theo provider và theo loại email, mọi cuộc điều tra sẽ chuyển từ “cãi nhau” sang “đọc dữ liệu”. Và đó là lúc đội bạn biến một vấn đề gây bực bội thành một lợi thế vận hành: gửi mail ổn, người dùng tin, funnel trơn tru.

Tip: Temporary inboxes are best for low-risk sign-ups and verification. Avoid sensitive accounts that require long-term recovery access.