1. 程式人生 > 其它 >更快更安全,HTTPS 優化總結

更快更安全,HTTPS 優化總結

在網站升級到 HTTPS 之後,我們還可以有很多玩意可以折騰,優化 HTTPS,讓它更快更安全。這裡是一篇 HTTPS 優化的總結,也包含問題的解決方法,不過不僅僅包括 HTTPS 的優化,也包含 HTTP 一些安全相關的配置。

因為平時用 Nginx 比較多,本文涉及到 Web Server 的大多數例子都會以 Nginx 為例。如果有錯誤歡迎指出。HTTPS 發展很快,尤其是在谷歌的推動之下,如果有過時的地方,也請指出。

HSTS

HSTS(HTTP Strict Transport Security)介紹

瀏覽器在訪問站點的時候,如果沒有指定 HTTPS 訪問,會預設使用 HTTP,所以我們會將 HTTP 重定向(301或302)到 HTTPS。這樣看起來沒有問題,但是當使用重定向進行跳轉時,網站就存在被劫持的可能。

因此有了 HSTS, 採用 HSTS 協議的網站將保證瀏覽器始終連線到網站的HTTPS版本,而不需要使用者手動在URL位址列中輸入包含 https://的加密地址,實現了一種新的跳轉方式(瀏覽器識別後直接跳轉),使用者訪問到的直接就是 HTTPS 的版本。HSTS 還可以用來防止基於 SSL Strip 的中間人攻擊。

HSTS 的 HTTP 頭部格式如下:

Strict-Transport-Security: max-age=<expire-time>[; includeSubDomains][; preload]

說明:

  • max-age:有效時間;瀏覽器記住的有效時間
  • includeSubDomains:是否包含子域名;可選引數
  • preload:是否預載入;可選引數

使用 HSTS

只需要加入相應的 HTTP 頭部資訊就可以。比如我們配置以頭部資訊:

Strict-Transport-Security: max-age=31536000; includeSubDomains

以 Nginx 為例可以這樣配置:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

HSTS Preloading

介紹

在上一條 HSTS 中,我們實現了瀏覽器維持 HTTPS 連線,但是仍然存在一個問題,如果我們是第一次訪問該站點呢?那瀏覽器並不知道該站點的配置,所以也就不知道應該用 HTTPS 去連線,這個問題怎麼解決?

HSTS Preload List(https://hstspreload.org/),是一個谷歌維護的列表,現在大部分主流瀏覽器都支援這個列表,這個列表直接告訴瀏覽器要用 HTTPS 訪問的站點有哪些,所以在訪問站點之前,瀏覽器先撈一遍這個列表,如果要訪問的站點在這裡面,就直接用 HTTPS 進行訪問,所以即使是第一次訪問,也會走 HTTPS 了。

加入Preload List

只需要前往這個站點 HSTS Preload List(https://hstspreload.org/) (可能需要訪問外國網站訪問)提交你的站點就可以,通過之後就加入 HSTS 預載入列表了。

在提交之前,你需要注意以下幾點:

  1. 提供有效的站點證書
  2. 將 HTTP 重定向到 HTTPS
  3. 所有的子域名也都要支援 HTTPS
  4. HSTS 頭部配置需要:
    1. max-age 需要至少 31536000 秒 (1年)
    2. 必須包含includeSubDomains引數
    3. 必須包含preload引數

比如 Nginx 為例修改配置為:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

注意事項

加入預載入列表時候要想從列表中刪除,需要很長的時間,如果你只是暫時玩玩 HTTPS,之後還會切換回 HTTP,需要謹慎考慮。

HTTP/2

HTTP/2 介紹

即 HTTP 2.0,是下一代的 HTTP 協議,目前大量採用的是 HTTP 1.1,HTTP/2 現在只支援 HTTPS 開啟。

HTTP/2 有這些特點:

  • 徹底的二進位制協議,頭資訊和資料體都是二進位制
  • 多路複用請求
  • 對請求劃分優先順序
  • 壓縮HTTP頭
  • 伺服器推送流(即Server Push技術)
  • 保持與HTTP 1.1語義的向後相容性

其中有些新東西都處於摸索階段,比如Server Push技術。目前 Nginx 1.13.9版本中已經包含了 Server Push,參考 Introducing HTTP/2 Server Push with NGINX 1.13.9;但是 Nginx 當前的 stable 版本是 1.12,我準備在未來 1.13 的 stable 版本中再開啟,相信效能又會有一定的提升。

上 HTTP/2 給我們帶來的最直觀的體驗就是,極大地加快了站點頁面的載入速度。

使用 HTTP/2

如果是使用 Nginx,我們可以非常方便地就直接升級到 HTTP/2,只需要注意以下幾點:

  • HTTP2 現在需要 HTTPS
  • HTTP2 要求 Nginx 版本是1.9.5以上
  • openssl 版本要求1.0.2

然後,修改Nginx的配置:在監聽埠的配置 listen443ssl 後面加上 http2 default_server就行了。

注意事項

如果在 Chrome51 版本的 Chrome 瀏覽器中,HTTP/2不生效,檢查一下是否支援 ALPN,支援 ALPN 需要開啟 OCSP Stapling。

OCSP Stapling

OCSP Stapling 是什麼

OCSP (Online Certificate Status Protocol) 通常是 CA 提供來實時驗證證書是否合法有效的。客戶端就可以根據證書中的 OCSP 資訊,傳送查詢請求到 CA 的線上驗證地址來查詢證書是否有效。OCSP 的問題在於,對 CA 機構的驗證介面高可用性有要求,增加了瀏覽器握手的延時。

OCSP Stapling 技術是對 OCSP協議 的進一步升級。伺服器事先模擬瀏覽器對證書鏈進行驗證,然後將 OCSP 驗證結果快取到本地。這樣,當瀏覽器訪問站點時,在握手階段,可以直接拿到 OCSP 響應結果和證書鏈,就不需要再向 CA 請求介面,對訪問速度有明顯提升。

開啟 OCSP Stapling

檢測 OCSP Stapling 的狀態:

openssl s_client -connect [yoursite.com]:443 -status

如果支援 OCSP Stapling 會看到 OCSPResponseData內有以下內容:

OCSP Response Status: successful (0x0)

而如果不支援,不會有 OCSPResponseData的內容。

Nginx 中開啟 OCSP Stapling

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/certs/chained.pem;

如果 sslcertificate 指令指定了完整的證書鏈,則 ssltrusted_certificate 可省略。例如:

server
{
    listen 443 ssl;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    ssl_certificate /etc/ssl/bundle.crt;
    ssl_certificate_key /etc/ssl/your_domain_name.key;

    ssl_stapling on;
    ssl_stapling_verify on;
}

重啟 Nginx 即可生效。

Apache 中開啟 OCSP Stapling

<VirtualHost></VirtualHost> 中新增:

SSLUseStapling on

<VirtualHost></VirtualHost> 外新增:

SSLStaplingCache shmcb:/tmp/stapling_cache(128000)

例如:

SSLStaplingCache shmcb:/tmp/stapling_cache(128000)
<VirtualHost *:443>
SSLEngine on
SSLProtocol all -SSLv3 -SSLv2

SSLCertificateFile /path/to/your_domain_name.crt
SSLCertificateKeyFile /path/to/your_private.key
SSLCertificateChainFile /path/to/root.crt

SSLUseStapling on
</VirtualHost>

然後重啟 Apache。

TLS False Start

TLS False Start 意味著搶先開始。在 TLS 握手的過程中,客戶端在傳送 Change Cipher Spec 和 Finished,即握手完成前,就開始傳送應用層的請求資料,服務端在 TLS 握手完成時直接返回響應資料。

開啟 TLS False Start 非常簡單,以 Nginx 為例,在配置中加上:

ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL:!eNULL:!MEDIUM:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!SEED;

ssl_ciphers 加密方式的配置因人而異,可以參考一些資料琢磨一下。

Session resumption

Session ID resumption

如果客戶端和伺服器端都儲存了 session keys,我們就可以重用加密的 session。通過給每個連線一個唯一標識,服務端可以知道一個進來的連線是否在之前已經建立過連線,如果在伺服器中也存在這個 session 的 key,那麼它就能重用。

重用 Session ID 需要伺服器儲存 Session 狀態等,這樣下次連線才能複用,這就需要伺服器儲存很多狀態資訊,所以耗費記憶體。

重用 Session ID 在 Apache 中可以通過 SSLSessionCache 配置,在 Nginx 中可以通過 ssl_session_cache 設定。

以 Nginx 為例,我們配置以下內容:

  • sslsessioncache 設定儲存SSL會話的快取型別和大小。預設值為 ssl_session_cache off,off為關閉,還有一些其它的快取型別,不過這裡建議使用shared共享快取型別,這種方法更為有效。
  • sslsessiontimeout 客戶端能夠反覆使用儲存在快取中的會話引數時間

例如:

ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;

共享快取,快取大小為50m,快取時間1天。

Session ticket resumption

在 Session ticket 重用中,伺服器不需要儲存所有的建立的 session 的狀態資訊,反而將狀態儲存成塊狀資料交給客戶端來維護。Session tickets 允許伺服器將某些資訊儲存到客戶端,類似於HTTP cookies 在資訊驗證的應用。

Session ticket 就是加密的儲存了重用 TLS 連線所需要的資訊的塊資料(比如 session keys)。通常使用只有伺服器才知道的 ticket key 來加密。

伺服器在初始握手期間向客戶端傳送 Session ticket 以便本地儲存。當重用 session 時,客戶端會將 Session ticket 傳送回伺服器交給伺服器進行解密,然後恢復會話。

複用 session ticket 在 Apache 中可以通過 SSLTicketKeyDefault 配置,在 Nginx 中可以通過 ssl_session_tickets 設定。 在 Nginx 中,例:

ssl_session_tickets on;
ssl_session_ticket_key current.key;
ssl_session_ticket_key previous.key;

Nginx 中使用 ssl_session_ticket_key file; 來配置用於加密和解密 SSL session_ticket的金鑰,如果用了多個指令檔案,則僅第一個指令檔案中的金鑰用來加密和解密;其它的金鑰檔案(下面的)用來解密,這樣的原因是,我們最好定期輪換加解密的 key,輪換的時候把舊的放在下面用來解密舊的 ticket,第一個放新的,用來加解密新的請求。

如果沒有配置 key 檔案,則 openssl 預設會在 ssl 初始化的時候生成隨機數的 key;這種時候只有在重啟 web server 的時候才會重新生成隨機 key。

Session ID resumption 與 Session ticket resumption

複用 session ticket 和 複用 session ID 的區別在於,複用 session ID 時在伺服器和客戶端儲存了 key,連線時比對兩邊的資料是否一致;而 session ticket 將資料加密後儲存在客戶端,客戶端請求時帶回資料讓伺服器解密,正常則複用,只有釋出的服務端能夠解密該資料。

如果在握手階段 session ID 和 session ticket 都提供了,將以 session ticket 為準,如果在 session ticket 階段被 pass 掉了才通過 session id 取 cache 中的資訊來複用。

Public-Key-Pins

Public-Key-Pins 用來做什麼呢

任何一家受信任的 CA 都可以簽發任意網站的站點證書,瀏覽器識別起來都是合法的,這些受信任的 CA 可以簽發任意網站的站點證書(包括你的站點),而這些受信任的 CA 有很多,如果某 CA 中的某鏈被攻破,就可以造成由偽造或不正當手段獲得網站證書的中間人攻擊。

所以 Public-Key-Pins 就是用來告訴瀏覽器當前網站的證書指紋,包括配置過期時間,在過期時間內,瀏覽器再次訪問這個網站的話就必須驗證證書鏈中的證書指紋,如果跟之前指定的證書指紋不匹配,就無法訪問。

如果我們自己更換了證書呢?為了避免這個情況導致的問題,所以我們在配置指紋的時候,至少配置兩個,其中包含一個備用指紋。

關於 HTTP Public Key Pinning (HPKP)的介紹,這裡非常詳細:PublicKeyPinning(https://developer.mozilla.org/en-US/docs/Web/HTTP/PublicKeyPinning)

Public-Key-Pins 的 HTTP 頭的格式如下:

Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"]

說明:

  • pin-sha256:Base64加密的證書指紋;一般情況至少指定兩個,其中包含一個備用指紋。
  • max-age:過期時間,秒
  • includeSubDomains:是否包含子域
  • report-uri:可選引數;驗證失敗時的上報地址

pin-sha256證書指紋這個配置很容器搞錯,我們需要指定一個備用指紋,而這個指紋並不是當前域名證書鏈中的指紋,應該是一個不在當前鏈中,未來有可能更換到該鏈的備用指紋。比如我的證書鏈,根證書是 DigiCert Global Root CA,中間證書是 Encryption Everywhere DV TLS CA - G1,再加上域名證書,我就可以配置中間證書為第一個pin-sha256,而第二個證書配置 Let's Encrypt 的證書指紋,這樣以後如果用 Let's Encrypt Authority 簽發證書,老使用者不會受到影響。

這裡 HTTP Public Key Pinning: You’re doing it wrong! 非常詳細地介紹了在配置這個配置時會出現的問題。

使用

生成 pin-sha256指紋,可以通過以下的方式來生成。

通過 RSA key 檔案生成:

openssl rsa -in my-rsa-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64

通過 ECC key 檔案生成:

openssl ec -in my-ecc-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64

通過 CSR 檔案生成:

openssl req -in my-signing-request.csr -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

通過域名之間生成:

openssl s_client -servername www.example.com -connect www.example.com:443 | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

生成證書指紋之後就可以將指紋加入到配置中。

Apache配置:

Header always set Public-Key-Pins "pin-sha256="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubDomains"

Nginx配置:

add_header Public-Key-Pins 'pin-sha256="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubDomains' always;

DNS CAA

DNS CAA 是什麼

CAA(Certificate Authority Authorization),即證書頒發機構授權。簡單地說,就是當域名的 DNS 解析存在 CAA 記錄時,則只允許在記錄中列出的 CA 機構頒發針對該域名(或子域名)的證書。CAA 記錄可以控制單域名 SS L證書的發行,也可以控制萬用字元證書。

所以設定了 CAA,如果有一天想更換非 CAA 記錄中的 CA,要記得把 DNS CAA 的解析記錄消掉。否則頒發會失敗。

設定 CAA

目前國內的雲服務中,阿里雲支援 CAA 的 DNS 解析,因為我本人用的阿里雲,所以其它雲服務產商不太瞭解,在 DNS 解析處看看就知道支援不支援了,如果不支援想新增的話也可以換用支援的 DNS 服務商來解決。

新增 DNS 解析,選擇 CAA 型別,填寫萬用字元還是非萬用字元的,然後就是記錄值了。

關於記錄值,我們可以簡單地使用 CAA Record Helper(https://sslmate.com/caa/) 來自動生成,非常方便。將生成的記錄值(比如 0issue"digicert.com" 這樣的)填入到 DNS 解析中既可。

X-Frame-Options 響應頭

X-Frame-Options HTTP 響應頭是用來給瀏覽器指示是否允許一個頁面在 <frame><iframe>或者 <object> 中展現的標記。通過設定 X-Frame-Options HTTP 響應頭,我們可以確保自己的網站內容沒有被嵌到別人的網站中去,也從而避免了點選劫持 (clickjacking) 的攻擊。

X-Frame-Options 有三個值:

  • DENY:表示該頁面不允許在 frame 中展示,即便是在相同域名的頁面中巢狀也不允許。
  • SAMEORIGIN:表示該頁面可以在相同域名頁面的 frame 中展示。
  • ALLOW-FROM uri:表示該頁面可以在指定來源的 frame 中展示。

就是說,如果設定為 DENY,不光在別人的網站 frame 嵌入時會無法載入,在同域名頁面中同樣會無法載入;如果設定為 SAMEORIGIN,那麼頁面就可以在同域名頁面的 frame 中巢狀。

CSP Level 2 規範中的 frame-ancestors 指令會替代這個非標準的 header。CSP 的 frame-ancestors 會在 Gecko 4.0 中支援,但是並不會被所有瀏覽器支援。然而 X-Frame-Options 是個已廣泛支援的非官方標準,可以和 CSP 結合使用。

Apache 配置

要把下面這行新增到“site”的配置中:

Header always append X-Frame-Options SAMEORIGIN

Nginx 配置

在“http”,“server”或者“location”的配置中:

add_header X-Frame-Options SAMEORIGIN;

X-Content-Type-Options 響應頭

我們都知道 Content-Type 是用來標識資源型別的。瀏覽器有個特性,就是當有些資源的 Content-Type 沒定義或者定義錯了,瀏覽器會啟用 MIME sniffing 來檢測該資源的型別然後解析內容並執行。所以攻擊者可以利用瀏覽器這個特性讓原本的請求中的資源型別解析為其它型別,所以一般情況下我們都禁止瀏覽器去檢測型別:

X-Content-Type-Options: nosniff

在 Nginx 中我們可以加上配置:

add_header X-Content-Type-Options nosniff;

Server Banner

伺服器版本號不應該存在在響應頭中,這樣容易讓攻擊者找到弱點。

Nginx 中可以增加以下配置:

server_tokens off;

就會去除版本號,比如 nginx/1.10.3 就變成了 ningx.

Web framework Information

和伺服器版本號一樣,我們應該移除一些頭部框架資訊,比如 X-Powered-By,X-Runtime,X-Version,X-AspNet-Version等。

在 Nginx 中加配置:

proxy_hide_header X-Powered-By;

如果是 fastcgi 模式的 PHP 應用則用:

fastcgi_hide_header X-Powered-By;

在 PHP 配置 php.ini 中移除版本號,可以設定 expose_php:

expose_php = off

X-XSS-Protection 響應頭

XSS Protection 顯然是用來防止 XSS 攻擊的,這個不需要多解釋了。

我們只需要知道,現在主流瀏覽器都支援,並且預設都開啟了 XSS 保護。配置這個響應頭可以將它關閉,但是如果你沒有更加好的防範 XSS 的解決方案,就留著吧。

在 Nginx 中配置啟用XSS保護,並在檢查到XSS攻擊時,停止渲染頁面:

add_header X-Xss-Protection "1; mode=block";

Content Security Policy

Content Security Policy(CSP),是用來有效防止 XSS 攻擊的,實際上就是提供了一個白名單告訴客戶端,哪些外部資源可以載入和執行。通過頁面的 <meta>標籤和 HTTP 的 Content-Security-Policy 頭資訊的都可以控制 CSP。

關於 CSP,我覺得阮一峰老師的這篇文章挺不錯的:Content Security Policy 入門教程(http://www.ruanyifeng.com/blog/2016/09/csp.html),以及 google developer 的:內容安全政策(https://developers.google.com/web/fundamentals/security/csp/?hl=zh-cn)。

CSP 的配置需要根據自己的實際情況來配置。這裡我就舉個例子:

Content-Security-Policy: default-src 'self' blob: data:; script-src 'self' 'unsafe-inline' *.baidu.com; img-src *

這裡定義所有的資源型別都預設只能從當前域名載入( 'self'),然後定義了 blob:data:(實際上如果不是非要載入字型 font 等,使用要謹慎,攻擊者很容器利用 data:URIs注入攻擊) 資料型別也允許載入; script-src 定義了只能從當前域名載入, 'unsafe-inline' 是為了使用監聽事件,允許載入百度的域名 *.baidu.com(使用百度統計的話); img-src* 而圖片檔案可以從任何域名載入。

以上只是隨便舉一個例子,這一套配置需要自己來定製。而且這個配置挺麻煩的,需要自己慢慢除錯,特別是如果引用了很多外部資源,不過在安全上起到很好的效果。

Cache Control

Cache Control 表示輸出頁面的快取首選項。強烈推薦自定義快取的偏好,如果不定義 Cache Control,將由瀏覽器或者代理來選擇是否快取記憶體,而這種不受控制的選擇有可能會導致效能問題或安全問題。

每個資源都可通過 Cache-Control HTTP 標頭定義其快取策略,Cache-Control 指令控制誰在什麼條件下可以快取響應以及可以快取多久。

Cache-Control 是一個靈活的配置,我們應該根據自己的需求定義最佳 Cache-Control 策略,參考 HTTP 快取(https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching)。

Secure Cookies

如果站定已經是基於 HTTPS 的,包含敏感資訊的 cookie,特別是 session id(在使用 session時,會在客戶端儲存一個cookie,記錄 session id),需要被標記為安全的。 例如以下這個設定 cookie 的頭:

set-cookie: PHPSESSID=03196cccbf3a8cd7d4fb22214fc5111e; path=/

HTTPS 站點加上 secure 標誌:

set-cookie: PHPSESSID=03196cccbf3a8cd7d4fb22214fc5111e; path=/; secure;

這樣只有在 HTTPS 下 cookie 才能生效。另一種防範不安全的 cookie 通過 HTTP 傳送的方式是 HSTS,上面已經提到過了,建議同時開啟 HSTS 和 secure cookie。

Session cookies 應該標記上 HttpOnly,防止通過 JavaScript 訪問,攻擊者可以利用這點進行 XSS 攻擊竊取 Session cookies。其它的 cookie 可以不這麼嚴格地標記,但是除非有從 JavaScript 去訪問的需求,都建議標記上 HttpOnly。

例如上面的 set-cookie,我們需要修改成以下的頭資訊:

set-cookie: PHPSESSID=03196cccbf3a8cd7d4fb22214fc5111e; path=/; secure; HttpOnly

不要混用 HTTPS 和 HTTP

最後一點,不要 HTTPS 和 HTTP 混用,既然上了 HTTPS,還留 HTTP 做什麼呢?為了 SEO 麼?

隨著時間推移,越早全站 HTTPS 優勢只會越明顯。

其它優化操作

SRI(Subresource Integrity)

子資源完整性(SRI)是允許瀏覽器檢查其獲得的資源(例如從 CDN 獲得的)是否被篡改的一項安全特性。它通過驗證獲取檔案的雜湊值是否和你提供的雜湊值一樣來判斷資源是否被篡改。

SRI 目前的瀏覽器相容性不好,而且會增加程式碼和配置的複雜度。可以參考:Subresource Integrity

Iframe Sandbox

Iframe Sandbox是html5的新屬性,是專門為 iframe 定製的,如果你用了 iframe,才建議加上這個屬性。可以參考: <iframe>(https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/iframe)

總結

基本上以上內容做完就差不多了,SSLLabs 上測試肯定是 A+ 的。先到這裡,以後再看看有沒有什麼要修改或者補充的。

最後貼兩個常用的測試站點

  • Qualys SSL Labs:https://www.ssllabs.com/ssltest/
  • HTTP Security Report:https://httpsecurityreport.com/

注:本文首發在我的部落格:更快更安全,HTTPS 優化總結(https://www.goozp.com/article/82.html)

參考資料

  • MDN web docs HTTP:https://developer.mozilla.org/en-US/docs/Web/HTTP
  • Speeding up HTTPS with session resumption:https://calendar.perfplanet.com/2014/speeding-up-https-with-session-resumption/
  • HTTP Security Best Practice:https://httpsecurityreport.com/best_practice.html
  • Guidelines for Setting Security Headers:https://www.veracode.com/blog/2014/03/guidelines-for-setting-security-headers
  • HTTP 快取:https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching

覺得本文對你有幫助?請分享給更多人。