标签 nginx 下的文章

有时候我们需要通过业务逻辑限制用户对某些路径的访问,这件事情通过nginx配置本身是做不了的,例如登录用户校验之类的逻辑。举个栗子:

有一个文件,这个文件位于服务器的 /home/server/private_files/ 目录。只有高等级的用户才能获取下载路径,且下载路径有时效,过期则失效。

要实现此功能,我们得把它分为两部分:一部分是下载路径的生成和有效性判断,这部分是业务逻辑代码实现的;另一部分则是对这个文件的保护,只有通过业务逻辑校验的请求才有权拿到此文件。

此时我们就需要用到Nginx的内部重定向功能了。

我们需要知道的两个知识点:

  1. Nginx在做反向代理拿到服务端生成的结果时,会检查服务端返回的响应头(Response Header),检查此字段:X-Accel-Redirect。如果此字段设置了内容,Nginx则会根据其设置的重定向地址,直接请求此地址的数据,并将数据输出。
  2. Nginx的location配置可以使用internal标记,将此路径标记为只允许内部访问。内部访问的意思并不是说localhost就可以访问,而是说「一次请求的」内部。

那么如何配置呢?

首先,nginx需要加上如下配置:

location /get_file/ {
    fastcgi_pass xxxxx # 或 proxy_pass,用于将请求转发到服务端
}

location /private_files/ {
    # 内部路径,禁止直接请求
    internal;
    alias /home/server/private_files/
}

然后,在服务端实现对 /get_file/xxx 的处理逻辑,如果允许访问,则返回的响应头中,写入如下信息:(以php为例)

// And redirect user to internal location
header("X-Accel-Redirect: /private_files/a.txt");

用图来表示一下:

在用户直接请求 /private_files/a.txt

直接访问

在用户请求 /get_files/xxxx

合法请求

有如下需求:

我有一个域名 huangfeiqiao.com,我想在上面搭几个web应用,分别是foo、bar、baz。

一种办法就是在nginx的document root目录中建立foo、bar、baz目录,访问方式就是huangfeiqiao.com/foo、huangfeiqiao.com/bar等等。但这样有几个缺点——

  1. 某些web应用可能本身就不支持非根目录的方式访问,导致不可用
  2. 因为各个应用可能需要进行location的特殊配置,而这些应用不在根路径的时候,location配置可能有所不同,更加复杂
  3. 不够酷

需要让foo、bar、baz都拥有自己的子域名(foo.huangfeiqiao.com、bar.huangfeiqiao.com等等),可以这么做:

  1. 在域名服务商那里,加一个CNAME记录,把需要配置的子域名指向根域对应的主机名即可。
  2. 然后这么写nginx配置:
server {
    listen 80;
    listen 443 ssl;
    server_name  huangfeiqiao.com;
    # blah blah...
    # 设置子域名目录
    set $doc "";
    if ($host ~ ^(\w+)\.huangfeiqiao\.com$) {
        set $doc $1;
    }
    # www的话就是根目录
    if ($host = "www.huangfeiqiao.com") {
        set $doc "";
    }
    # 设置此时域名对应的root
    root /path/to/www/$doc;
    index index.html index.htm index.php;

    # 日志配置
    access_log  logs/$host.access.log  main;
    error_log  logs/error.log;
}

搞定。

这样做的好处就是,之后要加个子域名,只要在www目录中新建一个子目录,取子域名作为目录名,然后在域名服务商新增一条字域名的CNAME记录就搞定了,非常方便。

站点运行在nginx服务,然而我并不想使用root来跑这个服务,因此也没法使用80端口。于是想着是否可以使用iptables进行端口转发,将80端口的数据转发到nginx服务的端口8888。

在搜了一堆资料后,有了如下方法

#!/bin/bash
# nat表,PREROUTING链增加转发规则
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8888
# mangle表,PREROUTING链给发向8888的包做个标记
iptables -t mangle -A PREROUTING -p tcp -m tcp --dport 8888 -j MARK --set-mark 1
# filter表,INPUT链,首先允许所有本地接口的数据包
iptables -t filter -I INPUT -i lo -j ACCEPT
# 然后拒绝所有被标记了1的包
iptables -t filter -A INPUT -m mark --mark 1 -j REJECT