服务器程序在3000端口提供服务,没有向外暴露;Apache 2绑定80端口,向外提供服务
Apache 2把接收到的请求转发给真正的服务器程序处理,这就是反向代理
反向代理配置
<VirtualHost *:80>
ServerAdmin webmaster@localhost
# 这个路径应该不需要配置吧
DocumentRoot /var/www/html
# 反向代理配置
ProxyPreserveHost On
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
https://github.com/GZhonghui/Web101/tree/master/04_https
// 需要开启信任代理
// 当使用反向代理时,实际的客户端 IP 地址会被代理服务器(Apache2)的 IP 地址替代
// 设置 trust proxy 后,Express 会信任代理服务器传递的 HTTP 头(如 X-Forwarded-For, X-Forwarded-Proto 等)
// 从而可以获取到真实的客户端 IP 地址
app.set('trust proxy', true);
...
// 绑定地址和端口的时候,使用 localhost:<非常用端口> 即可
const PORT = 3001;
app.listen(PORT, 'localhost', () => {
console.log(`server is running on http://localhost:${PORT}`);
});
...
// 其他代码保持不变即可
在后台启动 node 服务器
启用 Apache 所需模块
# 启用 SSL 和代理模块 sudo a2enmod ssl sudo a2enmod proxy sudo a2enmod proxy_http sudo a2enmod headers sudo a2enmod remoteip sudo service apache2 reload
开启 HTTP 服务 并绑定域名
<VirtualHost *:80>
ServerName domain.com # 绑定域名
ServerAdmin webmaster@localhost
# 反向代理配置
ProxyPreserveHost On
ProxyPass / http://localhost:3001/
ProxyPassReverse / http://localhost:3001/
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
# 配置好之后重启 Apache
sudo service apache2 restart
现在应该可以从外部使用域名访问到 node 服务器了 如果不行 请等待 DNS 解析或者清空浏览器缓存、使用无痕模式尝试
安装 acme.sh 并申请证书 这里我们使用非 root 身份(但是有 sudo 权限)
# 安装 acme.sh cd curl https://get.acme.sh | sh -s email=my@example.com source ~/.bashrc acme.sh --help # 申请证书 # 域名是我们在 Apache 配置中绑定的域名 # 网站根目录是我们反向代理服务器的 public 目录 acme.sh --issue -d domain.com -w /home/ubuntu/Web101/04_https/public/ # 安装证书 acme.sh --install-cert -d domain.com \ --cert-file /home/ubuntu/Web101/04_https/cert/cert.pem \ --key-file /home/ubuntu/Web101/04_https/cert/key.pem \ --fullchain-file /home/ubuntu/Web101/04_https/cert/fullchain.pem \ --reloadcmd "sudo service apache2 force-reload" # 因为不是 root 用户 所以需要使用 sudo 权限
完成之后 在 Apache 配置中启用 HTTPS 服务
# HTTP 虚拟主机配置 (/etc/apache2/sites-available/your-site.conf)
<VirtualHost *:80>
ServerName domain.com
ServerAdmin webmaster@localhost
# 重定向所有 HTTP 流量到 HTTPS
# 直接重定向到首页 这种做法比较简单
Redirect permanent / https://domain.com/
</VirtualHost>
<VirtualHost *:443>
ServerName domain.com
# 启用 SSL
# fullchain.pem 是证书链文件 如果需要的话就加上
SSLEngine on
SSLCertificateFile /home/ubuntu/Web101/04_https/cert/cert.pem
SSLCertificateKeyFile /home/ubuntu/Web101/04_https/cert/key.pem
SSLCertificateChainFile /home/ubuntu/Web101/04_https/cert/fullchain.pem
# 启用代理
ProxyPreserveHost On
# 添加请求头转发
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-SSL "on"
# 转发真实的客户端IP
ProxyAddHeaders On
RemoteIPHeader X-Forwarded-For
# 反向代理配置
ProxyPass / http://localhost:3001/
ProxyPassReverse / http://localhost:3001/
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
# 配置好之后重启 Apache
# 如果 Apache 重启失败 可能是部分模块没有启用 请检查 /etc/apache2/mods-enabled
sudo service apache2 restart
最后 检查一下 acme.sh 的 list,另外因为开启了 HTTPS 所以也要检查一下服务器的 443 端口是否开启。一切正常之后 就可以使用域名访问到 node 服务器了
如果服务程序是我们自己运行的,还算能找到真正的 public_path / web_root 在哪里,但是也有很多情况我们不知道这个目录在哪里,所以最好不要依赖与用这个目录验证证书
以下,我们在服务器上运行一个服务程序(以Alist为例),考虑使用Apache 2进行反向代理,并开启HTTPS
全程使用 root 身份
1 开启 Alist 服务,在 http://localhost:xxxx/ 提供服务,其余信息不需要
2 安装 acme.sh
3 启用 Apache 2 的模块(rewrite, alias, ssl, proxy, proxy_http, headers, remoteip 使用 a2enmod 命令)
4 配置 Apache 2 的HTTP
<VirtualHost *:80>
ServerName yourdomain.com
# 让 ACME 验证的请求能访问本地某个目录 (如 /var/www/certroot)
# 注意要提前创建好 /var/www/certroot 这个目录 我们用于验证证书
DocumentRoot /var/www/certroot
<Directory /var/www/certroot>
Require all granted
</Directory>
# 如果访问的路径是 .well-known/acme-challenge 下的文件,就让它直接访问本地
# 否则全部重定向到 HTTPS
RewriteEngine On
# 如果 URL 不匹配 .well-known/acme-challenge/,则跳转到 HTTPS
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
RewriteRule ^/(.*) https://yourdomain.com/$1 [R=301,L]
</VirtualHost>
5 重启服务器,我们验证配置,做申请证书前的准备
# 创建一个验证文件 root@NY-1:/var/www/certroot/.well-known/acme-challenge# pwd /var/www/certroot/.well-known/acme-challenge root@NY-1:/var/www/certroot/.well-known/acme-challenge# ls test.txt # 尝试使用 HTTP 访问 能访问到则成功 http://yourdomain.com/.well-known/acme-challenge/test.txt # 测试完之后记得把 test.txt 删了
6 申请证书(使用最方便的web root模式,开启HTTP服务也是为了能使用这个模式)
acme.sh --issue -d yourdomain.com -w /var/www/certroot
7 安装证书
acme.sh --install-cert -d yourdomain.com \ --cert-file /root/cert/yourdomain.com/cert.pem \ --key-file /root/cert/yourdomain.com/key.pem \ --fullchain-file /root/cert/yourdomain.com/fullchain.pem \ --reloadcmd "service apache2 force-reload"
8 有证书之后,我们补充 Apache 2 的 HTTPS 配置
<VirtualHost *:443>
ServerName yourdomain.com
# 启用 SSL
# fullchain.pem 是证书链文件 如果需要的话就加上
SSLEngine on
SSLCertificateFile /root/cert/yourdomain.com/cert.pem
SSLCertificateKeyFile /root/cert/yourdomain.com/key.pem
SSLCertificateChainFile /root/cert/yourdomain.com/fullchain.pem
# 启用代理
ProxyPreserveHost On
# 用于禁止正向代理,一般在做反向代理时为了安全都会配置
ProxyRequests Off
# 允许请求 URL 里带 %2F,并且不把它解码成 /,让后端自己解析 (Alist 要求)
AllowEncodedSlashes NoDecode
# 添加请求头转发
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-SSL "on"
# 转发真实的客户端IP
ProxyAddHeaders On
RemoteIPHeader X-Forwarded-For
# 反向代理配置
# nocanon 表示:禁止 Apache 对代理请求的路径做额外的“规范化”或“编码处理”
# 把原始请求尽可能忠实地传给后端 (Alist 要求)
ProxyPass "/" "http://127.0.0.1:5244/" nocanon
ProxyPassReverse "/" "http://127.0.0.1:5244/" nocanon
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
基础知识:HTTP(S)协议
# 需求模块:mod_proxy
# 将客户端请求中的 Host 头原封不动地转发给后端服务器
# 反向代理的时候 都建议开启
ProxyPreserveHost On
# 需求模块:mod_headers
# 为请求头中的一个Key设定Value 可以设置任何想要的自定义头部
RequestHeader set X-Real-IP %{REMOTE_ADDR}s
RequestHeader set X-Forwarded-For %{REMOTE_ADDR}s
# 请求头中的"KV对"可以在后端代码中这样读取(Go)
r.Header.Get("My-Custom-Token")