將WordPress 使用的CDN 從CloudFlare 切換到AWS CloudFront
AKA “如何白嫖貴死人的AWS”
CloudFlare 這間公司是個大善人,多年前剛起步的時候我不由得感嘆我靠!為什麼會有人做這種事情,在Twitter 上給CEO 發消息感激他的貢獻,當時他還回我了。
後來順著internet https 的流行,率先推出不要錢的SSL/TLS offloading 服務,引起一大波網站從其他CDN 遷移,因為其他的CDN 光是證書就要收上一大筆錢。
再後來他的WAF 規則對阻止wordpress 的spam comments 效果很好,所以就一直用他。
但,他從中國訪問的效果一向不佳,早年和百度合作的時候,曾經有一段時間很順暢但非常短暫,他的香港節點並不直接提供給中國用戶使用,中國用戶直接訪問會被遞送到美國的節點,如果網站啟用了CloudFlare 的收費服務Argo Smart Routing,中國用戶才會被遞送到香港節點。是不是聽起來和AWS Global Accelerator很像?
之所以想要換成AWS CloudFront ,是因為經過這些年行業廝殺,也有了0$ 的方案,主要特徵如下:
3個Free Plan (官方網站沒說)
5GB S3 存儲用於靜態文件。
每月1M 請求量。
每月100GB 數據傳輸量。
5 條cache behavior rules (官方網站上說的是5條WAF rules,沒有講cache behavior rules)。
好了看這張圖吧,吧啦吧啦一大堆,有用的沒幾個:

簡單來說,每天的流量小於3.3G,請求量小於33333,就可以考慮使用0$ 方案的CloudFront。
之所以想要切換是因為CloudFront 雖然也會時不時因為別的網站連帶被GFW 屏蔽掉,但比起來,CloudFlare 看起來是無差別在中國境內被限速,有時候在中國境內,CloudFront 還是蠻快的,畢竟浙江那麼多做海外電商的中小企業,每天都在抱怨錄入一個商品清單都要花兩個小時。
這是目錄:
【1】使用LetsEncrypt Certbot 為源站創建並啟用免費的正式 Certificate 。
【2】創建一個Free Plan Distribution。
【3】設置Nginx驗證header 以保護源站和只允許日本IP地址可以訪問到WP 的login page。
【4】Cache Behaviors 配置,針對wordpress 的兩種緩存路徑方案。
【5】使用AWS ACM 創建CloudFront 使用的免費正式 Certificate。
【6】安裝wordpress plugin C3-Cloudfront-Cache-Controller 來自動刷新CloudFront 緩存。
【8】使用CloudFront Function 來保護 wp-login.php和xmlrpc.php 。
首先為我的源站更換TLS 證書,因為CloudFront 只接受使用合法證書的源站,不接受自簽發證書。CloudFlare 雖然是支持無法驗證的自簽發證書,但是他也支持使用client/server 證書雙向驗證這種更安全的機制確保源站不會被直接訪問。
免費的合法證書,騰訊雲曾經提供過一段時間一年效期證書,但是到期後需要手動重新申請,不知道現在還有沒有,不過現在流行的是letsencrypt 可以免費續期,90天效期的合法數字證書。
安裝certbot和nginx plugin:
dnf install certbot python3-certbot-nginx
進行證書簽發,此時需要確保站點是可以訪問的,因為letsencrypt 會通過訪問站點完成驗證,建議先將網站域名直接解析至IP地址。
certbot --nginx -d bbken.org -d www.bbken.org
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Renewing an existing certificate for bbken.org and www.bbken.org
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/bbken.org/fullchain.pem
Key is saved at: /etc/letsencrypt/live/bbken.org/privkey.pem
This certificate expires on 2026-02-27.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Deploying certificate
Successfully deployed certificate for bbken.org to /etc/nginx/vhosts/bbken.org.conf
Successfully deployed certificate for www.bbken.org to /etc/nginx/vhosts/bbken.org.conf
Your existing certificate has been successfully renewed, and the new certificate has been installed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
雖然他說已經setup a scheduled task 但是有時候並沒有,如果驗證後沒有,那麼需要自己創建一個定時器:
創建定時器:
sudo tee /etc/systemd/system/certbot-renew.timer << 'EOF'
[Unit]
Description=Certbot Renewal Timer
[Timer]
OnCalendar=daily
RandomizedDelaySec=12h
Persistent=true
[Install]
WantedBy=timers.target
EOF
創建服務:
sudo tee /etc/systemd/system/certbot-renew.service << 'EOF'
[Unit]
Description=Certbot Renewal
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
EOF
啟用服務:
sudo systemctl daemon-reload
sudo systemctl enable --now certbot-renew.timer
驗證服務:
sudo systemctl status certbot-renew.timer
sudo systemctl list-timers | grep certbot
到這裡,證書就會每天檢查,一旦需要更新就會自動更新,那就等90天後再看看吧,反正現在看也看不出什麼來。
源站配置好之後,接下來需要配置CloudFront,添加一個新的distribution,
Create distribution ,輸入一個名字,例如CF-bbken.org,隨便輸入什麼貓貓狗狗都可以。

選擇源站類型,靜態網站也可以使用Amazon S3,比如做圖片網站,相冊,使用hugo 做blog,純靜態html 發布,這裡我使用Other,然後在下方輸入源站域名,因為源站不支持IP地址,所以你必須使用一個二級域名指向IP地址,例如source.bbken.org。

頁面拉到下方,選擇Customize origin settings,創建一個custom header,因為,失去了CloudFlare 的client/server 雙向數字證書驗證保護源站,要如何來保護CloudFront 後面的源站呢?
通過設置源站的 custom header,CloudFront 在向源站發送的每一個請求都會附加這個header,源站將通過這個header 識別來自於CloudFront 的請求並拒絕其他請求,從而保護源站不受到攻擊,這裡我使用如下的配置:
header name = X-Origin-Verify
value = 5Pb9Qsf2XbZK23C

與此同時,在nginx 的host 配置文件中,加入下面的配置,該配置將拒絕所有不帶有這個header 的請求從而保護源站:
###bbken.org at 443####
server {
server_name bbken.org www.bbken.org;
listen 443 ssl;
listen [::]:443 ssl;
# Block all requests without CloudFront header
if ($http_x_origin_verify != "5Pb9Qsf2XbZK23C") {
return 403;
}
同時,為了保護wordpress 的login page 和xmlrpc api,可以加入下面的配置,例如我可以只允許我的IP地址31.13.87.36 訪問wp-login.php,並限制只有來自於日本的IP地址可以訪問到xmlrpc api:
location = /wp-login.php {
allow 31.13.87.36; # Your IP address
deny all;
}
include fastcgi-ssl.conf;
fastcgi_pass unix:/home/www/php.sock;
}
location = /xmlrpc.php {
if ($http_cloudfront_viewer_country != "JP") {
return 403;
}
但用戶的請求中並不會帶有country code 呢,這個country code 從何而來?這就是下一步的cache settings 中需要修改的部分:

將cache policy 設置為CachingOptimozed,將Origin request policy 設置為AllViewerAndCloudFrontHeaders-2022-06。
這裡配置的是default 路徑下所有文件的cache policy,在源站請求策略中,需要為源站送去所有來自於用戶的headers 並且加上CloudFront 額外的headers, 其中就包括了根據Maxmind GeoIP database 分析用戶IP地址所得出的 country code。
你可以仔細閱讀官方用戶指南中,CloudFront 加上了哪些headers。
也可以寫一個頁面來獲取他,例如headers.php
<?php
header('Content-Type: text/plain');
print_r(getallheaders());
?>
他長這個樣子,比如我在日本,訪問的時候甚至可以精確到城市Osaka,當然,Maxmind 的GeoIP city 免費資料庫出錯的機率有時候還是很大的,我想以AWS 很摳的風格來說,應該不會去買收費的資料庫。
Array
(
[Cloudfront-Viewer-Latitude] => 34.84230
[Cloudfront-Viewer-Time-Zone] => Asia/Tokyo
[Cloudfront-Viewer-Postal-Code] => 562-0021
[Cloudfront-Viewer-City] => Osaka
[Cloudfront-Viewer-Country-Region-Name] => Osaka
[Cloudfront-Viewer-Country-Region] => 27
[Cloudfront-Viewer-Country-Name] => Japan
[Cloudfront-Viewer-Country] => JP
[Cloudfront-Viewer-Http-Version] => 3.0
[Priority] => u=0, i
[Sec-Fetch-Dest] => document
[Sec-Fetch-User] => ?1
[Sec-Fetch-Mode] => navigate
[Sec-Fetch-Site] => none
[Upgrade-Insecure-Requests] => 1
[Sec-Ch-Ua-Platform] => "macOS"
[Sec-Ch-Ua-Mobile] => ?0
[Sec-Ch-Ua] => "Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"
[X-Amz-Cf-Id] => Sc-drS9O65XlBKlqmXBgQy5o8GKUtQ4pRw8NiqKxWEBvkIk7Bh4VKg==
[Cloudfront-Viewer-Tls] => TLSv1.3:TLS_AES_128_GCM_SHA256:fullHandshake
[Cloudfront-Viewer-Address] => 152.69.197.33:49333
[Accept-Encoding] => br,gzip
[Cloudfront-Viewer-Longitude] => 135.50400
[Cloudfront-Viewer-Asn] => 31898
[Connection] => keep-alive
[Via] => 3.0 0cf2f9f29d4ea64bbc1cf639883c7e5a.cloudfront.net (CloudFront)
[User-Agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
[X-Forwarded-For] => 152.69.197.33
[Cloudfront-Forwarded-Proto] => https
[Accept] => text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
[Accept-Language] => en-US,en;q=0.9
[Cookie] => _ga=GA1.1.1737721359.1763112581; _ga_PBL0EPJRYT=GS2.1.s1764820194$o3$g1$t1764820240$j14$l0$h0; cf_clearance=AeyyOOy1qnnUVxucgyJEPLGjgOaxN7w3CJX0f7Kkty8-1764388065-1.2.1.1-84shCFFoEVI.YrH7rr6BuaQRU0UILV7DKkL8GyWY8O5cDTyCJ0LL9RleTZVQS11eUsBX.npUGs3UkUgDRUkBHsC3JndxqSC.W9TNlLYSOncByE5Mj88kppK55MB4.CwjTcDq6QoB7ULnu7I4hrFFiD6PQnRMY6TfBFZL_Sm2dyMcuXX3GF2Yvlr8wBqbsUWVLx_v8OuzDFViHOUXS9PqG4BsG1.R5ASPCPqbyr_bgXI
[Cloudfront-Is-Android-Viewer] => false
[Cloudfront-Is-Ios-Viewer] => false
[Cloudfront-Is-Desktop-Viewer] => true
[Cloudfront-Is-Smarttv-Viewer] => false
[Cloudfront-Is-Tablet-Viewer] => false
[Cloudfront-Is-Mobile-Viewer] => false
[X-Origin-Verify] => 5Pb9Qsf2XbZK23C
[Host] => bbken.org
[Content-Length] =>
[Content-Type] =>
)
預覽一下,堅決的創建他。

接下來進入Cache Behaviors 設置,添加幾個新的cache policy,網路上有很多關於wordpress 的cache policy 設置,他們使用傳統的legacy settings, 都已經老舊而顯得不合時宜,這裡有兩種方案提供給你,注意喔,不能超過5條喔:
第一種:預設緩存所有文件和路徑,然後將必要的不需要cache 的路徑單獨拿出來,請注意先後順序,有優先級。
優點:靜態化很強,如果使用了wp-supercache 進行靜態化的話,效果就很好,因為所有blog posts 都是pure html 進行緩存。
缺點:comments 的部分可能還需要加入到 Managed-CachingDisabled ,需要安裝plugin來自動刷新緩存,否則你新增一篇blog 但是首頁不會更新。

/wp-login.php
Managed-CachingDisabled
Managed-AllViewerAndCloudFrontHeaders-2022-06
/xmlrpc.php
Managed-CachingDisabled
Managed-AllViewerAndCloudFrontHeaders-2022-06
/wp-admin/*
Managed-CachingDisabled
Managed-AllViewerAndCloudFrontHeaders-2022-06
Default(*)
Managed-CachingOptimized
Managed-AllViewerAndCloudFrontHeaders-2022-06
第二種:預設不緩存所有文件和路徑,然後將需要緩存的路徑拿出來,請注意先後順序,有優先級。
優點:只緩存必要的部分,這兩個路徑是官方建議以及wp-supercache 建議,以及目前網路上大多數wordpress 和cloudfront 結合建議緩存的路徑。
缺點:對於靜態化的wordpress 無法做到所有的blog 頁面緩存。

/wp-includes/*
Managed-CachingOptimized
Managed-AllViewerAndCloudFrontHeaders-2022-06
/wp-content/*
Managed-CachingOptimized
Managed-AllViewerAndCloudFrontHeaders-2022-06
Default(*)
Managed-CachingDisabled
Managed-AllViewerAndCloudFrontHeaders-2022-06
配置好Behaviors 之後,回到Distribution 的首頁,我還沒有添加domain,沒有添加之前,CloudFront 上面就不能識別你的domain name,即使你將域名解析過來也是不行的,點擊Add domain。

輸入domain name,這裡我輸入兩個,你也可以輸入多個,甚至可以將不同的domains 放在同一個distrbution:

這個時候,會提示沒有TLS 證書,點擊Create certificate,

AWS certificate manager 會自動為你生成兩個需要在domain resolver 那邊需要添加的CNAME,將他們添加進去。

添加之後這個頁面會自動刷新驗證,DNS 解析生效時間不一,有的長有的短,但是現在大多數DNS 服務商都已經縮短為300秒,哪像我剛上網那時候都一天兩天的,不要錢的證書創建成功後,點擊Next。
!注意!如果你上面將多個不同的domains 加入到同一個certificate,client 將可以從webbrowser 中的certificate 識別到你所有的站點,如果你不想讓別人知道所有的站點都是你的,那麼還是把他們分開比較好,比如你做了一個支持藍營的網站,然後又做了一個支持綠營的網站,然後你把兩個網站的domains 放到了同一個certificate,被人發現就不太好了。

預覽一下,沒問題,點擊Add domains,

這樣就完全結束了!等待右上角的Deploying 完成,就可以訪問啦!

是不是很簡單。
好了回過頭來說wordpress 的cloudfront plugin,主要就是刷新緩存,安裝C3 Cloudfront Cache Controller,安裝完成後,首先需要創建一個IAM 用戶,為他賦予CloudFront 讀取和刷新緩存的權限,並分配一個AK/SK,當然,如果你的server 在EC2 ,你也可以使用instance profile ,這樣AK/SK 可以留空即可。輸入Distribution ID 和AK/SK,Save Changes。
輸入post id 來刷新,或是選擇Flush All Cache。
輸入post id 刷新,將會刷新post,分類以及首頁。
在更新一篇blog 後,他會自動刷新。

是不是很簡單。
那麼免費的5Gb S3 容量要如何使用?
將wp-content 完全offload 是不太可行的,因為wp-supercache 會往這個目錄裡面寫一些臨時文件,uploads 目錄倒是沒有頻繁的變動,適合放S3。
第一個問題,如何將server 上的文件同步過去?
首先需要建立一個S3 bucket,這個bucket 不需要一定在us-east-1,雖然CloudFront 一定在us-east-1,我這裡選用離我比較近的日本大阪,創建一個bbken.org bucket,然後創建一個wp-content 目錄就好了。
接下來需要使用aws cli 進行同步,其他的第三方S3 同步工具也是可以的。
首先在EC2 上安裝aws cli,安裝完成後,你可以在EC2 instance 上使用的IAM profile 中加入S3 的相關權限,或是直接在aws cli 中配置AK/SK,如果你使用的不是EC2 ,那麼你只能使用AK/SK。
如果使用IAM profile ,可以直接切換到www 用戶來執行看看同步有沒有問題:
aws s3 sync /home/www/bbken.org/wp-content/uploads/ s3://bbken.org/wp-content/uploads/ --delete
如果使用AK/SK,請在www 用戶下面配置,不要使用root,不要使用root,不要使用root!
確認執行沒有問題,加入到crontab 每5分鐘執行一次:
*/5 * * * * /usr/bin/aws s3 sync /home/www/bbken.org/wp-content/uploads/ s3://bbken.org/wp-content/uploads/ --delete
接下來修改CloudFront,首先需要為原來的 distribution 添加一個S3 的origin,前面添加的bucket 會自動出現在下拉菜單裡面,選上他,Origin path 留空,Name 可以為他起一個名字。

預設狀態下S3 bucket 是沒有public access 的,這裡需要為bucket 添加權限,讓CloudFront 可以訪問到他,在Origin access 中選擇Origin access control settings,並點擊右方的Create new OAC。
使用預設的配置,你必須選擇 sign requests,這代表CloudFront 會添加額外的header 來告訴S3 : I am CloudFront ,如果你不告訴S3 你是誰,那麼下面的Policy 將會拒絕訪問,即使請求來自於CloudFront 的IP。

為OAC 起個名字,Create! 此時下方會提示,你需要Copy Policy 為目的S3 bucket 進行設置。

這個Policy 長這樣,就像上面講的,這個Policy 將只允許來自於CloudFront sign 之後的請求:
{
"Version": "2008-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bbken.org/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456788888:distribution/EUUUUCCCC5555P"
}
}
}
]
}
點擊下方的 Go to S3 bucket permissions ,會跳轉到目標bucket ,點擊bucket 的 Permissions tab 。

選擇Edit Bucket policy ,然後把上面CloudFront OAC 設置輸出給你的Policy 貼進去,Save Changes。

這樣我就添加好並配置好了S3 的origin,但當下 cache 並不會讀這個origin,因為還沒有配置 cache behaviors,那麼創建一個新的 cache behavior,設置如下:
path pattern = /wp-content/uploads/*
Origin and origin groups = 上面配置的S3 origin,下拉菜單不可能選錯的。
對於S3 origin 來說,預設的Cache policy 就是Managed-CachingOptimized ,所以預設的配置都不用修改,Create behavior !

創建完成後,這一條 behavior 可能會位於下方,請將他move up 到第一條,等待 distribution deploying 完成就可以測試訪問了!
我們來使用curl 測試一個uploads 目錄中的css 文件,可以看到 header 中 server: AmazonS3,這表示文件是從S3 讀取出來的,成功!
curl --head https://bbken.org/wp-content/uploads/themify-customizer-8.1.0-7.6.3.css
HTTP/2 200
content-type: text/css
content-length: 1568
date: Sat, 13 Dec 2025 02:11:57 GMT
last-modified: Fri, 12 Dec 2025 10:02:30 GMT
etag: "84710a7f8b71f7d6c0300b3f7bc56d41"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 08d9736cfbefba5242262ce2c5dfadea.cloudfront.net (CloudFront)
x-amz-cf-pop: KIX50-P2
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: KFDgOe_gDCW0jluzZq30Y9Z0F6o1AdvlT8yQFbKstvfLtdGlBzHXrA==
age: 22
是不是很簡單。
但是,每5分鐘同步一次,這樣看起來 highly inefficient,讓我們試一下inotify。
dnf install inotify-tools -y
創建一個監控目錄並在變動發生時進行同步的script:
sudo tee /usr/local/bin/aws-cli-sync-s3.sh << 'EOF'
#!/bin/bash
/usr/bin/inotifywait -m -r -e modify,create,delete /home/www/bbken.org/wp-content/uploads/ |
while read path action file; do
echo "Change detected: $action $file"
/usr/bin/aws s3 sync /home/www/bbken.org/wp-content/uploads/ s3://bbken.org/wp-content/uploads/ --delete
done
EOF
讓他可以execute:
chmod +x /usr/local/bin/aws-cli-sync-s3.sh
創建一個systemd 服務,這樣開機的時候就可以自己運行了,使用www 用戶,不要使用root,不要使用root,不要使用root:
sudo tee /etc/systemd/system/s3-sync.service << 'EOF'
[Unit]
Description=S3 Sync Service
After=network.target
[Service]
Type=simple
User=www
ExecStart=/usr/local/bin/aws-cli-sync-s3.sh
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
不要有任何遲疑,勇敢的啟動他:
sudo systemctl daemon-reload
sudo systemctl enable s3-sync.service
sudo systemctl start s3-sync.service
sudo systemctl status s3-sync.service
systemctl status s3-sync.service
● s3-sync.service - S3 Sync Service
Loaded: loaded (/etc/systemd/system/s3-sync.service; enabled; preset: disabled)
Active: active (running) since Sat 2025-12-13 09:23:26 TST; 1h 8min ago
Main PID: 375084 (aws-cli-sync-s3)
Tasks: 3 (limit: 2114)
Memory: 7.2M
CPU: 33.431s
CGroup: /system.slice/s3-sync.service
├─375084 /bin/bash /usr/local/bin/aws-cli-sync-s3.sh
├─375085 /usr/bin/inotifywait -m -r -e modify,create,delete /home/www/bbken.org/wp-content/uploads/
└─375086 /bin/bash /usr/local/bin/aws-cli-sync-s3.sh
測試寫一個文件到uploads:
cd /home/www/bbken.org/wp-content/uploads
mkdir 2049
cd 2049
tee blade.txt << 'EOF'
NetworkBandwidthInAllowanceExceeded
EOF
看一下此時的log,哎,真的有在同步:
journalctl -u s3-sync.service -f
Dec 13 09:29:55 hostname-of-server aws-cli-sync-s3.sh[375086]: Change detected: CREATE blade.txt
Dec 13 09:30:00 hostname-of-server aws-cli-sync-s3.sh[375676]: [196B blob data]
Dec 13 09:30:00 hostname-of-server aws-cli-sync-s3.sh[375676]: Completed 36 Bytes/~36 Bytes (1.2 KiB/s) with ~0 file(s) remaining (calculating...)
Dec 13 09:30:00 hostname-of-server aws-cli-sync-s3.sh[375086]: Change detected: MODIFY blade.txt
測試訪問一下,可以從S3 訪問到這個文件(server: AmazonS3),說明同步是成功的:
curl --head https://bbken.org/wp-content/uploads/2049/blade.txt
HTTP/2 200
content-type: text/plain
content-length: 36
date: Sat, 13 Dec 2025 01:30:29 GMT
last-modified: Sat, 13 Dec 2025 01:30:01 GMT
etag: "e97005bde85593141d27b0f606dab0c4"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 4290d8e21534487fb5d6b6aa6374cbe2.cloudfront.net (CloudFront)
x-amz-cf-pop: KIX50-P2
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: zzvmzEkPaSnOJHmuwK9ANKABP3JK38wcvPnBALKlCWy1pVxZjq3CoQ==
age: 26
看看刪除會不會同步:
rm -rf 2049
此時的log:
Dec 13 09:33:06 hostname-of-server aws-cli-sync-s3.sh[375086]: Change detected: DELETE blade.txt
Dec 13 09:33:10 hostname-of-server aws-cli-sync-s3.sh[375988]: delete: s3://bbken.org/wp-content/uploads/2049/blade.txt
Dec 13 09:33:10 hostname-of-server aws-cli-sync-s3.sh[375988]: [139B blob data]
Dec 13 09:33:10 hostname-of-server aws-cli-sync-s3.sh[375988]: Completed 0 Bytes/~72 Bytes (0 Bytes/s) with ~0 file(s) remaining (calculating...)
Dec 13 09:33:10 hostname-of-server aws-cli-sync-s3.sh[375086]: Change detected: DELETE blade.txt
Dec 13 09:33:15 hostname-of-server aws-cli-sync-s3.sh[375086]: Change detected: DELETE,ISDIR 2049
我去S3 看了,文件已經被同步刪除,但是!
CloudFront 仍然有緩存,這是個小bug,必須要手動清除緩存才行。
看嘛,是不是很簡單。
注意到CloudFront 已經啟用HTTP/3 了我的源站還在HTTP/1.1。
更新一下nginx好了。
全局配置文件nginx.conf 中 http 段落加入:
http {
http2 on;
http3 on;
vhosts.conf 中server 段落加入:
###bbken.org at 443####
server {
server_name bbken.org www.bbken.org;
listen 443 ssl ;
listen 443 quic reuseport;
listen [::]:443 ssl;
listen [::]:443 quic reuseport; # IPv6
add_header Alt-Svc 'h3=":443"; ma=86400';
重啟一下nginx -s reload。
檢查一下udp 監聽:
ss -ulnp | grep :443
UNCONN 0 0 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=281315,fd=28),("nginx",pid=281314,fd=28),("nginx",pid=240301,fd=28))
UNCONN 0 0 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=281315,fd=26),("nginx",pid=281314,fd=26),("nginx",pid=240301,fd=26))
UNCONN 0 0 [::]:443 [::]:* users:(("nginx",pid=281315,fd=27),("nginx",pid=281314,fd=27),("nginx",pid=240301,fd=27))
UNCONN 0 0 [::]:443 [::]:* users:(("nginx",pid=281315,fd=29),("nginx",pid=281314,fd=29),("nginx",pid=240301,fd=29))
測試一下header:
HTTP/2 200
server: nginx/1.28.0
date: Fri, 05 Dec 2025 08:03:12 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
vary: Accept-Encoding, Cookie
cache-control: max-age=3, must-revalidate
因為curl 不支持HTTP/3 所以等我想到怎麼測再說。
對於wp-login.php 和xmlrpc.php 的保護,是很重要的問題,不對他們加以保護將會導致大量的Bots 來訪問,雖然不會造成什麼安全問題(也不是完全沒有,有一些spam comments會突破Akismet 的限制,我不是很清楚為什麼,可能是因為API rate limit),但是會消耗大量的Memory 和CPU資源。
在CloudFlare 上可以直接通過rule 來設置若是訪問到這些url 返回圖形驗證或403,但是在CloudFront 這個功能通過Functions 來實現,是要付錢的,不過非常便宜,大概是為了防止濫用。
Invocation pricing is $0.10 per 1 million invocations ($0.0000001 per invocation).
Create Function,Build and Publish 到相應的 Distribution ID 就可以了,這個Function 長這樣,不要忘記Publish 喔。這裡的例子是當檢測到訪問這兩個URI 的IP地址不是來自於台灣的時候,就返回403 ,並且不緩存403 的返回,如果忘記最後一步,403被緩存後會導致所有訪問這兩個URI 的請求都被返回403。
function handler(event) {
var request = event.request;
var uri = request.uri;
// Check if accessing protected paths
if (uri === '/wp-login.php' || uri === '/xmlrpc.php') {
// Get country from CloudFront headers
var country = request.headers['cloudfront-viewer-country'];
if (!country || country.value !== 'TW') {
return {
statusCode: 403,
statusDescription: 'Forbidden',
headers: {
'cache-control': { value: 'no-cache, no-store, must-revalidate' },
'pragma': { value: 'no-cache' },
'expires': { value: '0' }
}
};
}
}
return request;
}
設定好之後,log 裡面就安靜多了,CloudFront 的Monitoring 裡面可以看到捕捉到很多壞人。

同時也可以看到4xx error 的紀錄穩定在相應的程度。




