标签归档:Nginx

Nginx反代cloudflare加速网站访问

这篇文章不涉及重写url的部分,即反代使用的域名和源站使用的域名相同,只有国内会解析到反代服务器上,国外访问网站依旧直接走cloudflare->源站。dnspod使得我们可以使用相同的域名分别连接反代服务器与cloudflare服务器,所以不会有下一篇文章中的host不匹配问题。

前言

最近看见一台配置相当不错的欧洲服务器,于是买了下来并把网站迁移了上去,没想到配置确实是不错,但是到大陆的网络连接就一言难尽了,四处绕路、环球旅行都是常见的情况,并且丢包率相当惊人。不过配合cloudflare自选节点还是勉强可以一用的,甚至某些地区的访问还挺不错。

但是cloudflare自选节点只能指定一个大概的方向,可能一个ip对于某些地方的运营商是不错的线路,但是到了另外一些地方,依旧是绕路的,并且到了晚上依旧十分爆炸。这两天,校园网突然变成了移动出口,我算是体验到了移动用户的痛苦,到了晚上,虽然经过自选节点网站走的是香港cloudflare节点,但是打开速度依旧缓慢,有时候光是握手都要花费很长的时间,忍无可忍!于是我想着有没有什么办法能够加速一下大陆地区的访问呢?经过挂上香港的代理测试,香港打开这个网站的速度还是挺快的,说明源站在欧洲并不是什么大问题,主要还是cloudflare到大陆太堵了。于是我想着能不能通过反向代理的方式来加速一下大陆地区的访问呢?这样,用一个亚太地区的低配置(高配置机器太贵了,所以买了欧洲的)好线路的vps就可以极大的提升大陆地区用户访问我的网站的体验了。 由于最近太穷了,也找不到什么好线路的机器,所以我暂时先使用了吃灰中的谷歌云。

贴一张图,看看反代后的效果(不过gcp最近挺炸的,真要用的话还是建议找一个三网cn2的香港机器来反代):

思路介绍

先大致说一下我的操作思路,使用dnspod智能解析,境外依旧走cloudflare的cdn,而对于大陆地区则解析到香港的谷歌云上,谷歌云再反向代理cloudflare的ip,并带上主机名(host)即可(当然也可以直接反向代理到源站)。

实际操作

默认你已经使用dnspod之类的智能解析并用了cname接入cloudflare,不过即使你没用cdn也可以继续看下去,到时候反代的时候直接用源站ip即可。

配置证书

首先我们要为用于反代的香港gcp配置证书,我们要在dnspod里面添加一条A记录,并把境外的请求都解析到香港gcp上(应为let’s encrypt的验证服务器在国外),等待一会,当你看见境外的服务器解析此ip能解析到你的反代机器上时就可以了。

/etc/nginx/sites-enable/下新建一个站点文件,就叫reverse吧,注意修改成你的站点域名。

server {
    if ($host = xuchengen.cn) {
        return 301 https://$host$request_uri;
    }
        listen 80;
        listen [::]:80;
        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;
        server_name xuchengen.cn;
        location / {
                try_files $uri $uri/ =404;
        }
}

server {

        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;
        server_name xuchengen.cn;

        location / {
           proxy_ssl_name xuchengen.cn;
           proxy_ssl_server_name on;
           proxy_set_header  Host  xuchengen.cn;
           proxy_set_header  X-real-ip $remote_addr;
           proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_redirect off;
           proxy_pass https://1.0.0.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";

        }
    listen [::]:443 ssl;
    listen 443 ssl;
}

这里先解释一下里面几个关键的点,把反代部分单独拿出来看。

location / {
    proxy_ssl_name xuchengen.cn;
    proxy_ssl_server_name on;
    proxy_set_header  Host  $http_host;
    proxy_set_header  X-real-ip $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect off;
    proxy_pass https://1.0.0.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

首先看几个重要的设置:

proxy_set_header  Host  $http_host;
proxy_set_header  X-real-ip $remote_addr;
proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_name xuchengen.cn;
proxy_ssl_server_name on;
  1. 首先设置了Host,指明了我们要访问的是服务器上的哪个网站,因为我们反代使用的是相同的域名,所以直接用$http_host即可,如果你用了不同的域名,那就需要手动修改成你的源站域名了。
  2. 接着设置了$remote_addr,这个用于告诉我们的服务器访客的真实ip,而不是反代服务器的ip。
//防止 CDN 造成无法获取客户真实 IP 地址
if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    $list = explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']);
    $_SERVER['REMOTE_ADDR'] = $list[0];
}
  1. 我们使用了转发链的第一个ip,即真正的请求者的ip,这样就可以正常显示访客ip了。
  2. $proxy_add_x_forwarded_for代表的是http请求经过的服务器的ip,包括请求者ip,还有中间的代理服务器(如果提供)的ip,如果想要像第二点中那样查看真实的ip,这个也得加上。 执行nginx -t 看看还报错不报错,接着使用certbot申请证书。
  3. proxy_ssl_name和proxy_ssl_server_name 这个比较重要,如果你对cloudflare设置的反向代理出现了502的错误,大概就是没有设置这个参数 参考关于反代CloudFlare节点时返回502错误码的解决方案因为在https握手的过程中不包含host信息,所以cloudflare都不知道我们要访问哪个网站,所以我们要设置sni来指明我们要访问的是哪个网站 采用如下写法也是可以的:
proxy_ssl_name $host;
proxy_ssl_server_name on;

补充阅读:

在过去的 HTTP 时代,解决基于名称的主机同一 ip 地址上托管多个网站的问题并不难。当一个客户端请求某特定网站时,把请求的域名作为主机头(host)放在 http header 中,从而服务器根据域名可以知道把该请求引向哪个域名服务,并把匹配的网站传送给客户端。但是此方式到 https 就失效了,因为 SSL 在握手的过程中,不会有 host 信息,所以服务端通常返回配置中的第一个可用证书,这就导致不同虚拟主机上的服务不能使用不同证书(但在实际中,证书通常是与服务对应。)。 为了解决此问题,产生了 SNI,SNI 中文名为服务器名称指示,是对 SSL/TLS 协议的扩展,允许在单个 IP 地址上承载多个 SSL 证书。SNI 的实现方式是将 HTTP 头插入到 SSL 的握手中,提交请求的 Host 信息,使得服务器能够切换到正确的域并返回相应的正确证书。

  1. 最后记得 proxy_pass https://1.0.0.1; 随便找个cloudflare的ip来反代即可。
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx
#最后输入
sudo certbot --nginx
#选择你刚才输入的域名

自动配置好证书之后,我们就可以前往dnspod把境外的解析改回cloudflare了(如果之前默认是cname指向cloudflare,那么直接停用境外指向反代服务器的A记录即可),境内的保持不动。

我们可以看看我们的电脑解析到的ip是不是gcp的,我心急所以直接修改了host文件,不过多等一下其实也可以。然后再次访问我们的域名,有没有感觉快了很多呢?再在域名后面输入/cdn-cgi/trace,看看我们走的什么节点,可以看到,即使我们不是移动显示的也是hkg(电信和联通本来应该走欧洲或者美国节点的)。

最后我们还可以在dnspod里面设置d监控,检测到如果反代服务器挂掉了,那么境内也解析到cloudflare哦。

 

Nginx反向代理WebSocket

WebSocket协议相比较于HTTP协议成功握手后可以多次进行通讯,直到连接被关闭。但是WebSocket中的握手和HTTP中的握手兼容, 它使用HTTP中的Upgrade协议头将连接从HTTP升级到WebSocket。这使得WebSocket程序可以更容易的使用现已存在的基础设施。

WebSocket工作在HTTP的80和443端口并使用前缀ws://或者wss://进行协议标注,在建立连接时使用HTTP/1.1的101状态码进行协议切换, 当前标准不支持两个客户端之间不借助HTTP直接建立Websocket连接。

开发小程序的时候需要用到WebSocket长连接和推送技术,但是必须使用wss,并且必须通过域名访问。这时候就需要用到nginx反向代理了。

原理

一般我们开发的WebSocket服务程序使用ws协议,明文的。但是怎样让它安全的通过互联网传输呢?这时候可以通过nginx在客户端和服务端直接做一个转发了, 客户端通过wss访问,然后nginx和服务端通过ws协议通信。如下图所示:

配置

前提条件是你有一个域名,并且申请好了证书。

新建nginx配置文件/etc/nginx/conf.d/websocket.conf,内容如下:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream websocket {
    server localhost:8282; # appserver_ip:ws_port
}

server {
     server_name test.enzhico.net;
     listen 443 ssl;
     location / {
         proxy_pass http://websocket;
         proxy_read_timeout 300s;
         proxy_send_timeout 300s;
         
         proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection $connection_upgrade;
     }
    ssl_certificate /etc/letsencrypt/live/test.enzhico.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/test.enzhico.net/privkey.pem;
}

之类解释一下关键配置部分:

最重要的就是在反向代理的配置中增加了如下两行,其它的部分和普通的HTTP反向代理没有任何差别。

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

这里面的关键部分在于HTTP的请求中多了如下头部:

Upgrade: websocket
Connection: Upgrade

这两个字段表示请求服务器升级协议为WebSocket。服务器处理完请求后,响应如下报文:

# 状态码为101
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: upgrade

告诉客户端已成功切换协议,升级为Websocket协议。握手成功之后,服务器端和客户端便角色对等,就像普通的Socket一样,能够双向通信。 不再进行HTTP的交互,而是开始WebSocket的数据帧协议实现数据交换。

这里使用map指令可以将变量组合成为新的变量,会根据客户端传来的连接中是否带有Upgrade头来决定是否给源站传递Connection头, 这样做的方法比直接全部传递upgrade更加优雅。

默认情况下,连接将会在无数据传输60秒后关闭,proxy_read_timeout参数可以延长这个时间。源站通过定期发送ping帧以保持连接并确认连接是否还在使用。

两个超时参数

proxy_read_timeout

语法 proxy_read_timeout time 默认值 60s 上下文 http server location 说明 该指令设置与代理服务器的读超时时间。它决定了nginx会等待多长时间来获得请求的响应。 这个时间不是获得整个response的时间,而是两次reading操作的时间。

proxy_send_timeout

语法 proxy_send_timeout time 默认值 60s 上下文 http server location 说明 这个指定设置了发送请求给upstream服务器的超时时间。超时设置不是为了整个发送期间,而是在两次write操作期间。 如果超时后,upstream没有收到新的数据,nginx会关闭连接

多次代理转发

工作中遇见过一种情况,就是某个域名在移动网络下面访问不了,这样的话我需要通过一个前段代理服务器做转发,这样就涉及到两次代理。

比如访问的websocket服务URL为:

wss://test.enzhico.net

这个在腾讯云公网IP上面,所有网络都能访问。另外一个域名board.xncoding.com解析到电信网络,部署在网关中心,这个域名腾讯云可以访问到。

在腾讯云主机上面:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
     server_name test.enzhico.net;
     location / {
         proxy_pass http://board.xncoding.com;
         proxy_read_timeout 300s;
         proxy_send_timeout 300s;
         #proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection $connection_upgrade;
    }
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/test.enzhico.net/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/test.enzhico.net/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

上面唯一要注意的是忙,把proxy_set_header Host $host;这一行注释掉了。

而在网关中心主机上面:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream websocket {
    server localhost:8282; # appserver_ip:ws_port
}

server {
    listen 80;
    server_name board.xncoding.com;
    location / {
        proxy_pass http://websocket;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

只需要最外层使用wss协议,里面的交互都使用ws协议,所以监听80端口即可。

参考

Nginx配置参考

好久没有写博客了,长此以往怕是对不住一年千把块钱的阿里云ECS费用,本篇徐叔主要分享一下我是如何优雅的配置Nginx服务器,顺便也推广一下APPNode

APPNode:可视化、简易高效、操作透明、高可扩展的 Linux 服务器集群管理面板。

优雅的配置Nginx:

user              nginx;
worker_processes  1;
error_log         /var/log/nginx/error.log warn;
pid               /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include                        /etc/nginx/mime.types;
    default_type                   application/octet-stream;
    log_format                     main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"';
    access_log                     /var/log/nginx/access.log main;
    sendfile                       on;
    keepalive_timeout              65s;
    include                        /etc/nginx/conf.d/*.conf;
    server_tokens                  off;
    server_names_hash_max_size     512;
    server_names_hash_bucket_size  128;
    client_header_buffer_size      32k;
    large_client_header_buffers    4 32k;
    gzip                           on;
    gzip_disable                   msie6;
    gzip_min_length                1k;
    gzip_comp_level                5;
    gzip_buffers                   4 16k;
    gzip_http_version              1.1;
    gzip_proxied                   any;
    gzip_vary                      on;
    gzip_types                     text/plain text/css text/xml text/javascript text/x-component application/json application/javascript application/xml application/xhtml+xml application/xml+rss application/rss+xml application/atom+xml application/x-font-ttf application/x-web-app-manifest+json font/opentype image/svg+xml image/x-icon;
    fastcgi_buffer_size            64k;
    fastcgi_buffers                4 64k;
    fastcgi_busy_buffers_size      128k;
    fastcgi_temp_file_write_size   256k;
    client_max_body_size           128m;
}

 

Nginx反向代理Frp内网穿透神器

内网穿透说起来也算不上什么高深的技术,随着近些年微信公众号、支付宝生活号、小程序等新形态应用的持续火热,处于内网开发环境外网无法访问一直困扰着不少开发者。如何将内网服务暴露给外网访问?早些年的国产代表作有花生壳(收费),Github上开源产品有Ngrok、Frp等,本篇主要介绍如何在Linux上使用Nginx反向代理Frp公用80端口。

继续阅读

基于NGINX服务器搭建WordPress后台地址404解决方案

不知道从什么时候开始越来越多的web服务器开始采用Nginx Server,居可靠消息Nginx Server在全球范围内已经取代Apache Server一家独大的格局。由于Nginx Server并不支持.htaccess文件所以基于PHP的站点或多或少总一些小问题。本篇文章徐叔教你如何解决基于Nginx搭建的Wordpress后台404找不到页面的问题。

一、首先开启伪静态

location / {
        if (-f $request_filename/index.html){
               rewrite (.*) $1/index.html break;
        }
        if (-f $request_filename/index.php){
               rewrite (.*) $1/index.php;
         }
        if (!-f $request_filename){
               rewrite (.*) /index.php;
         }
}

二、添加wordpress官方给出的解决方案其实也就也就一行代码

rewrite /wp-admin$ $scheme://$host$uri/ permanent;

三、最终代码

location / {
        if (-f $request_filename/index.html){
               rewrite (.*) $1/index.html break;
        }
        if (-f $request_filename/index.php){
               rewrite (.*) $1/index.php;
         }
        if (!-f $request_filename){
               rewrite (.*) /index.php;
         }
}
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

 

自编译Nginx伪装Server版本信息

Nginx (“engine x”) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器。Nginx是由Igor Sysoev为俄罗斯访问量第二的Rambler.ru站点开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。本篇文章徐叔介绍如何伪装你的Nginx让脚本小子摸不着头脑。:)

一、前期准备

1.Centos7环境。

2.Nginx安装包建议下载稳定版(Stable version),本篇以1.10.2为例子。

3.确认系统中是否已安装gcc、openssl-devel、pcre-devel、zlib-devel。

yum -y install gcc openssl-devel pcre-devel zlib-devel

二、修改相关变量

1.目标文件路径src/core/nginx.h修改后如下:

/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


#ifndef _NGINX_H_INCLUDED_
#define _NGINX_H_INCLUDED_


#define nginx_version      1010002
#define NGINX_VERSION      "1.10.2"
#define NGINX_VER          "apple/" NGINX_VERSION

#ifdef NGX_BUILD
#define NGINX_VER_BUILD    NGINX_VER " (" NGX_BUILD ")"
#else
#define NGINX_VER_BUILD    NGINX_VER
#endif

#define NGINX_VAR          "APPLE"
#define NGX_OLDPID_EXT     ".oldbin"


#endif /* _NGINX_H_INCLUDED_ */

继续阅读