前言
很开心之前写的Nginx反代frp成功实现https和泛域名/泛解析能够受到大家的欢迎,下面开始填之前说过的纯享版的坑
包含本地nginx和frpc的配置文件,远端nginx和frps的配置文件,可谓是懒人的福音~
最近忙于工程没有关注blog的情况,发现很多朋友在考虑如何不暴露端口,请注意,本文是为了容差性兼容不正确配置的后端http和https才会暴露端口的,如果你对你的后端nginx配正确性很有信心,则不需要劫持502响应码,但这样如果发生后端配置错误,将直接抛出502给用户,降低用户体验。请谨慎考虑!
为了方便大家偷懒,我已经把配置文件上传至Github,大家下载的时候麻烦给颗星星吧~
如需理解原理,请查找文章末尾的原理链接,如有问题,欢迎大家留言或者知乎私信我~
如实在太懒连粘贴都不爱粘且比较壕气,本人可以提供廉价帮助配置服务,详情请咨询知乎私信~
请务必看完服务器拓扑假设及相关说明再复制代码
关于后端混杂htpps和http还不想全部在frps端处理http到https还想隐藏端口的问题
这种奇怪要求当然我有考虑到 解决方案有两种:
1.为个别后端为http的站点建立单独一组配置文件,在frps所在的服务器的nginx上反代http到https
2.自动检测后端是否为只有http,这里可以直接调用frps提供的api,用lua批量处理
我个人目前使用后的是第二种,但无奈写blog的时间太少,相关方案的示例我会在下一次摸鱼时总结放出
服务器拓扑假设
如上图所示,假设本地有两台服务器,一台nginx服务器负责本地网站的承载下面称为LS1
,另一台负责运行frpc下面称为LS2
实际使用中这两台服务器可以合二为一
远端有一台frps服务器
本纯享代码可分别实现四种不同方案(电脑端可根据用途按右侧标题导航快速查找)
- 本地LS1正确配置http跳转到https(TLS),远端frps直接监听80 443端口
- 本地LS1正确配置http跳转到https(TLS),远端nginx反代frps转发的https实现泛解析和http跳转到https,frps监听其他端口(适用于想要正确传递https客户端真实IP,由于frpc到frps线路质量不佳需要缓存,frps服务器同时需要挂其他网站等特殊需求)
- 本地LS1只配置http,远端nginx反向代理frps转发的http到https,frps监听其他端口(是远端nginx反代的伪https,极不推荐,但如有本地特殊http需求或因为懒癌晚期本地确不想用https的请看本部分)
- 本地LS1只配置http,远端nginx反向代理frps转发的http到http,frps监听其他端口
方案一:本地LS1正确配置http跳转到https(TLS),远端frps直接监听80 443端口
本地LS1的nginx配置server部分(最简代码,不含安全配置和静态资源缓存配置部分,不含引入开启PHP的配置文件部分,不含日志文件配置,如需要请自行添加原有配置文件的location和include)
server
{
listen 80;
listen 443 ssl http2;
server_name abc.yourdomain.com; #这里写你网站的域名,注意别删掉结尾的分号
index index.php index.html index.htm default.php default.htm default.html;
root /www/wwwroot/default; #这里写你网站根目录的地址
real_ip_header X-Forwarded-For; #用于接收远端frps服务器上nginx传递的真实IP
real_ip_recursive on;
if ($server_port !~ 443{ #用于80端口自动跳转到HTTPS
rewrite ^(/.*)$ https://$host$1 permanent;
}
ssl_certificate /etc/fullchain.pem; #这里写你ssl证书公钥(完整链)的位置,记得要给nginx访问这个文件的权限
ssl_certificate_key /etc/privkey.pem; #这里写你ssl证书私钥的位置,记得要给nginx访问这个文件的权限
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
}
本地LS2的frpc配置文件
[common]
server_addr = 121.8.1.111 frps服务器地址
server_port = frps服务端口
log_file = ./frpc.log
log_level = info
log_max_days = 3
privilege_token = 你在frps配置文件设置的验证密钥
pool_count = 5
tcp_mux = true
user = 自己定义的用户名
login_fail_exit = true
protocol = tcp
[服务名-自己定义-http]
type = http
local_ip = 192.168.1.111待转发的本地服务器IP,本机写127.0.0.1
local_port = 80 (如其他端口则修改)
use_encryption = false
use_compression = true
custom_domains = 定义域名,如abc.domain.com
[服务名-自己定义-https]
type = https
local_ip = 192.168.1.111待转发的本地服务器IP,本机写127.0.0.1
local_port = 443 (如其他端口则修改)
use_encryption = false
use_compression = true
custom_domains = 定义域名,如abc.domain.com
远程frps服务器的frps配置文件
[common]
bind_addr = 121.8.1.111服务器地址
bind_port = frp的服务端口
bind_udp_port = udp端口需要另外写好
kcp_bind_port = 与frp的服务端口相同即可
vhost_http_port = 80
vhost_https_port = 443
dashboard_addr = 后台面板的地址(可写服务器地址)
dashboard_port = 后台面板的端口,不能为上面定义过的端口
dashboard_user = 后台面板的用户名
dashboard_pwd = 后台面板的密码
log_file = ./frps.log
log_level = info
log_max_days = 3
privilege_token = 验证密钥,自己设置
heartbeat_timeout = 90
privilege_allow_ports = 123-456,789 端口范围 自己设置
authentication_timeout = 900
tcp_mux = true
方案二:本地LS1正确配置http跳转到https(TLS),远端nginx反代frps转发的https实现泛解析和http跳转到https,frps监听其他端口(适用于想要正确传递https客户端真实IP,由于frpc到frps线路质量不佳需要缓存,frps服务器同时需要挂其他网站等特殊需求)
本地LS1的nginx配置server部分(最简代码,不含安全配置和静态资源缓存配置部分,不含引入开启PHP的配置文件部分,不含日志文件配置,如需要请自行添加原有配置文件的location和include)
server
{
listen 80;
listen 443 ssl http2;
server_name abc.yourdomain.com; #这里写你网站的域名,注意别删掉结尾的分号
index index.php index.html index.htm default.php default.htm default.html;
root /www/wwwroot/default; #这里写你网站根目录的地址
real_ip_header X-Forwarded-For; #用于接收远端frps服务器上nginx传递的真实IP
real_ip_recursive on;
if ($server_port !~ 443{ #用于80端口自动跳转到HTTPS
rewrite ^(/.*)$ https://$host$1 permanent;
}
ssl_certificate /etc/fullchain.pem; #这里写你ssl证书公钥(完整链)的位置,记得要给nginx访问这个文件的权限
ssl_certificate_key /etc/privkey.pem; #这里写你ssl证书私钥的位置,记得要给nginx访问这个文件的权限
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
}
本地LS2的frpc配置文件
[common]
server_addr = 121.8.1.111 frps服务器地址
server_port = frps服务端口
log_file = ./frpc.log
log_level = info
log_max_days = 3
privilege_token = 你在frps配置文件设置的验证密钥
pool_count = 5
tcp_mux = true
user = 自己定义的用户名
login_fail_exit = true
protocol = tcp
[服务名-自己定义-http]
type = http
local_ip = 192.168.1.111 待转发的本地服务器IP,本机写127.0.0.1
local_port = 80 (如其他端口则修改)
use_encryption = false
use_compression = true
custom_domains = 定义域名,如abc.domain.com
[服务名-自己定义-https]
type = https
local_ip = 192.168.1.111 待转发的本地服务器IP,本机写127.0.0.1
local_port = 443 (如其他端口则修改)
use_encryption = false
use_compression = true
custom_domains = 定义域名,如abc.domain.com
远程frps服务器的frps配置文件
[common]
bind_addr = 121.8.1.111服务器地址
bind_port = frp的服务端口
bind_udp_port = udp端口需要另外写好
kcp_bind_port = 与frp的服务端口相同即可
vhost_http_port = 1234 #选择任意非80端口即可
vhost_https_port = 5678 #选择任意非443端口即可
dashboard_addr = 后台面板的地址(可写服务器地址)
dashboard_port = 后台面板的端口,不能为上面定义过的端口
dashboard_user = 后台面板的用户名
dashboard_pwd = 后台面板的密码
log_file = ./frps.log
log_level = info
log_max_days = 3
privilege_token = 验证密钥,自己设置
heartbeat_timeout = 90
privilege_allow_ports = 123-456,789 端口范围 自己设置
authentication_timeout = 900
tcp_mux = true
远程frps服务器的nginx配置server部分(最简代码,不含安全配置和静态资源缓存配置部分,不含引入开启PHP的配置文件部分,不含日志文件配置,如需要请自行添加原有配置文件的location和include)
server
{
listen 80;
listen 443 ssl http2;
server_name *.yourdomain.com; #你的域名,记得确认已经将*的A记录解析到
charset utf-8;
if ($server_port !~ 443){ #http跳转到https
rewrite ^(/.*)$ https://$host$1 permanent;
}
ssl_certificate /etc/fullchain.pem; #这里写你ssl证书公钥(完整链)的位置,记得要给nginx访问这个文件的权限
ssl_certificate_key /etc/privkey.pem; #这里写你ssl证书私钥的位置,记得要给nginx访问这个文件的权限
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
location / {
resolver 8.8.8.8; #重要,依靠此行解析$host,也可更换其他DNS服务器
proxy_ssl_server_name on; #重要,反代后端https必要
proxy_set_header X-Real-IP $remote_addr; #将真实IP封在head中传递给后端
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
####################################################################################
#重要提示!!
#如果上一行这样写出现跳转到有端口号的网址,为后端跳转方式过于僵硬,建议修改后端网站程序
#若实在难以debug后端代码,则如此修改即可避免:proxy_set_header Host $host:$server_port;
####################################################################################
proxy_pass https://$host:5678; #5678为frps的https端口
error_page 502 http://$host:6666/$request_uri;
#遇到502错误(后端为http时)通过error_page跳转给下一个server处理
#6666端口定义为任意值,和下文对应初相等即可
}
}
server
{
listen 6666; #任意端口,用于接收上一个server502错误不能处理的请求
listen 7777 ssl http2; #任意端口但需要注意修改后下文的两个也需要修改
server_name _; #为避免server_name相同导致的错误,这里使用_接管其他配置文件未定义的host
charset utf-8;
if ($server_port !~ 7777){ #这里的7777要和上文保持一直
rewrite ^(/.*)$ https://$host:777$1 permanent; #这里的7777要和上文保持一致
}
ssl_certificate /etc/fullchain.pem; #这里写你ssl证书公钥(完整链)的位置,记得要给nginx访问这个文件的权限
ssl_certificate_key /etc/privkey.pem; #这里写你ssl证书私钥的位置,记得要给nginx访问这个文件的权限
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host:7777$request_uri;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_pass http://121.8.1.111:1234;
#注意,这里的121.8.1.111需要改成你frps服务器的IP,1234需改成你frps服务器的http监听端口
error_page 502 http://$host:1234$request_uri;
#如再次处理不了 则直接跳转给frps的http,1234需改成你frps服务器的http监听端口
}
}
方案三:本地LS1只配置http,远端nginx反向代理frps转发的http到https,frps监听其他端口(是远端nginx反代的伪https,极不推荐,但如有本地特殊http需求或因为懒癌晚期本地确不想用https的请看本部分)
本地LS1的nginx配置server部分(最简代码,不含安全配置和静态资源缓存配置部分,不含引入开启PHP的配置文件部分,不含日志文件配置,如需要请自行添加原有配置文件的location和include)
server
{
listen 80;
server_name abc.yourdomain.com; #这里写你网站的域名,注意别删掉结尾的分号
index index.php index.html index.htm default.php default.htm default.html;
root /www/wwwroot/default; #这里写你网站根目录的地址
real_ip_header X-Forwarded-For; #用于接收远端frps服务器上nginx传递的真实IP
real_ip_recursive on;
}
本地LS2的frpc配置文件
[common]
server_addr = 121.8.1.111 frps服务器地址
server_port = frps服务端口
log_file = ./frpc.log
log_level = info
log_max_days = 3
privilege_token = 你在frps配置文件设置的验证密钥
pool_count = 5
tcp_mux = true
user = 自己定义的用户名
login_fail_exit = true
protocol = tcp
[服务名-自己定义-http]
type = http
local_ip = 192.168.1.111 待转发的本地服务器IP,本机写127.0.0.1
local_port = 80 (如其他端口则修改)
use_encryption = false
use_compression = true
custom_domains = 定义域名,如abc.domain.com
远程frps服务器的frps配置文件
[common]
bind_addr = 121.8.1.111服务器地址
bind_port = frp的服务端口
bind_udp_port = udp端口需要另外写好
kcp_bind_port = 与frp的服务端口相同即可
vhost_http_port = 1234 #选择任意非80端口即可
vhost_https_port = 5678 #选择任意非443端口即可
dashboard_addr = 后台面板的地址(可写服务器地址)
dashboard_port = 后台面板的端口,不能为上面定义过的端口
dashboard_user = 后台面板的用户名
dashboard_pwd = 后台面板的密码
log_file = ./frps.log
log_level = info
log_max_days = 3
privilege_token = 验证密钥,自己设置
heartbeat_timeout = 90
privilege_allow_ports = 123-456,789 端口范围 自己设置
authentication_timeout = 900
tcp_mux = true
远程frps服务器的nginx配置server部分(最简代码,不含安全配置和静态资源缓存配置部分,不含引入开启PHP的配置文件部分,不含日志文件配置,如需要请自行添加原有配置文件的location和include)
server
{
listen 80;
listen 443 ssl http2;
server_name *.yourdomain.com; #你的域名,记得确认已经将*的A记录解析到
charset utf-8;
if ($server_port !~ 443){ #http跳转到https
rewrite ^(/.*)$ https://$host$1 permanent;
}
ssl_certificate /etc/fullchain.pem; #这里写你ssl证书公钥(完整链)的位置,记得要给nginx访问这个文件的权限
ssl_certificate_key /etc/privkey.pem; #这里写你ssl证书私钥的位置,记得要给nginx访问这个文件的权限
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
location / {
proxy_set_header X-Real-IP $remote_addr; #将真实IP封在head中传递给后端
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
####################################################################################
#重要提示!!
#如果上一行这样写出现跳转到有端口号的网址,为后端跳转方式过于僵硬,建议修改后端网站程序
#若实在难以debug后端代码,则如此修改即可避免:proxy_set_header Host $host:$server_port;
####################################################################################
proxy_pass http://121.8.1.111:1234;
#注意,这里的121.8.1.111需要改成你frps服务器的IP,1234需改成你frps服务器的http监听端口
error_page 502 http://$host:1234$request_uri;
#如处理不了 则直接跳转给frps的http,1234需改成你frps服务器的http监听端口
}
}
方案四:本地LS1只配置http,远端nginx反向代理frps转发的http到http,frps监听其他端口
本地LS1的nginx配置server部分(最简代码,不含安全配置和静态资源缓存配置部分,不含引入开启PHP的配置文件部分,不含日志文件配置,如需要请自行添加原有配置文件的location和include)
server
{
listen 80;
server_name abc.yourdomain.com; #这里写你网站的域名,注意别删掉结尾的分号
index index.php index.html index.htm default.php default.htm default.html;
root /www/wwwroot/default; #这里写你网站根目录的地址
real_ip_header X-Forwarded-For; #用于接收远端frps服务器上nginx传递的真实IP
real_ip_recursive on;
}
本地LS2的frpc配置文件
[common]
server_addr = 121.8.1.111 frps服务器地址
server_port = frps服务端口
log_file = ./frpc.log
log_level = info
log_max_days = 3
privilege_token = 你在frps配置文件设置的验证密钥
pool_count = 5
tcp_mux = true
user = 自己定义的用户名
login_fail_exit = true
protocol = tcp
[服务名-自己定义-http]
type = http
local_ip = 192.168.1.111 待转发的本地服务器IP,本机写127.0.0.1
local_port = 80 (如其他端口则修改)
use_encryption = false
use_compression = true
custom_domains = 定义域名,如abc.domain.com
远程frps服务器的frps配置文件
[common]
bind_addr = 121.8.1.111服务器地址
bind_port = frp的服务端口
bind_udp_port = udp端口需要另外写好
kcp_bind_port = 与frp的服务端口相同即可
vhost_http_port = 1234 #选择任意非80端口即可
vhost_https_port = 5678 #选择任意非443端口即可
dashboard_addr = 后台面板的地址(可写服务器地址)
dashboard_port = 后台面板的端口,不能为上面定义过的端口
dashboard_user = 后台面板的用户名
dashboard_pwd = 后台面板的密码
log_file = ./frps.log
log_level = info
log_max_days = 3
privilege_token = 验证密钥,自己设置
heartbeat_timeout = 90
privilege_allow_ports = 123-456,789 端口范围 自己设置
authentication_timeout = 900
tcp_mux = true
远程frps服务器的nginx配置server部分(最简代码,不含安全配置和静态资源缓存配置部分,不含引入开启PHP的配置文件部分,不含日志文件配置,如需要请自行添加原有配置文件的location和include)
server
{
listen 80;
server_name *.yourdomain.com; #你的域名,记得确认已经将*的A记录解析到
charset utf-8;
location / {
proxy_set_header X-Real-IP $remote_addr; #将真实IP封在head中传递给后端
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
####################################################################################
#重要提示!!
#如果上一行这样写出现跳转到有端口号的网址,为后端跳转方式过于僵硬,建议修改后端网站程序
#若实在难以debug后端代码,则如此修改即可避免:proxy_set_header Host $host:$server_port;
####################################################################################
proxy_pass http://121.8.1.111:1234;
#注意,这里的121.8.1.111需要改成你frps服务器的IP,1234需改成你frps服务器的http监听端口
error_page 502 http://$host:1234$request_uri;
#如处理不了 则直接跳转给frps的http,1234需改成你frps服务器的http监听端口
}
}
你好,博主,我用的方案一,浏览器成功显示证书,但是网页显示403 Forbidden,本地访问页面正常,不知道是哪的原因能帮我看看吗?QQ87868382
謝謝大神, 我用case03已經成功實現FRP>HTTPS, 請問大神"是远端nginx反代的伪https", 請不是真正的HTTPS加密碼?
CASE03中是在远端服务器(frps所在服务器)使用Nginx反向代理将HTTP转换为HTTPS。对用户来讲,用户到远端服务器使用的是真实的HTTPS协议,但后端服务器监听的,和frps转发的均为HTTP协议,如果被直接访问到你frps所在服务器的http端口,还是会使用http协议,并不是全程https,对你的后端服务而言也感受不到https的存在。所以称作伪https
Raymond大佬, 我用你的case03的代碼, 的確可以免端口輸入就可以連線frpc,但網址還是會顯示端口. 而且我已加入證書. 但沒有跳轉到HTTPS, 請問這是正常嗎? 求解惑. 萬分感謝!
我觉得方案3最方便,为啥不推荐?有什么坏处吗?
奇怪我用了第二个方法,浏览本来就是https的正常,http跳转到6666端口就直接显示6666端口号然后网页无法访问。请问博主如何解决。
如果本地https正确配置的话,http2是可以自动识别没有跳转的,跳转了说明实际本地https配置无效,这时远端nginx反代的是http端口,如果本地用了强制跳转的话就会访问不了。应该检查本地的https是否正常访问、远端frps的https是否可以正常访问、证书是否有错误。有其他问题可以知乎私信我。
方案2确实能实现无缝的切换,但是有个小瑕疵,就是访问后域名会带上端口号,经过测试,是个别域名会带,有些域名又不会带,不知道什么情况,您提供的两种nginx方案都已经试过:
proxy_set_header Host $host;
proxy_set_header Host $host:$server_port;
真的太感谢了,我一直在找没想到就前几天你更了这个!!!
还有,捕捉大佬一个笔误!方案二(就是我用的方案)里“远程frps服务器的nginx配置server部分”,第二块“server”里第二个7777端口写成了777
嘿嘿
有没有本地不搭建NGINX的方案啊!? 我觉得应该可以实现!
本地可以搭建其他服务,只要frpc监听对应端口就可以了。
不想在内网再搭建NGINX的服务!
我现在用的方案二 但是有个问题 如果是http的服务 转到了 6666 改成 7777的https 转过去 但是端口没有隐藏!!
我现在用的折中的方法 就是 https的直接转过去 http的直接已http继续访问!
不知道有没有更好的方法!!!!
解决方案有两种

1.为个别后端为http的站点建立单独一组配置文件,在frps所在的服务器的nginx上反代http到https
2.自动检测后端是否为只有http,这里可以直接调用frps提供的api,用lua批量处理
我个人目前使用后的是第二种,但无奈写blog的时间太少,相关方案我会在下一次摸鱼时放出
确实是这样,期待能避免该瑕疵的解决方案