nginx 简单实践:静态资源部署、URL 重写【nginx 实践系列之一】

〇、前言

本文为 nginx 简单实践系列文章之一,主要简单实践了两个内容:静态资源部署、重写,仅供参考。

关于 Nginx 基础,以及安装和配置详解,可以参考博主过往文章:

https://www.cnblogs.com/hnzhengfy/p/Nginx.html

一、静态资源部署

当前项目的结构基本上都是前后端分离,前端的相关资源,如 html,js,css 或者图片,变更频率比较低但访问量比较大,若想保证系统性能,可以将这些文件放在指定目录下,通过 nginx 的 root 或 alias 配置方式来指定静态资源的路径。

基于高并发高性能的 ningx,用户就可以通过域名加路径的方式,高效快速的进行页面访问。

下面是一个简单的示例。

nginx 配置示例(部分):

复制代码
server {
    listen       8080;  # 绑定端口
    server_name  localhost;
	
    charset utf-8;  # 指定字符集,不然 html 文件中的汉字会乱码
	
    location /test {  # 路径匹配规则
        alias  /usr/tmp/test; # 别名,将当期路径对应到新的目录中
        index  index.html index.htm;
    }

    location /test_2 {  # 路径匹配规则
        alias  /usr/tmp/test/test_2; # 别名,将当期路径对应到新的目录中
        index index.html index.htm;
    }
}

示例文件示例,文件位置为 /usr/tmp:

/usr/tmp/test/test_page.html

/usr/tmp/test/test_2/test_2_page.html

/usr/tmp/test/test_2/westlage.jpg

测试结果如下图:

参考:https://www.cnblogs.com/qingshan-tang/p/12763522.html

二、Rewrite:URL 重写

2.1 简介

rewrite 就是 URL 重写,是一个将传入的 web 重定向到其他 URL 的过程。

上一部分静态资源部署,主要用的是 location 相关配置来实现静态资源的直接访问,下面先看下 rewrite 和 location 的区别。

  • rewrite 和 location 比较

rewrite 更侧重于 URL 路径的重写和跳转,而 location 则侧重于请求路径的匹配和处理。在实际使用中,两者往往结合使用,以实现复杂的请求处理逻辑。

功能目的

rewrite 主要用于重写 URL 路径 ,可以实现内部跳转(不改变浏览器地址栏的URL)或外部跳转(使用 redirect 标志时),通常用于在同一域名内更改获取资源的路径

location 用于匹配请求的 URL 路径,本身不实现跳转,而是对匹配到的路径进行相应的处理,如访问控制、反向代理或静态资源服务。

配置位置

rewrite 只能放在 server {}, location {}, if {} 中,并且只能对域名后边的除去传递的参数外的字符串起作用。

location 作为 server {} 块中的一个指令,可以基于请求字符串、虚拟主机名称(IP或域名)和URL进行匹配。

执行顺序

rewrite 在 server 块中的 rewrite 指令会首先执行,然后是 location 匹配,最后是 location 块中的 rewrite 指令。

location 直接根据请求的 URL 路径进行匹配,一旦匹配成功,就会执行对应的处理逻辑。

2.2 rewrite 的优势

  • **隐藏真实目录结构:**通过隐藏服务器上的真实文件路径和目录结构,防止攻击者通过直接访问文件路径来获取敏感信息。
  • **规范化 URL:**强制规范化 URL 格式既可以避免一些常见的安全漏洞,如路径遍历攻击(Directory Traversal)或路径参数欺骗等等,又可以保证 URL 格式的一致性。
  • 防止盗链: 通过 Rewrite 可以实施防盗链策略,防止其他网站直接链接到本站的资源。这有助于减轻服务器负载,防止不法分子盗用网站的带宽,并提高资源的安全性。
  • HTTP 到 HTTPS 的强制重定向:可以确保数据在传输过程中的安全性。这是保障通信安全的一种有效手段,尤其对于涉及用户敏感信息的网站至关重要
  • 条件性重写:可以根据请求中的条件来选择是否进行重写,有助于实现访问控制和强化安全性。例如,只有特定 IP 范围的请求才允许进行某种操作。
  • 跨站点脚本(XSS)防护:Rewrite 可以对请求参数进行过滤或修改,防止恶意用户通过注入脚本来进行 XSS 攻击。对 URL 和参数进行适当的重写可以减轻 XSS 攻击的风险。
  • 统一资源标识符(URI)规范化:通过强制规范化 URI,可以防止攻击者尝试混淆或绕过安全策略。规范化的URI有助于提高应用程序的安全性,防范一些常见的攻击手法。
  • 避免敏感信息泄露:Rewrite 可以限制对某些敏感信息或文件的访问,确保只有授权用户能够获取特定内容。这有助于防止敏感信息泄露和未授权访问。

2.3 Rewrite 相关指令

Rewrite 相关指令有 if、rewrite、set、return。

2.3.1 if 语句

Nginx 中的 if 语句常用于实现精细的访问控制策略。例如,可以设置只有特定IP或内网IP可以访问某些资源,其他IP则被重定向到停服更新页面,也可以通过 if 语句实现 URL 重写、限制访问速度等功能。

复制代码
# 基本语法
if (condition) {
    ......
}

在匹配中可以用的一些符号和常量:

复制代码
# if 可以支持如下条件判断匹配符号
~             正则匹配(区分大小写)
~*            正则匹配(不区分大小写)
!~            正则不匹配(区分大小写)
!~*           正则不匹配(不区分大小写)
-f 和!-f      用来判断是否存在文件,! 表示不存在
-d 和!-d      用来判断是否存在目录
-e 和!-e      用来判断是否存在文件或目录
-x 和!-x      用来判断文件是否可执行

# 在匹配过程中可以引用一些 Nginx 的全局变量
$args            	请求中的参数
$document_root      针对当前请求的根路径设置值
$host            	请求信息中的 Host,如果请求中没有Host行,则等于设置的服务器名
$limit_rate        	对连接速率的限制
$request_method     请求的方法,比如 GET、POST 等
$remote_addr        客户端地址
$remote_port        客户端端口号
$remote_user        客户端用户名,认证用
$request_filename   当前请求的文件路径名(带网站的主目录 /usr/local/nginx/html/images/a.jpg)
$request_uri        用于表示客户端请求的完整 URI,也就是请求地址,它记录了客户端发起的请求地址
$query_string       与 $args 相同
$scheme             用的协议,比如 http 或者是 https
$server_protocol    请求的协议版本,HTTP/1.0 或 HTTP/1.1
$server_addr        服务器地址,如果没有用 listen 指明服务器地址,使用这个变量将发起一次系统调用以取得地址(造成资源浪费)
$server_name        请求到达的服务器名
$document_uri       与 $uri 一样,URI 地址
$server_port        请求到达的服务器端口号

后文将进行示例演示。

2.3.2 rewrite flag 简介:redirect、permanent、break、last

Nginx 中的 rewrite 指令可以包含一个可选的 flag(标志),用于控制重写操作后的行为

这个 flag 的类型总共有四种,如下:

  • redirect:这是一种临时重定向,状态码为 302。当重写完成后,Nginx 会以临时重定向的方式返回重写后生成的新 URL 给客户端。浏览器地址栏会显示跳转后的 URL 地址,但爬虫不会更新 URL。
  • permanent:这是一种永久重定向,状态码为301 。与 redirect 类似,permanent 也会直接对 URL 地址进行重定向,并显示跳转后的 URL 地址。从实现功能的角度看,redirect 和 permanent 是一样的,不存在好坏和性能上的问题。然而,对于 SEO(搜索引擎优化)来说,使用 permanent 更为友好,因为它告诉搜索引擎这是一个永久性的移动,把原始页面的所有排名信号都传递到新的URL上,无需再请求到原地址,也避免了地址栏的重复跳转,更友好。
  • break:在重写完成后,break 会停止处理 当前 URL 在当前 location 以及其他 location 中后续的其他重写操作 。它不会跳出 location 作用域,也不会重新搜索与更改后的 URI 相匹配的 location。适用于一个 URL 只需要一次重写的场景。2
  • last:重写完成后,last 会停止处理 当前 URI 在当前 location 中后续的其他重写操作 。但它会对新的 URL 启动新一轮重写检查,并可能跳出当前 location 作用域,搜索与更改后的 URI 相匹配的 location。适用于一个 URL 可能需要多次重写的场景。

2.3.3 permanent 简单示例

注意:可以使用测试域名,但前提是要修改 hosts 文件。

路径和重启:Linux(/etc/hosts)(重启命令:/etc/init.d/network restart);Windows(C:\Windows\System32\drivers\etc\hosts)。

  • 实例:变更整个资源路径

示例:从 http://www.testczzj.com:8080/test/test_page.html

------> http://www.testczzj.com:8080/test2/test2.html

示例文件夹结构:

/usr/tmp/test/test_page.html

/usr/tmp/test2/test2_page.html

复制代码
<div id="div1">
	<h1>这是test2_page页面</h1>
</div>

server {
    listen       8080;
    server_name  www.testczzj.com;
    charset utf-8;
	
    location /test {
        index  index.html index.htm;
        # 匹配成功之后的全部路径,全部替换成指定的地址
        rewrite  .*  /test2/test2_page.html  permanent;
    }

    location /test2 {
        alias  /usr/tmp/test2;
        index index.html index.htm;
    }
}

自动跳转:(输入第一行地址回车自动跳转到新地址)

  • 实例:变更路径中的一部分

示例:http://www.testczzj.com:8080/2024/test/test_page.html

------> http://www.testczzj.com:8080/2025/test/test_page.html

目的就是将 URL 中的 /2024/ 自动换成 /2025/,当然,实际业务中可以一般没有这么简单。

示例文件夹结构:

/usr/tmp/2025/test/test_page.html

复制代码
<div id="div1">
	<h1>这是test_page页面</h1>
</div>

server {
    listen       8080;
    server_name  www.testczzj.com;
    charset utf-8;
	
    location /2024/test {
        index  index.html index.htm;
        # 先匹配 2024 之后的全部路径,再拼接到 2025 之后
        rewrite  ^/2024/(.*)  /2025/$1  permanent; #  (.*):表示以任意结尾,全部匹配取到
    }

    location /2025/test2 {
        alias  /usr/tmp/test2;
        index index.html index.htm;
    }
}

自动跳转:(输入第一行地址回车自动跳转到新地址)

  • 实例:将旧域名永久重定向到新域名

示例:http://www.testczzj.com/2024/test/test_page.html

------> http://www.testczzj_new.com

根据路径中的标识:/2024,来拦截,将全部请求直接跳转到新的地址主页。

复制代码
server {
    listen       8080;
    server_name  www.testczzj.com www.testczzj_new.com;
    charset utf-8;
	
    location /2024 {
        root /usr/tmp/2024
        index  index.html index.htm;
        if  ($host ~* www.testczzj.com){ # 将 testczzj 转到 testczzj_new
            rewrite .* http://www.testczzj_new.com permanent;
            }
    }
}

自动跳转:

  • 实例:只更新协议和域名,url 路径不变

示例目标:http://www.testczzj.com/2024/test/test_page.html

------> https://cn.bing.com/2024/test/test_page.html

配置如下:

复制代码
server {
    listen       80;
    server_name  www.testczzj.com;
    charset utf-8;
	
    location /2024 {
       root /usr/tmp/2024;
       index  test_page.html  index.html  index.htm;
        if  ($host ~* www.testczzj.com){ # $request_uri 用于表示客户端请求的完整 URI,也就是请求地址
            rewrite .* http://cn.bing.com$request_uri permanent;
            }
    }
}

自动跳转:(由于 bing 后路径没有资源,因此页面会加载失败)

  • 实例:在 URL 地址末尾没有 / 时,默认添加

示例目标:http://www.testczzj.com/2024/test

------> http://www.testczzj.com/2024/test/

注:示例文件路径:/usr/tmp/2024/test/test_page.html。

为什么要添加这个斜线?

搜索引擎对带有和不带有斜杠的 URL 有着不同的处理方式。通常,带有斜杠的 URL 被视为目录,而不带斜杠的 URL 被视为文件或特定资源。

当 URL 的斜杠使用不一致时,可能会导致不必要的重定向,从而影响页面加载速度和用户体验。一致的 URL 结构有助于提高用户体验。用户期望目录级别的 URL 以斜杠结尾,而文件级别的 URL 则不带斜杠。

复制代码
server {
    listen       80;
    server_name  www.testczzj.com;
    charset utf-8;
	
    location /2024/test {
       root /usr/tmp;
       index  test_page.html  index.html  index.htm;
        if  (-d  $request_filename){ # 用于检查请求的文件名是否是一个目录。如果是目录,则执行大括号内的指令
            # (.*):捕获任意字符(除了换行符)零次或多次,并将其存储在第一个捕获组中
            # ([^/]):捕获除斜线(/)之外的任意单个字符,并将其存储在第二个捕获组中
            # $host:当前请求的主机名
            rewrite ^(.*)([^/])$ http://$host$1$2/ permanent; 
            }
    }
}

自动补上斜线:

  • 实例:重定向后,在 URL 末尾添加 Query 参数

示例目标:http://www.testczzj.com/login/czzj.html

------> http://www.testczzj.com/2024/test/login.html?name=czzj

示例文件路径:/usr/tmp/2024/test/login.html。

把功能隐藏到 URL 路径中去。

复制代码
server {
    listen       80;
    server_name   www.testczzj.com;
    charset utf-8;

    location /login {
       root /usr/tmp/2024;
       index  login.html  index.html  index.htm;
       # 正则表达式的目的:匹配以 /login/ 开头并以 .html 结尾的内容为第一捕获组:$1
       rewrite ^/login/(.*)\.html$ http://$host/test/login.html?name=$1;
    }

    location /test { # 处理重写后的请求:/test
            alias  /usr/tmp/2024/test;
            index  login.html  index.htm;
    }
}

如下图自动跳转:

  • 实例:将固定格式 /2025-01-02/ 重定向为 /2025/01/02/

示例目标:http://www.testczzj.com/test/2025-01-03/test_page.html

------> http://www.testczzj.com/test/2025/01/03/test_page.html

示例文件路径:/usr/tmp/test/2025/01/03/test_page.html

复制代码
server {
    listen       80;
    server_name   www.testczzj.com;
    charset utf-8;

    location /test {
       root /usr/tmp;
       index  test_page.html  index.html  index.htm;
       # ([0-9]+): 这是一个捕获组,用于匹配一个或多个数字(即至少一个数字)
       # (.*): 这是第四个捕获组,用于匹配任意数量的任意字符(包括空字符)
       rewrite ^/test/([0-9]+)-([0-9]+)-([0-9]+)(.*)$ /test/$1/$2/$3$4 permanent;
    }
}

如下图自动跳转:

2.4 set 指令

set 指令用于设置一个变量的值。这个指令可以在多个上下文中使用,例如 http、server、location 和 if 块中。

复制代码
# 基本语法
set $variable_name value;
# $variable_name:要设置的变量名,必须以 $ 符号开头
# value:要赋予变量的值,可以是字符串、数字或由其他变量组成的表达式
  • 实例:将域名转换为路径参数

示例目标:

http://username1.testczzj.com/test/userPage1.html

------> http://www.testczzj.com/username1/userPage1.html

http://username2.testczzj.com/test/userPage2.html

------> http://www.testczzj.com/username2/userPage2.html

示例文件:

/usr/tmp/username1/userPage1.html

/usr/tmp/username2/userPage2.html

复制代码
# hosts 添加配置,注意请将'服务器IP地址'替换为自己真实的服务器 IP
测试机IP地址 www.testczzj.com
测试机IP地址 username1.testczzj.com
测试机IP地址 username2.testczzj.com
# Linux(编辑:/etc/hosts)(重启命令:/etc/init.d/network restart)
# Windows(编辑:C:\Windows\System32\drivers\etc\hosts)

nginx 配置如下:

复制代码
server {
    listen       80;
    server_name   www.testczzj.com;
    charset utf-8;

    location /test {
       root /usr/tmp;
       index index.html  index.htm;
       if ( $host ~* ^www.testczzj.com$ ){
            break; # 终止 nginx 在当前请求中的所有后续 location 的匹配
            }
       if ( $host ~* "^(.*)\.testczzj\.com$" ) {
            set $user $1; # 将正则表达式中第一个捕获组的值赋给变量 $user
            rewrite .* http://www.testczzj.com/$user permanent;
            }
    }

    location /username1 {
            alias  /usr/tmp/username1;
            # root /usr/tmp;
            index  userPage1.html index.html  index.htm;
    }

    location /username2 {
            alias /usr/tmp/username2;
            # root /usr/tmp;
            index userPage2.html index.html index.htm;
    }
}

效果如下图:

2.5 return 指令

return 指令是 nginx 配置中用于立即返回指定状态码可选的响应体的指令。它通常用于处理特定的请求,并立即终止进一步的处理。

复制代码
# 语法
return code [text];
# code: 要返回的 HTTP 状态码,例如 200, 404, 500 等
# [text]: 可选的响应体文本。如果省略,Nginx 将使用默认的响应体

# 简单示例
location / {
    return 200; # 对所有匹配 / 路径的请求返回 HTTP 200 状态码
}
# 返回状态码和自定义响应体
location /custom {
    return 403 "Access Denied"; # 对所有匹配 /custom 路径的请求返回 HTTP 403 状态码,并在响应体中包含 "Access Denied" 文本
}
# 重定向到另一个 URL
location /redirect {
    return 301 http://example.com; # 将所有匹配 /redirect 路径的请求重定向到 http://example.com,并返回 HTTP 301 状态码
}

return 指令会立即终止当前请求的处理,不会继续执行后续的配置指令。因此,它通常放在 location 块的开头。

如果需要返回带有复杂内容的响应体,建议使用 add_header 和 echo 模块,或者通过内部重定向到一个专门处理这些内容的 location。

  • 实例:若 URL 中有调用 .sh 脚本文件,直接返回 403 拒绝操作

示例目标:

http://www.testczzj.com/test/

------>正常加载指定页面:http://www.testczzj.com/test/test_page.html

http://www.testczzj.com/test/ts.sh

------> 返回禁止访问:403 Forbidden

nginx 配置如下:

复制代码
server {
    listen       80;
    server_name   www.testczzj.com;
    charset utf-8;

    location ~*  \.sh$ { # 写在最前边
            return  403;
    }
    
    location /test {
            alias  /usr/tmp/test;
            index  test_page.html index.html  index.htm;
    }
}

效果如下图:

  • 实例:http 转 https(端口 80 转 443)

注意,这个测试需要有 SSL 证书。

如下配置项供参考:(未经实际验证)

复制代码
server {
    listen       80;
    server_name  www.testpm.cn;
    access_log  /var/log/nginx/http_access.log  main;
    return 301 https://www.testpm.cn$request_uri;   #返回301永久重定向
}
server {
    listen 443 ssl;
    server_name www.testpm.cn;
    access_log  /var/log/nginx/https_access.log  main;

    #ssl on;
    ssl_certificate   /etc/nginx/cert/2447549_www.testpm.cn.pem;
    ssl_certificate_key  /etc/nginx/cert/2447549_www.testpm.cn.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    ssl_prefer_server_ciphers on;

    location / {
        root  /usr/share/nginx/html;
        index index.html index.htm;
    }
}

可以通过 curl 命令进行检验:

复制代码
# 使用 curl 命令行工具发送 HTTP 请求的命令
# -I 或 --head 选项告诉 curl 只获取 HTTP 响应的头部信息,而不下载整个内容
# 这对于检查服务器响应头信息非常有用
# 当你运行这个命令时,curl 会向目标服务发送一个 HTTP HEAD 请求,并返回该请求的响应头信息
# 响应头信息通常包括状态码、内容类型、服务器信息、日期等。
[root@localhost ~]# curl -I http://www.testpm.cn
HTTP/1.1 301 Moved Permanently
Server: nginx/1.16.0
Date: Wed, 03 Jul 2019 13:52:30 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://www.testpm.cn/

2.6 last(重新进行匹配)、break(终止) 指令

last 和 break 指令是用于控制 rewrite 模块的行为,它们在处理重写规则时有不同的作用

last 指令在当前的 rewrite 规则执行完后,会停止处理后续的 rewrite 规则,并根据重写后的新 URI 重新发起一次请求,并从服务器配置的顶部开始重新匹配 location 块。

示例:假设有一个 location 块/old_path,使用rewrite ^/old_path(.*)$ /new_path$1 last;,当访问/old_path/something时,nginx 会将其重写为/new_path/something重新匹配 location 块

break 指令在当前的 rewrite 规则匹配成功后,会停止处理后续的 rewrite 规则,但不会重新开始匹配 location 块,即在满足特定条件后终止重写过程

示例:同样的 location 块/old_path,如果使用rewrite ^/old_path(.*)$ /new_path$1 break;,当访问/old_path/something时,nginx 会将其重写为/new_path/something,但不会重新匹配 location 块,而是继续处理当前请求的其他阶段。

参考:https://blog.csdn.net/m0_62396418/article/details/135747521

相关推荐
虚伪的空想家4 小时前
rook-ceph配置dashboard代理无法访问
ceph·云原生·k8s·存储·rook
Yo_Becky10 小时前
【PyTorch】PyTorch预训练模型缓存位置迁移,也可拓展应用于其他文件的迁移
人工智能·pytorch·经验分享·笔记·python·程序人生·其他
lovely_nn1 天前
docker 介绍
docker·k8s
退役小学生呀11 天前
三、kubectl使用详解
云原生·容器·kubernetes·k8s
cwtlw11 天前
Excel学习03
笔记·学习·其他·excel
ALe要立志成为web糕手11 天前
Kubernetes安全
安全·web安全·网络安全·k8s·云安全
老陈头聊SEO13 天前
AI与SEO关键词协同进化
其他
蚊子不吸吸14 天前
在Docker、KVM、K8S常见主要命令以及在Centos7.9中部署的关键步骤学习备存
linux·学习·docker·kubernetes·centos·k8s·kvm
学视线12314 天前
汽车加气站操作工考试题库含答案【最新】
其他
nikoni2314 天前
Return ratio法计算环路增益
笔记·其他·硬件工程