掛載 DNS、HTTPS 到遠端伺服器
緣起
原先以為把 DNS 掛到後端上跟前端一樣簡單,設定好 DNS 記錄就可以輕鬆地轉過去,後來發現事情遠遠沒有這麼單純 QQ,在伺服器端需要設定的東西比網頁本身的內容還多,因此把這個過程憑藉微薄的印象寫下,希望下次不要再卡這麼久了...
HTTPS
因為前端放到了 Vercel 上再搭配 Cloudflare 的 SSL/TLS 服務掛上 https,所以後端也必須要是 https 協定才可以順利的把 API 打到前面去。在平台的選擇上,使用 Cloudflare 所提供的網域註冊、DNS 紀錄等功能,可以按照平台上的指示去操作 ( 選擇在 Cloudflare 購買網域一方面是覺得不需要另外再將網域從其他平台託管到 Cloudflare 上比較方便 )。
生成密鑰
購買網域並接通後 Cloudflare 會提供免費的 SSL/TLS 鎖頭,若想要將其綁定到後端伺服器上,需要在 SSL/TLS > 原始伺服器 中產生一組 certificate 和 private key,把密碼複製到新檔案中並儲存成 .pem 的格式。
後端程式碼嵌入
在生成密鑰後需要把它們放到後端當中進行認證,放的方法有很多種,可以用 SFTP 或是 FileZilla 的方式把檔案送過去,但就是不要直接 git 上雲端,就跟不要把密碼相關的東西上傳是相同的道理~
接下來是在後端的程式碼當中引入密鑰,以 Golang 為例:
func main() { server := gin.Default() // ...其餘程式 certFile := "/path/to/certificate.pem" keyFile := "/path/to/private-key.pem" server.RunTLS(":"+global.CONFIG.Server.Port, certFile, keyFile) }
經過這樣的設定,後端 server 就可以在 https 的協定下運行了,可以拿 https://localhost:<port> 來測試看看有沒有順利接通。
那討厭的 CORS
在解 CORS 的這段過程可說是十分坎坷,原先以為只要把前後端的路徑弄成同源就可以收工了,但是嘗試了很久還是瘋狂收到 CORS 的無情洗臉,最後把後端的 CORS 設定也好好整理了一遍最後才順利完工,只能說調校伺服器的難度確實是高很多,但經過這一番操作對它也有更深的認識了。
伺服器端設定
最後一步就是要把新的網域也註冊到後端伺服器當中,首先要確認自己使用的 web 伺服器是哪一款,以我為例用的是 Apache,在寫一些設定檔的時候就需要找到對應的寫法來寫。
啟用模組
首先我們需要先啟用一些在後續設定檔建立的過程中,需要用到的模組。我們必須要事先將這些模組開啟,才不會在執行我們的設定檔時產生錯誤。
$ sudo a2enmod proxy $ sudo a2enmod proxy-http $ sudo a2enmod ssl $ sudo a2enmod headers
建立 .conf
再來要在 Apache 的設定檔路徑底下 ( 通常是 /etc/apache2/sites-available ) 新增一個 xxx.conf 的檔案,用來建立 VirtualHost 將網域指向給 localhost 裡對應的 port,而裡面的內容大致為:
<VirtualHost *:443> ServerName <domain> # 這邊放入你的網域名稱 SSLProxyEngine On SSLProxyVerify None SSLProxyCheckPeerCN Off SSLProxyCheckPeerName Off SSLCertificateFile /path/to/certificate.pem SSLCertificateKeyFile /path/to/private-key.pem # 剛剛取得的密鑰檔案 ProxyPreserveHost On ProxyPass / https://localhost:<port>/ ProxyPassReverse / https://localhost:<port>/ # 設定要指向的 port ErrorLog ${APACHE_LOG_DIR}/xxx-error.log CustomLog ${APACHE_LOG_DIR}/xxx-access.log combined # log 檔的位址,可以去對應的目錄底下查看 log 紀錄 </VirtualHost>
設定完成後,需要啟用這個設定檔並重新啟動 Apache:
$ sudo a2ensite xxx.conf $ sudo systemctl restart apache2
剛剛提到的 log 可以到 /var/log/apache2 或類似的路徑底下查看。另外如果你的伺服器有防火牆的設定,也要記得去把 https 443 這個 port 打開。
在啟用的過程中也有遇到另一個小問題是系統文件的權限限制,因為在這個檔案裡調用了不同路徑底下的密鑰檔案,被系統權限攔下來了,所以額外調整了密鑰檔案的存取權限
$ chmod 644 /path/to/xxx.pem
後端設定 CORS 限制
最後一步是要回到我們的後端加上 CORS 的一些限制,這邊我選擇在 router 的區塊底下放入這些設定:
corsConfig := cors.DefaultConfig() corsConfig.AllowOrigins = []string{"https://your.domain"} corsConfig.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"} corsConfig.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization"} router.Use(cors.New(corsConfig)) // 如果是 PUT、DELETE 這種複雜請求,在發送前會先丟一個 OPTIONS 類型的請求 // 來確認是否可以順利發送,因此需要針對 OPTIONS 的回應做處置 router.OPTIONS("/*cors", func(c *gin.Context) { c.Header("Access-Control-Allow-Origin", "https://your.domain") c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Authorization") c.Header("Access-Control-Allow-Credentials", "true") c.AbortWithStatus(204) })
收工...?
基本上走到這一步應該就算是大功告成了,不過由於當初的過程來來回回實在是場面混亂,所以也難保會有其他意外的錯誤產生,這時候就是考驗 debug 的功力了 ( ? ),總之就是需要根據當下遇到的情境試著推敲可能產生問題的來源,也可以借助各大 GenAI 的力量可以更快、更有機會找到癥結點然後一一斬除~