Nginx External Rewrite 跨server跳转

今天研究了下nginx中如何实现一次跨server跳转的问题. 想要实现例如请求rewrite.cn而转向本地的rewrited.cn来处理. nginx作为一个反向代理服务器, 拥有强大的跳转指令. 我们经常会使用rewrite指令来进行uri重写以实现跨location的内部跳转.

为了解决跨server的location跳转, 首先想到的仍然是rewrite指令. 将整个url重写成new_url.我们看一下官方wiki说明:

rewrite regex replacement [ flag ]

If the replacement string begins with http:// then the client will be redirected, and any further rewrite directives are terminated.

同样是在nginx官网的Pitfalls找到如下一段配置:

1
2
3
4
5
6
7
8
server {
    server_name www.domain.com;
    return 301 $scheme://domain.com$request_uri;
}
server {
    server_name domain.com;
    [...]
}

很明显, 通过这两段我们可以知道我们可以通过http协议的跳转协议来让客户端进行跳转, 很遗憾, 这样不是一个很好的解决方案, 我希望的是纯粹在server端做….

如果rewrite不行,想到可以试试第三方模块, 很容易的就想到了agentzh春哥ngx_lua模块. 于是就将注意力集中到了ngx.exec, ngx.redirect, ngx.location.capture上. 经过简单阅读相关文档, 发现还是行不通. 另外在github上找到了类似的需求, agentzh也做了相应的回答(点我). 简单说就是因为内部制约问题, 无法用过重写或者子请求等情况进行跨server的访问. 最简单的实现方法是通过proxy_pass代理模块来进行.

那我们就按照上述思路实现一下好了. 现在我们希望通过访问rewrite.cn/rewrited.cn/js/action.js来访问到rewrited.cn/js/action.js. 因为考虑到重写的需求可能会很复杂, 因此使用lua模块 进行rewrite

rewrite.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- get the target host from the uri
function rewrite_decode(uri)
  local _, _, host, d_uri = string.find(uri, '^/(.-)(/.*)$')

  return host, d_uri
end


local uri = ngx.var.uri
host, d_uri = rewrite_decode(uri)

if host == nil or d_uri == nil then
  return ngx.exit(ngx.HTTP_NOT_FOUND)
end


ngx.req.set_uri(d_uri, false)
ngx.req.set_header('Host', host)
ngx.var._proxy_host = host

rewrite.com的nginx配置文件:

rewrite.lua
1
2
3
4
5
6
7
8
9
10
11
12
server {
  server_name rewrite.cn;
  listen 80;
  access_log  logs/rewrite.cn.log;

  location / {
      set $_proxy_host '';
      rewrite_by_lua_file lua_script/rewrite.lua;
      proxy_set_header Host $_proxy_host;
      proxy_pass http://127.0.0.1:80;
  }
}

rewrited.com的nginx配置文件:

rewrite.lua
1
2
3
4
5
6
7
8
9
server {
  server_name rewrited.cn;
  listen 80;
  access_log  logs/rewrited.cn.log;

  location / {
      content_by_lua_file lua_script/fake_content.lua;
  }
}

这里的fake_content.lua是用来进行相关的测试的, 内容如下:

fake_content.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- debug

local uri  = ngx.var.uri
local args = ngx.var.args
local host = ngx.var.host

local hds = ngx.req.get_headers()


ngx.say("uri => " .. uri)

if args ~= nil then
  ngx.say("args => " .. args)
else
  ngx.say("args => nil")
end


if host ~= nil then
  ngx.say("host => " .. host)
else
  ngx.say("host is nil")
end


ngx.say("\n------ req headers ------")
for k, v in pairs(hds) do
  ngx.say(k .. " : " .. v)
end

可以通过curl以及观察相应的访问日志来查看是否正常:

1
curl -x 127.0.0.1:80 rewrite.cn/rewrited.cn/js/action.js

Comments