原则上,本地开发调试环境和线上生产环境的差异越小越好。由于目前大多数站点(无论是直接供用户访问的网站还是提供后端 API 的站点)使用的都是 HTTPS,所以在本地开发调试环境也使用 HTTPS 很有必要。
它不仅有助于尽早发现和 HTTPS 相关的一些问题(比如代码中不小心请求了 HTTP 资源,由于本地调试阶段使用 HTTP 协议,所以没有发现,上线后因为混合内容被浏览器阻塞,网站显示异常甚至不能正常工作),也有助于避免一些客户端的限制(比如通过局域网访问站点,测试站点的 service worker 功能)。
其实在本地配置 HTTPS 非常快捷方便,有现成的成熟工具可以使用。大致分两步:
- 生成证书。
- 修改项目的启动服务。
生成证书
我们将使用 [mkcert] 这个零配置的命令行工具生成证书。
首先安装 mkcert。macOS 下可以使用 Homebrew 安装,其他系统请参考 mkcert 的文档:
brew install mkcert
brew install nss
其中,nss
是可选的,如果不使用或者不需要测试 Firefox,那么可以不安装 nss
。
接着我们创建一个目录来存放证书,比如 ~/.cert
:
mkdir -p ~/.cert
自动生成证书:
mkcert -key-file ~/.cert/key.pem -cert-file ~/.cert/cert.pem "localhost"
让系统信任生成的证书:
mkcert -install
因为需要在系统中安装本地 root CA,所以运行上述命令会请求 sudo 权限。只有初次生成证书时需要运行这个命令,后续通过 mkcert -key-file
生成的证书会自动被系统信任。
修改项目的启动服务
许多框架都提供了几乎是开箱即用的 HTTPS 支持,比如在启动命令时加上相应的命令行参数或是环境变量,或者在启动服务的代码中进行简单的配置。下面就以前端开发比较流行的[http-server]、React、[Express] 为例(三者正好对应了前述三种方式),介绍如何在本地开发调试时启动 HTTPS 服务。
http-server
纯静态项目可以通过 [http-server] 进行调试,要切换至 HTTPS 模式,只需加上三个命令行参数:
npx http-server -S -C ~/.cert/cert.pem -K ~/.cert/key.pem
React
通过 Create React App 创建的 React 项目,只需在启动服务时指定几个环境变量就可以切换至 HTTPS 模式:
npx create-react-app example
cd example
HTTPS=true SSL_CRT_FILE=$HOME/.cert/cert.pem SSL_KEY_FILE=$HOME/.cert/key.pem npm start
为了省去每次启动都需要在前面加上这几个环境变量的麻烦,可以直接修改 package.json
的 scripts.start
项:
"start": "HTTPS=true SSL_CRT_FILE=$HOME/.cert/cert.pem SSL_KEY_FILE=$HOME/.cert/key.pem react-scripts start",
这样,以后只需运行 npm start
即可启动 HTTPS 服务。
Express
相比上述两个项目,[Express] 稍微麻烦一点点,需要简单修改启动服务代码。
首先我们创建项目目录并安装 expressjs:
mkdir example
cd example
npm init -y
npm i express
然后编写以下简单的启动 HTTPS 服务的示例代码:
const express = require('express')
const app = express()
const https = require('https')
const fs = require('fs')
const os = require('os')
const path = require('path')
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
https.createServer({
key: fs.readFileSync(path.join(os.homedir(), '.cert/key.pem')),
cert: fs.readFileSync(path.join(os.homedir(), '.cert/cert.pem'))
}, app).listen(port, () => {
console.log(`Example app listening at https://localhost:${port}`)
})
可以看到,和 Express 官网上的 Hello World 示例代码相比,我们另外引入了 https 包,并通过 createServer
方法创建了 https 服务,并指定了证书路径为之前生成的本地证书。
反向代理
线上生产环境常见的一种架构是在 HTTP 服务前加一层反向代理,HTTPS 加解密在反向代理层面完成。如果项目的生产环境使用这一架构,那么为了最大限度地与生产环境保持一致,本地开发调试时也可以采取类似的架构,比如使用 [Caddy] 充当反向代理服务器。
macOS 下 Caddy 可以通过 Homebrew 安装,其他系统请参考 Caddy 文档:
brew install caddy
正常启动 HTTP 服务后,只需一行命令即可加上反向代理(这里假定服务的端口是 3000):
caddy reverse-proxy --from localhost --to localhost:3000
通过 https://localhost 即可访问服务:
Caddy 会自动生成证书,获取系统信任,无需另行生成证书,也无需修改项目的启动服务。
结语
为了保证本地开发调试环境和线上生产环境尽可能一致,在本地也使用 HTTPS 很有必要。这里介绍了两种在本地开发调试阶段使用 HTTPS 的方法,实施起来都非常快捷方便,可以根据项目的具体情况和个人偏好选用。当然,除了在这两种解决问题的方案中选择,也可以选择直接消灭问题,在线上环境进行调试。许多 serverless 云服务都提供线上调试功能,比如 LeanCloud 的云引擎提供和生产环境几乎完全一样的预备环境,可以自动获取项目 git 仓库的更新并部署到预备环境,调试完成、测试通过后可以一键发布至生产环境,免去在本地搭建环境、配置工具的麻烦。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。