Nginx 如何解决跨域、防盗链和 URL 重写?
简化版
跨域(CORS):浏览器同源策略限制跨域请求,Nginx 可通过 add_header 添加 Access-Control-Allow-Origin 等响应头,并处理 OPTIONS 预检请求,在网关层统一解决跨域。防盗链:防止别的网站直接引用你的图片/资源盗刷流量,用 valid_referers 检查请求来源 Referer,非法来源返回 403。URL 重写(rewrite):用 rewrite 指令按正则改写 URL,实现路径映射、跳转、伪静态等(rewrite 正则 替换 [flag],常用 flag 有 last、break、redirect、permanent)。
详细版
① 跨域 CORS:
location /api/ {
add_header Access-Control-Allow-Origin $http_origin; # 允许的源
add_header Access-Control-Allow-Methods "GET,POST,PUT,DELETE,OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type,Authorization";
add_header Access-Control-Allow-Credentials true;
if ($request_method = OPTIONS) {
return 204; # 预检请求直接返回
}
proxy_pass http://backend;
}
② 防盗链:
location ~* \.(jpg|png|gif)$ {
valid_referers none blocked server_names *.mysite.com;
if ($invalid_referer) {
return 403; # 非法来源拒绝
}
}
③ URL 重写 rewrite:
rewrite ^/old/(.*)$ /new/$1 permanent; # 301 永久重定向
rewrite ^/article/(\d+)$ /article.php?id=$1 last; # 伪静态
- rewrite flag:
last(重写后重新匹配 location)、break(重写后停止)、redirect(302 临时)、permanent(301 永久)。
完整版教学
一、跨域 CORS:为什么以及怎么解决
同源策略是浏览器的安全机制:默认只允许网页向同源(协议、域名、端口都相同)的地址发请求,跨源(比如前端 a.com 访问接口 api.b.com)会被浏览器拦截。这就是跨域问题。
CORS(跨域资源共享) 是标准的解决方案:服务端在响应里加特定的头,告诉浏览器「我允许这个源跨域访问我」,浏览器就放行。可以在后端加,也可以统一在 Nginx 层加(更集中、后端无需改代码):
Access-Control-Allow-Origin:允许哪个源跨域(*允许所有,或指定具体域名;带凭证时不能用*)。Access-Control-Allow-Methods:允许的 HTTP 方法。Access-Control-Allow-Headers:允许的自定义请求头。Access-Control-Allow-Credentials:是否允许带 Cookie。
预检请求(OPTIONS):对于非简单请求(如带自定义头、PUT/DELETE),浏览器会先发一个 OPTIONS 请求探路(预检),确认服务端允许后才发真正的请求。Nginx 要专门处理 OPTIONS——直接返回 204 和 CORS 头,不用转发给后端。
二、防盗链:保护你的资源不被盗用
盗链指别的网站直接引用你服务器上的资源(尤其图片、视频、下载文件)。比如你网站的图片 mysite.com/img.jpg,被 other.com 直接 <img src="mysite.com/img.jpg"> 引用——用户访问 other.com 时,图片流量和带宽却由你的服务器承担,相当于被白嫖流量。
防盗链原理:利用 HTTP 请求头里的 Referer(记录请求是从哪个页面发起的)。正常情况下,加载你网站图片的请求,Referer 应该是你自己的域名;如果 Referer 是别的网站,就是盗链。
Nginx 用 valid_referers 配置合法来源:
valid_referers none blocked server_names *.mysite.com;——none(无 Referer,如直接访问)、blocked(Referer 被防火墙删除)、自己的域名都算合法。- 不在白名单的来源,
$invalid_referer变量为真,return 403拒绝。
注意 Referer 可以伪造,所以防盗链只能防「普通盗链」,对刻意伪造 Referer 的防不住(要更强防护得用签名 URL、Token 等)。
三、URL 重写 rewrite:改写请求路径
rewrite 用正则表达式改写请求的 URL,用途很多:
- 路径映射/迁移:老路径
/old/xxx重写到新路径/new/xxx。 - 伪静态:把动态 URL(
/article.php?id=123)对外展示成静态样子(/article/123),对 SEO 和美观有好处。 - 重定向跳转:HTTP 跳 HTTPS、旧域名跳新域名。
语法:rewrite 正则 替换内容 [flag]。正则里用 (...) 捕获、替换里用 $1、$2 引用。
四、rewrite 的四个 flag(关键考点)
rewrite 末尾的 flag 决定重写后的行为,容易混淆:
last:重写 URL 后,用新 URL 重新发起一轮 location 匹配(重新走一遍 location 规则)。常用于需要重写后再匹配其他 location 的情况。break:重写 URL 后,停止继续处理 rewrite,直接用当前 location 的配置处理新 URL(不再重新匹配 location)。redirect:返回 302 临时重定向(浏览器地址栏变成新 URL,临时性,搜索引擎不转移权重)。permanent:返回 301 永久重定向(永久性,搜索引擎会把权重转移到新 URL,SEO 友好)。
记忆:last 和 break 是内部重写(URL 变了但浏览器地址栏不变,服务器内部转发);redirect 和 permanent 是外部重定向(返回 3xx,浏览器地址栏变化、会再发一次请求)。last 会重新匹配 location,break 不会。
五、rewrite 与 location 的配合
rewrite 和 location 经常配合,理解执行顺序:Nginx 先按 location 匹配,进入 location 后可能有 rewrite。last 会让重写后的 URL 跳出当前 location 重新匹配(可能进入另一个 location),break 则留在当前 location 继续处理。用错 flag 可能导致重写死循环或匹配不到预期 location,是常见坑。
六、常见误区与追问
| 考点 | 正确口径 |
|---|---|
| CORS | 通过响应头控制浏览器跨域读取权限 |
| 防盗链 | 根据 Referer 等限制外站引用资源 |
| Rewrite | 按规则重写 URI 或跳转 |
add_header Access-Control-Allow-Origin https://a.com;
valid_referers none blocked server_names *.example.com;
rewrite ^/old/(.*)$ /new/$1 permanent;
CORS 是浏览器安全策略,防盗链是资源访问控制,rewrite 是 URL 规则变换,三者别混。
- 误区:CORS 是 Nginx 让请求能不能发出。 跨域请求可能已到服务器,浏览器根据响应头决定 JS 能不能读响应。
- 误区:Referer 防盗链绝对可靠。 Referer 可能为空或被伪造,只适合作为基础防护,重要资源要签名 URL。
- 误区:rewrite 和 proxy_pass 是一回事。 rewrite 改 URI 或返回跳转,proxy_pass 是把请求代理到上游。
- 追问:带 Cookie 的 CORS 怎么配? 不能用通配
*,要指定 Origin,并允许 credentials。 - 追问:防盗链误伤怎么处理? 允许空 Referer、白名单域名,并对客户端/APP 场景做签名兜底。
- 追问:rewrite permanent 和 last 有什么区别? permanent 返回 301 给客户端,last 在服务端继续按新 URI 匹配。
七、加强记忆
Nginx 三个常见需求:① 跨域 CORS——用 add_header 加 Access-Control-Allow-Origin/Methods/Headers/Credentials,并单独处理 OPTIONS 预检(返回 204),在网关层统一解决同源策略限制;② 防盗链——用 valid_referers 检查请求的 Referer 头,非法来源($invalid_referer)返回 403,防止资源被别站盗用流量(但 Referer 可伪造,只防普通盗链);③ URL 重写 rewrite——rewrite 正则 替换 [flag] 改写 URL 做路径映射/伪静态/跳转,四个 flag:last(重写后重新匹配 location)、break(重写后留在当前 location)、redirect(302 临时)、permanent(301 永久)。口诀:跨域加响应头处理预检、防盗链查 Referer、rewrite 记住 last/break 内部转、redirect/permanent 外部跳。