通过ssh tunnel和nginx从外网访问本地服务

在某些情况下, 需要让外网能够访问本地正在开发的网站, 例如做微信开发,微信API接受消息的url必须是外网可访问的地址,我们不可能每次修改代码再发布到外网服务器进行调试,通常可以通过ngrok来实现,使用ngrok就不需要拥有自己的外网服务器,但ngrok有时候还是有点慢, 而且每次地址都会变,当然也有国内的类似服务如natapp, natapp免费版本有各种限制,想想也不好用,虽然我也没用过。 但如果有一台外网服务器, 那么通过ssh tunnel和nginx就可以轻松实现从外网访问local的服务,这样就不用每次都发布到外网服务。下面将讲解如何实现。

前提条件:有一台外网可以访问的服务器

基本思路: 通过ssh建立一个通道,把远程外网机器上的一个端口和本地的端口建立起一个连接,远程机器机器上的那个端口的访问请求通过ssh tunnel转到本地机器的端口,而本地机器的这个端口也就是你web服务的端口,例如apache/tomcat/nginx的80或8080端口,这样就使得本地机器可在外网可以访问了

例如你的外网机器可以通过myhost.com访问(或者直接使用ip也可以),在本地机器运行如下命令:

ssh -nNT -R 8000:localhost:80 user@myhost.com
上面的命令就建立起了外网机器8000端口和本地机器80端口的通道,当访问外网机器8000端口时, 请求会被ssl tunnel转到本地机器的80端口,这样就已经实现了我们的目的,但是…
微信官方明确要求:
微信公众号接口只支持80接口
不知道为什么腾讯要做这个限制,但我们肯定不愿意一台机器唯一的宝贵80端口只用来做微信开发,好在nginx可以解决这个问题,配置如下:
server {
    listen 80;
    server_name myhost.com www.myhost.com;
    root /var/www;

    location /wechat/ {
        rewrite /wechat/(.*) /$1 break;
        proxy_pass http://127.0.0.1:8000;
        error_log /var/www/logs/wechat_error.log;
        access_log /var/www/logs/wechat_access.log;
    }
}
这样80端口还是可以用于其它的网站,url包含/wechat/的请求会被转到8000端口, 然后再转到本地机器的80端口, 在微信开发平台就可以大致设置url为http://myhost.com/wechat/receive, 相对应的本地localhost/receive用于接受微信的消息。
使用ssh tunnel和nginx相比于ngrok的好处有:
1. 如果是国内的服务器, 速度应该比ngrok快
2. 地址固定, 不会像ngrok那样每次都变,这样就省去每次都去微信开发平台设置url的时间,而且如果使用ngrok服务的话,通常需要点击好几次才能保存成功
补充:
上面的方法对微信消息的收发是没有问题的, 但对于微信网页开发就有一些问题了, 因为涉及到认证、url跳转, 静态资源(js/css/image)的访问,下面就这些问题提出一个更好的方案,那就是专门为微信开发新建一个二级域名,例如:wechat.myhost.com, 新建 nginx server如下:
server {
    listen 80;
    listen 443 ssl;
    ssl_certificate /usr/local/nginx/ssl/nginx.crt;
    ssl_certificate_key /usr/local/nginx/ssl/nginx.key;
    server_name wechat.myhost.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
    }
    error_log /mnt/www/logs/wechat_error.log;
    access_log /mnt/www/logs/wechat_access.log;
}
当然也得把那个二级域名加到本地机器的server name 里
server {
    listen 80;
    server_name wechat.dev wechat.myhost.com;
    ...