Quay lại Blog
Công nghệ

Ảnh dán sang trang khác bị vỡ? Cây chẩn lỗi 403 và chống hotlink 2026

Đăng ngày14 min read
Chia sẻ

Bạn dán link một tấm ảnh lên blog, xem trên máy ngon lành. Đăng lên, người khác thấy lại là một cái khung icon vỡ. Hoặc tệ hơn — cả chỗ đó thành một mảng trống, chẳng có gì.

Cái hố này mình dính không chỉ một lần. Có lần toang online là lúc giúp forum của bạn dời ảnh, local xanh hết, lên rồi một phần ba thumbnail trang chủ vỡ sạch, lúc đó mình còn tưởng chưa xóa cache. Debug ba ngày mới phát hiện, căn bản không phải lỗi của mình — là trang nguồn ảnh bật chống hotlink (hotlink protection), chỉ cần request không phát ra từ chính domain của nó, chặn tất.

Nhìn từ góc kiến trúc, "ảnh không hiện được ở trang khác" thật ra là ba kiểu thất bại hoàn toàn khác nhau bị gộp lại nói chung. Muốn sửa đúng, bước đầu là phân biệt bạn đang gặp kiểu nào.

Phân biệt trước: 403, vỡ ảnh, 200 trống là ba chuyện

Nhiều người cứ thấy ảnh mất là gọi chung "vỡ ảnh", rồi thử loạn xạ. Nói toạc ra, ba triệu chứng này gốc rễ chênh nhau xa, đường chẩn lỗi cũng khác nhau.

Triệu chứng HTTP status Gốc rễ khả năng nhất Hướng chẩn lỗi
Khung icon vỡ (icon rạn nứt) 403 / 404 Chống hotlink chặn, hoặc link sai Kiểm tra Referer và độ đúng của url
Khung icon vỡ (icon rạn nứt) 200 nhưng không phải ảnh Nhận về là trang HTML lỗi không phải file ảnh Kiểm tra Content-Type
Một mảng trống (không icon vỡ) 200 CSS che, hoặc CSP chặn load Kiểm tra DevTools Console và CSP
Icon xoay xong biến mất Timeout / CORS Chính sách cross-domain hoặc server timeout Kiểm tra chi tiết request trong Network

Xem status trước, xem Content-Type sau, cuối cùng mới xem màn hình. Đảo ngược thứ tự này là quay vòng như gà mắc tóc.

Nguyên lý tầng dưới: Referer và chống hotlink vận hành thế nào

Muốn hiểu vì sao ảnh ở trang khác bị chặn, phải biết trình duyệt lén làm gì lúc load ảnh.

Khi trang A dùng <img src="https://B.com/photo.jpg"> đi load ảnh của trang B, request trình duyệt phát ra sẽ mang kèm một header Referer (đúng, chữ này lịch sử viết sai luôn, thiếu một chữ r, sai tới tận hôm nay). Header này báo trang B: "Ảnh này được trang A yêu cầu."

Nguyên lý tầng dưới là thế này: server hoặc CDN của trang B nhận request xong, lấy domain của Referer đi so với một danh sách trắng. Logic so đại khái là —

1. Referer rỗng (mở ảnh thẳng, bookmark) → tùy cấu hình cho qua hay chặn
2. Domain của Referer trong danh sách trắng → cho qua
3. Domain của Referer không trong danh sách trắng → trả 403 Forbidden

Đây chính là chống hotlink. Mục đích rất đơn giản: không muốn để người khác xài chùa băng thông của mình. Bạn nhúng ảnh trang B trên trang mình, mỗi lần người khác xem trang bạn, hóa đơn traffic tính lên đầu trang B, trang B tất nhiên khó chịu.

Ở đây có một chi tiết nhiều người không biết: Referrer-Policy mặc định của trình duyệt hiện đại là strict-origin-when-cross-origin. Khi request cross-domain, nó chỉ gửi "origin domain" (ví dụ https://a.com/), không gửi full path. Nên chống hotlink lấy được tên domain của bạn, đủ để so danh sách trắng, cũng đủ để chặn bạn. Đó là lý do chống hotlink trị được hotlink.

Vì sao ảnh của bạn ở trang khác thành vỡ ảnh: một cây chẩn lỗi

Trải nguyên lý trên ra, gốc rễ vỡ ảnh thật ra thu về được thành một cây. Bạn cứ theo nó đi xuống.

Nhánh 1: Status có phải 403 không?

Nếu phải, chín phần là chống hotlink. Trang B thấy Referer của bạn không trong danh sách trắng, từ chối thẳng. Trường hợp này bạn không sửa được cấu hình trang B, giải pháp duy nhất là đừng hotlink ảnh của nó nữa (lát nói lời giải đúng).

Nhánh 2: Status là 200, nhưng màn hình vỡ ảnh?

Mở nội dung gốc của "tấm ảnh" này xem. Rất có thể cái bạn nhận là một trang HTML — vài trang không trả 403, mà trả một trang "cấm hotlink", status vẫn là 200. Trình duyệt coi HTML là ảnh để phân tích, tất nhiên rạn cho bạn xem. Kiểm tra Content-Type là lộ tẩy: ảnh bình thường là image/jpeg, image/png, trang lỗi sẽ là text/html.

Nhánh 3: Bạn dán "link trang" chứ không phải "direct link ảnh"?

Đây là bẫy người mới hay gặp nhất. https://imgur.com/aBcDeFg là một trang, https://i.imgur.com/aBcDeFg.jpg mới là bản thân file ảnh. Cái trước bạn dùng <img src> đi load, nhận về là cả trang HTML, không phải ảnh. Khác một cái i. và đuôi file, kết quả một trời một vực.

Nhánh 4: Referrer-Policy tự hại chính bạn?

Kiểu ẩn hơn. Nếu trang của bạn đặt Referrer-Policy: no-referrer, trình duyệt lúc load ảnh hoàn toàn không gửi Referer. Vài cấu hình chống hotlink là "Referer rỗng chặn hết", thế là bạn ngược lại bị chính cấu hình riêng tư nhà mình chặn ngoài cửa. Thực đo cho thấy, kiểu này thường xuất hiện ở trang rất coi trọng riêng tư, tắt cứng meta referrer.

Bắt tay chẩn đoán: DevTools và curl hai chiêu định vị

Lý thuyết xong, thực tế chẩn sao? Mình cố định dùng hai công cụ, năm phút định vị được chín phần vỡ ảnh.

Chiêu một: bảng Network của DevTools

Mở developer tools của trình duyệt (F12), sang Network, refresh trang, gõ tên file ảnh vào bộ lọc. Bấm vào request đó, xem ba thứ:

  • Status: là 403, 404 hay 200?
  • Content-Type trong Response Headers: là image/* hay text/html?
  • Referer trong Request Headers: cái thật sự gửi đi là gì? Rỗng hay domain của bạn?

Ba cái xem xong, gốc rễ đại khái nổi lên rồi.

Chiêu hai: curl điều khiển Referer bằng tay

DevTools thấy được hiện tượng, nhưng muốn xác nhận "rốt cuộc có phải chống hotlink không", curl gọn nhất. Bạn nhét được Referer tùy ý, xem server phản ứng đổi thế nào:

# Hoàn toàn không mang Referer (mô phỏng mở ảnh thẳng)
curl -I "https://example.com/photo.jpg"

# Mang một Referer "giả vờ từ chính trang nguồn"
curl -I -H "Referer: https://example.com/" "https://example.com/photo.jpg"

# Mang một Referer "từ trang ngoài của bạn"
curl -I -H "Referer: https://your-blog.com/" "https://example.com/photo.jpg"

Cách đọc rất trực quan. Nếu cái thứ nhất và thứ ba trả 403, nhưng cái thứ hai trả 200 — chúc mừng, đóng đinh chống hotlink. Server chỉ nhận Referer của chính trang nguồn, còn lại chặn hết. Nếu cả ba đều 200, thì vỡ ảnh chẳng liên quan chống hotlink, quay lại kiểm tra url hoặc CSP.

Thói quen thực đo của mình là chạy ba dòng cùng lúc, đối chiếu kết quả rõ hơn nhiều so với chỉ xem một dòng. Đó là lý do curl hiệu quả hơn bấm mò trong trình duyệt.

Hành vi chống hotlink của các image host / CDN thường gặp

Tính khí mặc định của các dịch vụ khác nhau nhiều. Dưới đây là quan sát mấy năm dời ảnh của mình, cấu hình thực tế mỗi nhà có thể chỉnh bất cứ lúc nào, nhưng hướng lớn là vậy.

Loại dịch vụ Mặc định chống hotlink Hành vi Referer rỗng Hợp nhúng ngoài?
Tự dựng Nginx / Apache Tùy bạn set Thường cho qua Tùy cấu hình
CDN doanh nghiệp (set được danh sách trắng) Bật tắt được Set cho qua hoặc chặn Set đúng thì được
Vài image host miễn phí Hay bật Hay chặn Hay bị chặn
Domain ảnh của mạng xã hội Gần như đều bật Gần như đều chặn Không được, đừng mơ
Image host chuyên chia sẻ Thường tắt Cho qua Được, đây mới là ý nghĩa tồn tại của nó

Trọng điểm là hàng cuối. Ảnh của mạng xã hội (các domain CDN của mấy trang social), url bạn copy ra gần như trăm phần trăm không nhúng ngoài được, vì mô hình kinh doanh của chúng là khóa bạn trong site. Muốn lấy mấy ảnh đó đi dán chỗ khác, đường cụt.

Còn image host chuyên "chia sẻ", chống hotlink mặc định tắt — ý nghĩa tồn tại của nó chính là để ảnh của bạn được trang ngoài load ổn định. Hai loại này đừng có lẫn lộn.

Bẫy Referrer-Policy: cả trình duyệt cũng có thể quay ra hại bạn

Trước có nhắc một tình huống ẩn, ở đây trải ra nói, vì nó dễ khiến người ta debug tới nghi ngờ nhân sinh nhất.

Referrer-Policy là header mà trang (bên phát request) đặt được. Nó quyết định trình duyệt gửi bao nhiêu thông tin Referer. Giá trị có mấy loại:

  • no-referrer: hoàn toàn không gửi.
  • origin: chỉ gửi origin domain.
  • strict-origin-when-cross-origin: cross-domain chỉ gửi domain, tụt xuống HTTP thì không gửi (mặc định hiện đại).
  • unsafe-url: gửi cả full path (không khuyến nghị, lộ riêng tư).

Điểm mâu thuẫn ở đây. Chống hotlink phải dựa Referer để phán nguồn, nhưng chính sách riêng tư lại khuyến khích gửi ít thậm chí không gửi Referer. Khi một trang vì riêng tư mà đặt no-referrer, nó đi load bất kỳ ảnh nào vừa có chống hotlink vừa "Referer rỗng chặn hết", sẽ vỡ sạch.

Mình toang online một lần đúng kiểu này — một trang rất coi trọng riêng tư, cả site gắn no-referrer, kết quả mọi ảnh nhúng ngoài vỡ hết. Lúc đó mình còn tưởng image host sập, tra nửa ngày mới phát hiện là cấu hình meta nhà mình bóp chết Referer. Cái bẫy này khó nghĩ ra, vì "tăng cường riêng tư" nghe sao lại hại chính mình được?

Cách làm đúng: đừng hotlink ảnh của người khác

Chẩn lỗi tới cuối, bạn sẽ phát hiện một sự thật tàn nhẫn: chỉ cần ảnh không phải bạn tự host, bất kỳ "ảnh của trang khác" nào cũng có thể biến mất vào thời điểm bạn không kiểm soát được. Bên kia đổi chống hotlink, đổi url, xóa file, thậm chí đóng cả trang, trang bạn vỡ theo.

Cách thật sự ổn chỉ có hai.

Một, re-host ảnh về chỗ bạn kiểm soát được. Đừng nhúng thẳng url người khác. Tải ảnh về, upload lên không gian của bạn hoặc một image host cho phép nhúng ngoài, đổi sang cái url ổn định đó. Nhìn từ góc kiến trúc, bản chất bước này là lấy tính khả dụng của trang bạn từ tay server người khác về lại tay mình — chỉ cần nguồn không phải cái bạn kiểm soát, thì mỗi lần bên kia chỉnh chống hotlink, đổi cấu trúc url, thậm chí gỡ cả trang, đều phản ánh thẳng thành vỡ ảnh trên trang bạn, mà bạn còn chẳng nhận được cảnh báo, thường là độc giả báo mới biết toang. Nếu bạn đã có một mớ link cũ rải rác trên domain người khác cần dời sạch một lần, mình có viết riêng một bài SOP thay hàng loạt link ảnh đầy đủ, dùng script quét url cũ cả site thay một lần, đỡ hơn nhiều so với thay từng tấm. Nguồn kiểm soát được mới là nguồn ổn.

Hai, chọn một image host mặc định tắt chống hotlink. Nhiều image host miễn phí vì tiết kiệm băng thông sẽ chặn nhúng ngoài, dán lên forum hay blog là vỡ cho bạn xem. Muốn nhúng ngoài, thì chọn một dịch vụ nêu rõ cho phép direct link.

Đây cũng là một nguyên tắc khi bọn mình làm lpic.cc: sau upload cho bạn một url direct link ảnh ổn định, không mặc định chặn trang ngoài, để bạn dán lên forum, blog, Discord đều hiện bình thường, không tự dưng thành 403. Ảnh cần riêng tư, cũng dùng mật khẩu bảo vệ kiểm soát ai xem được. Muốn thử thì upload miễn phí một tấm, dán link sang trang khác test là biết ổn không.

Nói thật cũng phải nói rõ giới hạn: hiện gói miễn phí ảnh có hạn lưu 30 ngày, không phải kho vĩnh viễn, và dịch vụ sống bằng doanh thu quảng cáo. Lưu trữ dài hạn hoặc kho ảnh thương mại số lượng lớn, bạn nên cân nhắc gói chuyên nghiệp trả phí, hoặc tự dựng lưu trữ có backup. Chọn công cụ đúng bối cảnh mới có nghĩa — điều này mình dính bẫy rồi mới học được.

Câu hỏi thường gặp FAQ

Q: Vì sao cùng một link ảnh, mình tự mở được, dán sang trang khác thì vỡ?

Vì khi bạn "mở ảnh thẳng", Referer là rỗng, hoặc từ nguồn được cho phép, chống hotlink cho qua. Dán sang trang khác, Referer biến thành domain ngoài đó, không trong danh sách trắng, bị chặn thành 403. Cùng một url, Referer khác nhau, kết quả hoàn toàn khác.

Q: 403 và vỡ ảnh là cùng một chuyện à?

Không. 403 là server từ chối rõ ràng (status 403), vỡ ảnh chỉ là biểu hiện màn hình "trình duyệt không vẽ được ảnh". 403 gây vỡ ảnh, nhưng vỡ ảnh cũng có thể do url sai, Content-Type sai, CSP chặn load và lý do khác. Xem status trước mới biết là kiểu nào.

Q: Đặt trang thành Referrer-Policy: no-referrer có né được chống hotlink không?

Đôi khi được, đôi khi ngược lại thảm hơn. Nếu chống hotlink bên kia là "Referer rỗng cho qua", không gửi Referer đúng là qua được. Nhưng nếu bên kia là "Referer rỗng chặn hết", bạn ngược lại vỡ sạch. Chiêu này không đáng tin, đừng coi là lời giải chuẩn.

Q: Mình dùng chương trình (Python / Node) kéo ảnh cũng bị 403, làm sao?

Cùng lý với trình duyệt — chương trình của bạn không mang đúng Referer. Dùng curl test trước xem bên kia nhận Referer nào, rồi bổ sung cùng header đó vào HTTP request của bạn. Nhưng lưu ý: né chống hotlink người khác để kéo ảnh có thể vi phạm điều khoản dịch vụ của họ, tự cân nhắc.

Q: Làm sao nhanh chóng phán một image host có nhúng ngoài được không?

Upload một ảnh test, lấy direct link của nó, chạy curl -I -H "Referer: https://google.com/". Trả 200 là cho phép nhúng ngoài, trả 403 là có chống hotlink, đừng mang đi dán trang ngoài. Ba mươi giây nghiệm xong.

References

#chống hotlink#hotlink protection#403 Forbidden#Referer#Referrer-Policy#CDN#direct link ảnh
H

Tác giả

Harvey Zhang

Đồng sáng lập & CTO

Kỹ sư backend và kiến trúc hệ thống