當我們把大部分的運算移往前端瀏覽器時,難免會遇到需要一個相對準確的當下時間,我們都知道客戶端的時間是不可信的,有可能使用者從來不校時,或是使用者有屬於自己的時差,又或者使用者與之校時的伺服器,其時間根本就是錯的,種種原因都造成客戶端的時間不可信,面對這個問題,我們第一時間想到的大概就是做一個取得後端伺服器時間的 Web API 給前端使用,這篇文章要來分享儘量不透過後端 Web 應用程式來取得伺服器時間。
「Date」Response Header
Date Response Header 是一個很容易讓人遺忘的 Header,但是幾乎每一個回應都有 Date Header,只要回應是真正從伺服器傳來的,那麼這個 Date 就是伺服器的時間,即使是 400、404、500、... 這樣非 200 的回應都有 Date Header。
所以利用這個特性,我們可以準備一個靜態檔案,這個靜態檔案的內容可以是空的沒關係,把它放在 Web 伺服器上,提供給前端來存取,下面我就產生一個 server-time.js
,放在 IIS 的 Default Web Site 上,示範透過 fetch API 及 jQuery 的 $.ajax() 來取得 Date Response Header。
另外,為了避免瀏覽器本身的 Cache,我們會需要在 Request Header 加上 Cache-Control: no-cache
,確保每次的回應都是真正從伺服器傳來的。
CDN + server-time Web API
如果我們的網站前面是有卡 CDN 的話,我建議可以搭配 CDN 來處理取得伺服器時間的問題,我們的 Web API 有上 CDN 的話,CDN 的 Response Headers 通常都會有一個 Age Header,這個 Age Header 的值是以秒為單位,說明了我們 Web API 的內容在 CDN 上被 Cache 了多久。
所以,利用上述這個特性我們就可以做一個 server-time
的 Web API,傳回的是伺服器當下的 Unix Timestamp,讓它 Cache 在 CDN 上很長一段時間,讓 CDN 來代替後端 Web 應用程式提供伺服器的時間。
透過被 CDN Cache 當下的 Server Time,加上 Age Header 的值,就可以得到當下的伺服器時間。
還有,我們無法控制使用者的網路到我們的 CDN 之間是不是還有其他的 Proxy 或 CDN,因為某些中間代理的機制會擅自更改來源的 Response Headers,所以我增加了識別的機制,檢查回應如果是來自於我們的 CDN,則才去解析 Age Header 的值。
限制
無論是透過 Date Header 或是 Age Header 得到的伺服器時間,還是會因為網路的延遲、客戶端設備的運算速度而有所影響,但是誤差值都還在可以接受的範圍,如果需要取得非常精確的時間就要尋求其他方式了。