「Hotlink Protection」(直接連結保護)是經營網站經常需要去注意的一塊,但為什麼我們會需要「Hotlink Protection」呢?身為圖文並茂的網路文章作家,最擔心得就是自己的文章被別人整篇連文帶圖地複製貼上到其它地方了。此時如果圖片有套用「Hotlink Protection」的話,就可以讓被盜用的圖片在其它網站上「不被正常顯示」出來,如此一來,就能使其它誤入盜文頁面的訪客可以知道該篇文章是篇被盜用的文章。不同的網站架構有不同的「Hotlink Protection」的設定方式,如果您的網站有開啟Cloudflare的CDN服務的話,可以參考這篇文章,來實現「Hotlink Protection」的功能。



我的網站伺服器是用Nginx或Apache,難道不能直接對它們做設定嗎?

很遺憾,只要我們有啟用Cloudflare的CDN服務,就無法有效地透過自己的伺服器程式來設定Hotlink Protection。這是因為網頁瀏覽器在開啟您網站的圖片時,會去連結Cloudflare的CDN伺服器,而非我們自己的伺服器,所以就算我們有在自己的伺服器上製作Hotlink Protection的功能,也根本不會被觸發到。

既然有Cloudflare CDN,為什麼還要Hotlink Protection?被盜連的圖,其流量不是不會算在我們頭上嗎?

的確,有了Cloudflare CDN之後,就算圖片被盜連,它主要也還是會去吃Cloudflare CDN伺服器的流量。但是,您真的願意讓您文章中的圖片原封不動地在盜文網站上顯示出來嗎?所以還是設定一下Hotlink Protection吧!

Cloudflare內建的Hotlink Protection開關

Cloudflare的控制後台中,其實就有提供一個一鍵套用Hotlink Protection的開關,只要打開來,就可以讓有啟用CDN功能的網域擁有Hotlink Protection的功能。這個開關藏在Cloudflare控制後台的「Scrape Shield」分頁中。

cloudflare-hotlink-protection

這個Hotlink Protection一旦開啟,整個有開啟Cloudflare CDN服務的網域(包含子網域)下的圖片連結,就只能在這整個網域(包含子網域)下打開,否則的話會直接回傳HTTP的403狀態(Forbidden)。這個開關確實很方便,但它完全不能做其它額外的設定。也就是說,如果您只想要讓部份圖片連結有Hotlink Protection,或是也想把其它網域納入白名單的話,這個功能就不太合用了。

用Cloudflare的Workers來實作Hotlink Protection

Cloudflare提供的「Workers」可以針對有開啟CDN服務的網域加入額外的中介程式。中介程式所使用的程式語言為JavaScript,建議使用Chrome瀏覽器或是其它以Chromium為核心的瀏覽器來做這個小節的動作。

cloudflare-hotlink-protection

官方也有在文件中提供Hotlink Protection的程式碼樣板:

https://developers.cloudflare.com/workers/recipes/hotlink-protection/

addEventListener('fetch', event => {
  event.respondWith(fetchAndApply(event.request))
})

/**
 * If the browser is requesting an image and 
 * the referer does not match your host
 * we redirect the request to your page
 */
async function fetchAndApply(request) {
  // Fetch the response.
  let response = await fetch(request)

  // If it's an image, engage hotlink protection based on the
  // Referer header.
  let referer = request.headers.get('Referer')
  let contentType = response.headers.get('Content-Type') || ''
  if (referer && contentType.startsWith('image/')) {
    // It's an image and there's a Referer. Verify that the
    // hostnames match.
    if (new URL(referer).hostname !==
        new URL(request.url).hostname) {
      // Hosts don't match. This is a hotlink. Redirect the
      // user to our homepage.
      return new Response('', {
        status: 302,
        headers: {
          'Location': '/'
        }
      })
    }
  }

  // Everything is fine, return the response normally.
  return response
}

以上這個樣板,可以限制圖片連結,只能夠在相同的網域中被開啟。如果是不同的網域,就會被暫時轉址到該圖片連結網域的首頁。

若要使用這個樣板,我們需要先開啟Cloudflare的「Workers」編輯器。

cloudflare-hotlink-protection

接著建立一個新的腳本(Script),腳本名稱自訂。在此筆者將這個腳本的名稱取為「image-hotlink-protection」。

cloudflare-hotlink-protection

cloudflare-hotlink-protection

接著先不要去編輯腳本,先來到路徑(Route)的設定分頁。

cloudflare-hotlink-protection

新增要套用腳本的網址,網址可以使用星號「*」來符合所有字串組合。以本站來說,本站的圖床網域為「img.magiclen.org」,文章中所有的圖片都放置在這個「img.magiclen.org」網域下,因此我填入的網址為「https://img.magiclen.org/*」。

cloudflare-hotlink-protection

cloudflare-hotlink-protection

新增好網址後,就可以回去腳本分頁,編輯剛才新增的腳本。

cloudflare-hotlink-protection

將樣板的程式碼貼上。

cloudflare-hotlink-protection

接著就可以改改這個樣板程式碼,使它更符合我們的需求。以本站的情況來說,我們希望被盜連的圖,可以轉而去連「https://magiclen.org/images/OG-image.jpg」,所以就去修改回傳的HTTP標頭的「Location」欄位,直接將網址填入。這邊要注意的是,填進「Location」欄位的網址,最好不要去啟用Hotlink Protection。

cloudflare-hotlink-protection

同樣再以本站的情況來說,我們希望我們的圖床「img.magiclen.org」,可以至少在本站網域「magiclen.org」和子網域中正常使用。因此去修改原先判斷「hostname」的方式,如下圖:

cloudflare-hotlink-protection

腳本程式撰寫完後,就可以按下「Deploy」(部署)按鈕來套用。

cloudflare-hotlink-protection