第455集Nginx静态服务从基础到架构实战
|字数总计:6.8k|阅读时长:29分钟|阅读量:
Nginx静态服务从基础到架构实战
1. 概述
1.1 Nginx静态服务的重要性
Nginx作为静态资源Web服务器具有极高的传输效率,常常用于静态资源处理、请求分发和动静分离。相比动态应用服务器(如Tomcat、PHP-FPM),Nginx在处理静态资源时具有以下优势:
Nginx静态服务优势:
- 高性能:基于epoll事件模型,支持高并发
- 低资源消耗:内存占用少,CPU消耗低
- 高效传输:sendfile零拷贝技术
- 功能丰富:压缩、缓存、防盗链、跨域等
1.2 静态资源定义
静态资源:非服务器动态运行生成的文件,包括:
- 图片资源:jpg、png、gif、webp、svg等
- 样式文件:css、less、sass等
- 脚本文件:js、ts等
- 字体文件:woff、woff2、ttf、eot等
- 文档文件:html、htm、xml、txt、pdf等
- 媒体文件:mp4、mp3、flv、avi等
- 压缩文件:zip、rar、tar、gz等
1.3 本文内容结构
本文将从以下几个方面全面解析Nginx静态服务:
- 静态资源类型:分类、特点、应用场景
- 静态资源场景:CDN、动静分离、文件服务器
- 静态资源配置语法:sendfile、tcp_nopush、tcp_nodelay
- 静态资源文件压缩:gzip压缩配置、压缩比率、压缩类型
- 静态资源浏览器缓存:Expires、Cache-Control、ETag、Last-Modified
- 静态资源跨域访问:CORS配置、跨域场景
- 静态资源防盗链:referer验证、防盗链策略
2. 静态资源类型
2.1 静态资源分类
2.1.1 按文件类型分类
| 类型 |
扩展名 |
特点 |
应用场景 |
| 图片 |
jpg、png、gif、webp、svg |
体积大、访问频繁 |
网站图片、图标、背景 |
| 样式 |
css、less、sass |
文本文件、可压缩 |
页面样式、主题 |
| 脚本 |
js、ts、jsx |
文本文件、可压缩 |
前端逻辑、框架 |
| 字体 |
woff、woff2、ttf、eot |
体积中等 |
自定义字体 |
| 文档 |
html、htm、xml、txt |
文本文件、可压缩 |
页面、配置文件 |
| 媒体 |
mp4、mp3、flv、avi |
体积大、流式传输 |
视频、音频 |
| 压缩 |
zip、rar、tar、gz |
已压缩 |
下载文件 |
2.1.2 按访问频率分类
高频访问资源:
- 网站Logo、图标
- 公共CSS、JS文件
- 常用字体文件
中频访问资源:
低频访问资源:
2.2 静态资源特点
2.2.1 不变性
特点:
2.2.2 可压缩性
特点:
- 文本文件压缩率高(60-80%)
- 图片文件压缩率低(已压缩)
- 二进制文件压缩率中等
2.2.3 可缓存性
特点:
3. 静态资源场景
3.1 CDN场景
3.1.1 CDN架构
架构图:
1 2 3 4 5 6
| 用户请求 ↓ CDN边缘节点(Nginx) ↓ ├──→ 缓存命中 → 直接返回 └──→ 缓存未命中 → 回源获取
|
Nginx作为CDN节点:
3.1.2 CDN配置示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| server { listen 80; server_name cdn.example.com; location ~ .*\.(jpg|jpeg|png|gif|css|js)$ { root /data/cdn; expires 30d; add_header Cache-Control "public, immutable"; add_header Access-Control-Allow-Origin *; } }
|
3.2 动静分离场景
3.2.1 动静分离架构
架构图:
1 2 3 4 5 6
| 用户请求 ↓ Nginx(反向代理) ↓ ├──→ 静态资源 → Nginx直接服务 └──→ 动态请求 → 后端应用服务器
|
优势:
- 静态资源由Nginx高效处理
- 动态请求转发到应用服务器
- 提高整体性能
3.2.2 动静分离配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| upstream backend { server 192.168.1.100:8080; server 192.168.1.101:8080; }
server { listen 80; server_name www.example.com; location ~ .*\.(jpg|jpeg|png|gif|css|js|woff|woff2|ttf|svg)$ { root /data/www/static; expires 7d; access_log off; } location / { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
|
3.3 文件服务器场景
3.3.1 文件服务器架构
场景:
3.3.2 文件服务器配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| server { listen 80; server_name files.example.com; location /download/ { alias /data/files/; autoindex on; autoindex_exact_size off; autoindex_localtime on; limit_rate 1m; } location /upload/ { client_max_body_size 100m; proxy_pass http://upload_backend; } }
|
3.4 静态资源传输延迟最小化
3.4.1 优化策略
1. 启用sendfile:
2. 启用压缩:
3. 启用缓存:
4. CDN加速:
4. 静态资源配置语法
4.1 sendfile配置
4.1.1 sendfile原理
传统文件传输:
1
| 磁盘 → 内核缓冲区 → 用户缓冲区 → 内核缓冲区 → 网络
|
sendfile零拷贝:
优势:
4.1.2 sendfile配置语法
1 2 3
| Syntax: sendfile on | off; Default: sendfile off; Context: http, server, location, if in location
|
配置示例:
1 2 3 4 5 6 7 8 9 10 11 12
| http { sendfile on; server { listen 80; location / { sendfile on; root /data/www; } } }
|
注意事项:
- 需要内核支持
- 大文件传输效果明显
- 与aio配合使用效果更好
4.2 tcp_nopush配置
4.2.1 tcp_nopush原理
作用:
- 在sendfile开启情况下,提高网络包的传输效率
- 等待数据包填满后再发送(TCP_CORK)
- 减少网络包数量
4.2.2 tcp_nopush配置语法
1 2 3
| Syntax: tcp_nopush on | off; Default: tcp_nopush off; Context: http, server, location
|
配置示例:
1 2 3 4 5 6 7 8 9 10 11
| http { sendfile on; tcp_nopush on; server { listen 80; location / { root /data/www; } } }
|
工作原理:
- 启用TCP_CORK选项
- 等待数据包填满MSS(Maximum Segment Size)
- 减少网络包数量,提高传输效率
4.3 tcp_nodelay配置
4.3.1 tcp_nodelay原理
作用:
- 在keepalive连接下,提高网络的传输实时性
- 禁用Nagle算法
- 立即发送数据包
4.3.2 tcp_nodelay配置语法
1 2 3
| Syntax: tcp_nodelay on | off; Default: tcp_nodelay on; Context: http, server, location
|
配置示例:
1 2 3 4 5 6 7 8 9 10
| http { tcp_nodelay on; server { listen 80; location / { root /data/www; } } }
|
Nagle算法:
- 等待数据包填满或超时后发送
- 减少网络包数量
- 但会增加延迟
tcp_nodelay:
4.4 综合配置示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| http { sendfile on; tcp_nopush on; tcp_nodelay on; open_file_cache max=1000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; server { listen 80; server_name static.example.com; location / { root /data/www; index index.html; } } }
|
5. 静态资源文件压缩
5.1 gzip压缩概述
5.1.1 压缩原理
gzip压缩:
- 使用gzip算法压缩响应内容
- 减少传输数据量
- 提高传输速度
压缩效果:
- HTML:60-80%压缩率
- CSS:60-70%压缩率
- JS:50-70%压缩率
- JSON:60-80%压缩率
- 图片:压缩率低(已压缩)
5.1.2 压缩优势
优势:
劣势:
5.2 gzip基础配置
5.2.1 gzip开关
1 2 3
| Syntax: gzip on | off; Default: gzip off; Context: http, server, location, if in location
|
配置示例:
1 2 3 4 5 6 7 8 9 10 11 12
| http { gzip on; server { listen 80; location / { gzip on; root /data/www; } } }
|
5.2.2 gzip压缩类型
1 2 3
| Syntax: gzip_types mime-type ...; Default: gzip_types text/html; Context: http, server, location
|
配置示例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| http { gzip on; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/xhtml+xml image/svg+xml; }
|
常用MIME类型:
text/html:HTML文件
text/css:CSS文件
text/javascript:JavaScript文件
application/json:JSON数据
application/javascript:JS文件
application/xml:XML文件
5.3 gzip压缩级别
5.3.1 压缩级别配置
1 2 3
| Syntax: gzip_comp_level level; Default: gzip_comp_level 1; Context: http, server, location
|
级别说明:
1:最快压缩,压缩率低
2-6:平衡压缩速度和压缩率
7-9:最高压缩率,速度慢
推荐配置:
1 2 3 4
| http { gzip on; gzip_comp_level 6; }
|
5.3.2 压缩级别对比
| 级别 |
压缩率 |
速度 |
CPU消耗 |
推荐场景 |
| 1 |
低 |
快 |
低 |
高并发场景 |
| 3-4 |
中 |
中 |
中 |
一般场景 |
| 6 |
高 |
中 |
中 |
推荐 |
| 9 |
最高 |
慢 |
高 |
低并发场景 |
5.4 gzip协议版本
5.4.1 HTTP版本配置
1 2 3
| Syntax: gzip_http_version 1.0 | 1.1; Default: gzip_http_version 1.1; Context: http, server, location
|
配置示例:
1 2 3 4
| http { gzip on; gzip_http_version 1.1; }
|
版本说明:
1.0:HTTP/1.0协议
1.1:HTTP/1.1协议(主流)
5.5 gzip其他配置
5.5.1 最小压缩长度
1 2 3
| Syntax: gzip_min_length length; Default: gzip_min_length 20; Context: http, server, location
|
配置示例:
1 2 3 4
| http { gzip on; gzip_min_length 1000; }
|
说明:
- 小文件压缩效果不明显
- 压缩本身有开销
- 建议设置最小长度
5.5.2 禁用压缩的User-Agent
1 2 3
| Syntax: gzip_disable regex ...; Default: — Context: http, server, location
|
配置示例:
1 2 3 4
| http { gzip on; gzip_disable "msie6"; }
|
原因:
5.5.3 压缩缓冲区
1 2 3
| Syntax: gzip_buffers number size; Default: gzip_buffers 32 4k|16 8k; Context: http, server, location
|
配置示例:
1 2 3 4
| http { gzip on; gzip_buffers 16 8k; }
|
5.6 gzip_static预压缩
5.6.1 gzip_static原理
作用:
- 使用预压缩的.gz文件
- 减少实时压缩CPU消耗
- 提高响应速度
工作方式:
- 请求
style.css
- 查找
style.css.gz
- 如果存在,直接返回
- 如果不存在,实时压缩
5.6.2 gzip_static配置
1 2 3
| Syntax: gzip_static on | off | always; Default: gzip_static off; Context: http, server, location
|
配置示例:
1 2 3 4 5 6 7 8 9 10
| http { gzip_static on; server { listen 80; location / { root /data/www; } } }
|
参数说明:
on:优先使用预压缩文件,不存在则实时压缩
off:不使用预压缩文件
always:只使用预压缩文件,不存在则返回404
预压缩文件准备:
1 2 3 4 5 6
| gzip -k style.css
find /data/www -type f -name "*.css" -exec gzip -k {} \; find /data/www -type f -name "*.js" -exec gzip -k {} \;
|
5.7 压缩实战案例
5.7.1 图片压缩案例
配置示例:
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
| server { listen 80; server_name 192.168.56.11; sendfile on; access_log /var/log/nginx/static_access.log main; location ~ .*\.(jpg|gif|png)$ { gzip on; gzip_http_version 1.1; gzip_comp_level 2; gzip_types text/plain application/json application/x-javascript application/css application/xml application/xml+rss text/javascript application/x-httpd-php image/jpeg image/gif image/png; root /soft/code/images; } }
|
注意事项:
- 图片文件通常已压缩
- 压缩效果不明显
- 可能增加CPU消耗
- 建议只压缩未压缩的图片格式
5.7.2 文件压缩案例
配置示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| server { listen 80; server_name 192.168.56.11; sendfile on; access_log /var/log/nginx/static_access.log main; location ~ .*\.(txt|xml)$ { gzip on; gzip_http_version 1.1; gzip_comp_level 1; gzip_types text/plain application/json application/x-javascript application/css application/xml application/xml+rss text/javascript application/x-httpd-php; root /soft/code/doc; } }
|
压缩效果对比:
未启用gzip:
1 2
| Content-Length: 10240 bytes 传输时间: 100ms
|
启用gzip:
1 2
| Content-Length: 2560 bytes (压缩率75%) 传输时间: 30ms
|
5.7.3 完整压缩配置
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 30 31 32 33 34 35 36 37 38 39 40 41
| http { gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_min_length 1000; gzip_http_version 1.1; gzip_buffers 16 8k; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/xhtml+xml application/x-font-ttf application/vnd.ms-fontobject font/opentype image/svg+xml image/x-icon; gzip_disable "msie6"; server { listen 80; server_name www.example.com; location / { root /data/www; gzip_static on; } } }
|
6. 静态资源浏览器缓存
6.1 浏览器缓存机制
6.1.1 缓存流程
无缓存流程:
1
| 浏览器请求 → 无缓存 → 请求Web服务器 → 请求响应 → 呈现
|
有缓存流程:
1
| 浏览器请求 → 有缓存 → 校验过期 → 是否有更新 → 呈现
|
6.1.2 缓存校验机制
1. Expires(HTTP/1.0):
- 绝对时间
- 服务器返回过期时间
- 浏览器比较当前时间和过期时间
2. Cache-Control(HTTP/1.1):
- 相对时间
max-age:缓存有效期(秒)
no-cache:需要重新验证
no-store:不缓存
public:公共缓存
private:私有缓存
3. ETag:
- 文件内容哈希值
- 服务器生成
- 浏览器发送
If-None-Match头
- 服务器比较ETag,相同返回304
4. Last-Modified:
- 文件最后修改时间
- 服务器返回
- 浏览器发送
If-Modified-Since头
- 服务器比较时间,未修改返回304
6.2 expires配置
6.2.1 expires语法
1 2 3 4
| Syntax: expires [modified] time; expires epoch | max | off; Default: expires off; Context: http, server, location, if in location
|
参数说明:
time:时间值(如:1h、1d、1w、1m、1y)
modified:基于文件修改时间
epoch:不缓存
max:最大缓存(10年)
off:禁用缓存
6.2.2 expires配置示例
基础配置:
1 2 3 4
| location ~ .*\.(js|css|html)$ { root /soft/code/js; expires 1h; }
|
不同资源不同缓存时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| location ~ .*\.(js|css|html)$ { root /soft/code/js; expires 1h; add_header Cache-Control "public"; }
location ~ .*\.(jpg|gif|png)$ { root /soft/code/images; expires 7d; add_header Cache-Control "public, immutable"; }
location ~ .*\.(woff|woff2|ttf|eot)$ { root /soft/code/fonts; expires 30d; add_header Cache-Control "public, immutable"; }
|
基于修改时间:
1 2 3 4
| location ~ .*\.(js|css)$ { root /soft/code; expires modified 1h; }
|
6.3 Cache-Control配置
6.3.1 手动设置Cache-Control
1 2 3 4 5 6 7
| location ~ .*\.(js|css|html)$ { root /soft/code; add_header Cache-Control "public, max-age=3600"; expires 1h; }
|
Cache-Control值说明:
public:公共缓存,CDN可以缓存
private:私有缓存,只有浏览器可以缓存
max-age=3600:缓存3600秒(1小时)
immutable:内容不变,不需要重新验证
no-cache:需要重新验证
no-store:不缓存
6.3.2 不同场景的缓存策略
长期缓存(版本化资源):
1 2 3 4 5 6
| location ~ .*\.(js|css)$ { root /soft/code; expires max; add_header Cache-Control "public, immutable"; }
|
短期缓存(动态内容):
1 2 3 4 5 6
| location ~ .*\.(html|htm)$ { root /soft/code; expires 1h; add_header Cache-Control "public, max-age=3600"; }
|
不缓存(开发环境):
1 2 3 4 5 6 7
| location ~ .*\.(css|js|swf|json|mp4|htm|html)$ { root /soft/code; add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header Pragma "no-cache"; expires -1; }
|
6.4 ETag和Last-Modified
6.4.1 ETag配置
Nginx默认启用ETag,无需额外配置。
ETag工作原理:
- 服务器返回
ETag: "5a1e2678-2a8eb"
- 浏览器下次请求发送
If-None-Match: "5a1e2678-2a8eb"
- 服务器比较ETag,相同返回304 Not Modified
禁用ETag:
1 2 3 4
| location / { root /soft/code; etag off; }
|
6.4.2 Last-Modified配置
Nginx默认启用Last-Modified,无需额外配置。
Last-Modified工作原理:
- 服务器返回
Last-Modified: Wed, 29 Nov 2017 03:16:08 GMT
- 浏览器下次请求发送
If-Modified-Since: Wed, 29 Nov 2017 03:16:08 GMT
- 服务器比较时间,未修改返回304 Not Modified
禁用Last-Modified:
1 2 3 4
| location / { root /soft/code; if_modified_since off; }
|
6.5 缓存最佳实践
6.5.1 缓存策略建议
| 资源类型 |
缓存时间 |
Cache-Control |
说明 |
| HTML |
1小时 |
public, max-age=3600 |
内容可能变化 |
| CSS/JS(版本化) |
10年 |
public, immutable |
带版本号,内容不变 |
| CSS/JS(非版本化) |
1天 |
public, max-age=86400 |
内容可能变化 |
| 图片 |
7-30天 |
public, immutable |
内容不变 |
| 字体 |
30天 |
public, immutable |
内容不变 |
| API响应 |
不缓存 |
no-cache |
动态内容 |
6.5.2 完整缓存配置示例
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| http { open_file_cache max=1000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; server { listen 80; server_name www.example.com; root /data/www; location ~ .*\.(html|htm)$ { expires 1h; add_header Cache-Control "public, max-age=3600"; } location ~ .*\.(css|js)$ { expires max; add_header Cache-Control "public, immutable"; access_log off; } location ~ .*\.(jpg|jpeg|png|gif|webp|svg)$ { expires 7d; add_header Cache-Control "public, immutable"; access_log off; } location ~ .*\.(woff|woff2|ttf|eot)$ { expires 30d; add_header Cache-Control "public, immutable"; access_log off; } location /api/ { proxy_pass http://backend; add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; expires -1; } } }
|
7. 静态资源跨域访问
7.1 跨域访问概述
7.1.1 同源策略
同源策略:
- 协议相同(http/https)
- 域名相同(example.com)
- 端口相同(80/443)
跨域场景:
- 不同域名:
www.example.com → api.example.com
- 不同协议:
http://example.com → https://example.com
- 不同端口:
example.com:80 → example.com:8080
7.1.2 跨域访问限制
浏览器限制:
- 禁止跨域AJAX请求
- 禁止跨域Cookie访问
- 禁止跨域DOM操作
安全原因:
7.2 CORS配置
1 2 3
| Syntax: add_header name value [always]; Default: — Context: http, server, location, if in location
|
参数说明:
name:响应头名称
value:响应头值
always:总是添加(即使状态码不是2xx、3xx)
7.2.2 基础CORS配置
允许所有域名:
1 2 3 4 5 6 7 8
| location / { root /soft/code; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "Content-Type, Authorization"; }
|
允许指定域名:
1 2 3 4 5 6 7 8
| location / { root /soft/code; add_header Access-Control-Allow-Origin http://www.example.com; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "Content-Type, Authorization"; }
|
7.2.3 动态CORS配置
根据请求来源动态设置:
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
| map $http_origin $cors_origin { default ""; "~^https?://(www\.)?example\.com$" $http_origin; "~^https?://(www\.)?test\.com$" $http_origin; }
server { listen 80; server_name api.example.com; location / { root /soft/code; add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; add_header Access-Control-Allow-Headers "Content-Type, Authorization" always; add_header Access-Control-Allow-Credentials "true" always; if ($request_method = 'OPTIONS') { return 204; } } }
|
7.3 OPTIONS预检请求
7.3.1 预检请求说明
简单请求(不需要预检):
- GET、HEAD、POST方法
- 简单请求头(Content-Type: text/plain等)
复杂请求(需要预检):
- PUT、DELETE等方法
- 自定义请求头
- Content-Type: application/json
7.3.2 预检请求处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| location / { root /soft/code; add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With" always; add_header Access-Control-Allow-Credentials "true" always; add_header Access-Control-Max-Age "3600" always; if ($request_method = 'OPTIONS') { return 204; } }
|
7.4 跨域实战案例
7.4.1 准备HTML文件
测试页面:
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
| <html lang="en"> <head> <meta charset="UTF-8" /> <title>测试ajax和跨域访问</title> <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> </head> <script type="text/javascript"> $(document).ready(function(){ $.ajax({ type: "GET", url: "http://kt.xuliangwei.com/index.html", success: function(data) { alert("success!!!"); }, error: function() { alert("fail!!,请刷新再试!"); } }); }); </script> <body> <h1>测试跨域访问</h1> </body> </html>
|
7.4.2 配置Nginx跨域访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| server { listen 80; server_name kt.xuliangwei.com; sendfile on; access_log /var/log/nginx/kuayue.log main; location ~ .*\.(html|htm)$ { add_header Access-Control-Allow-Origin http://www.xuliangwei.com; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "Content-Type, Authorization"; root /soft/code; } }
|
测试结果:
- 未配置CORS:浏览器控制台报错,AJAX请求失败
- 配置CORS后:AJAX请求成功,可以正常访问
7.5 CORS最佳实践
7.5.1 安全建议
1. 限制允许的域名:
1 2
| add_header Access-Control-Allow-Origin http://www.example.com;
|
2. 限制允许的方法:
1 2
| add_header Access-Control-Allow-Methods "GET, POST";
|
3. 限制允许的请求头:
1 2
| add_header Access-Control-Allow-Headers "Content-Type";
|
4. 启用凭据时指定域名:
1 2 3
| add_header Access-Control-Allow-Origin http://www.example.com; add_header Access-Control-Allow-Credentials "true";
|
7.5.2 完整CORS配置
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
| map $http_origin $allow_origin { default ""; "~^https?://(www\.)?example\.com$" $http_origin; "~^https?://(.*\.)?example\.com$" $http_origin; }
server { listen 80; server_name api.example.com; location / { root /soft/code; add_header Access-Control-Allow-Origin $allow_origin always; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always; add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With" always; add_header Access-Control-Allow-Credentials "true" always; add_header Access-Control-Max-Age "3600" always; if ($request_method = 'OPTIONS') { return 204; } } }
|
8. 静态资源防盗链
8.1 防盗链概述
8.1.1 盗链定义
盗链:
- 在自己的界面展示不在自己服务器上的内容
- 通过技术手段获得他人服务器的资源地址
- 绕过别人资源展示页面
- 在自己页面向用户提供此内容
- 减轻自己服务器的负担,但消耗他人服务器资源
盗链危害:
- 消耗服务器带宽
- 增加服务器负载
- 影响正常用户访问
- 可能涉及版权问题
8.1.2 防盗链原理
防盗链思路:
- 区别哪些请求是非正常用户请求
- 基于HTTP Referer头判断
- 允许的Referer:正常访问
- 不允许的Referer:拒绝访问
8.2 valid_referers配置
8.2.1 valid_referers语法
1 2 3
| Syntax: valid_referers none | blocked | server_names | string ...; Default: — Context: server, location
|
参数说明:
none:允许没有Referer头的请求
blocked:允许Referer头被防火墙或代理服务器删除的请求
server_names:允许server_name匹配的域名
string:允许的域名或正则表达式
8.2.2 基础防盗链配置
只允许指定域名:
1 2 3 4 5 6 7 8 9
| location ~ .*\.(jpg|gif|png)$ { valid_referers none blocked www.example.com *.example.com; if ($invalid_referer) { return 403; } root /soft/code/images; }
|
允许空Referer(直接访问):
1 2 3 4 5 6 7 8 9
| location ~ .*\.(jpg|gif|png)$ { valid_referers none blocked www.example.com; if ($invalid_referer) { return 403; } root /soft/code/images; }
|
8.2.3 正则表达式防盗链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| location ~ .*\.(jpg|gif|png)$ { valid_referers none blocked www\.example\.com ~\.example\.com$ ~^https?://.*\.example\.com; if ($invalid_referer) { return 403; } root /soft/code/images; }
|
8.3 防盗链实战案例
8.3.1 准备HTML文件
盗链测试页面:
1 2 3 4 5 6 7 8 9 10
| <html> <head> <meta charset="utf-8"> <title>盗链测试</title> </head> <body style="background-color:red;"> <img src="http://192.168.69.113/test.jpg"> </body> </html>
|
8.3.2 配置防盗链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| server { listen 80; server_name 192.168.69.113; location ~ .*\.(jpg|gif|png)$ { valid_referers none blocked www.xuliangwei.com *.xuliangwei.com; if ($invalid_referer) { return 403; } root /soft/code/images; } }
|
8.3.3 验证防盗链
1. 正常访问(允许的Referer):
1 2 3 4 5 6 7 8
| curl -e "http://www.xuliangwei.com" -I http://192.168.69.113/test.jpg
HTTP/1.1 200 OK Server: nginx/1.12.2 Content-Type: image/jpeg Content-Length: 174315
|
2. 盗链访问(不允许的Referer):
1 2 3 4 5 6 7 8
| curl -e "http://www.baidu.com" -I http://192.168.69.113/test.jpg
HTTP/1.1 403 Forbidden Server: nginx/1.12.2 Content-Type: text/html Content-Length: 169
|
3. 直接访问(无Referer):
1 2 3 4 5
| curl -I http://192.168.69.113/test.jpg
|
8.4 防盗链高级配置
8.4.1 返回自定义错误页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| location ~ .*\.(jpg|gif|png)$ { valid_referers none blocked www.example.com; if ($invalid_referer) { return 403; } root /soft/code/images; }
error_page 403 /403.html; location = /403.html { root /soft/code/errors; }
|
8.4.2 返回默认图片
1 2 3 4 5 6 7 8 9 10
| location ~ .*\.(jpg|gif|png)$ { valid_referers none blocked www.example.com; if ($invalid_referer) { rewrite ^(.*)$ /default.jpg break; } root /soft/code/images; }
|
8.4.3 白名单和黑名单结合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| location ~ .*\.(jpg|gif|png)$ { set $allow_referer 0; if ($http_referer ~* "^https?://(www\.)?example\.com") { set $allow_referer 1; } if ($http_referer ~* "^https?://(www\.)?baidu\.com") { set $allow_referer 0; } if ($http_referer = "") { set $allow_referer 1; } if ($allow_referer = 0) { return 403; } root /soft/code/images; }
|
8.5 防盗链最佳实践
8.5.1 配置建议
1. 允许空Referer:
- 用户直接访问图片URL
- 某些浏览器可能不发送Referer
- 配置
none允许直接访问
2. 使用正则表达式:
3. 结合日志分析:
8.5.2 完整防盗链配置
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 30 31 32 33 34 35 36 37 38 39 40
| server { listen 80; server_name static.example.com; location ~ .*\.(jpg|jpeg|png|gif|webp)$ { valid_referers none blocked www\.example\.com ~\.example\.com$ ~^https?://.*\.example\.com; if ($invalid_referer) { access_log /var/log/nginx/hotlink.log; return 403; } root /data/images; expires 7d; add_header Cache-Control "public, immutable"; } location ~ .*\.(mp4|flv|avi)$ { valid_referers none blocked www.example.com; if ($invalid_referer) { return 403; } root /data/videos; expires -1; add_header Cache-Control "no-cache"; } }
|
9. 综合实战案例
9.1 完整静态资源服务器配置
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| http { sendfile on; tcp_nopush on; tcp_nodelay on; open_file_cache max=1000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_min_length 1000; gzip_http_version 1.1; gzip_buffers 16 8k; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/xhtml+xml image/svg+xml; gzip_disable "msie6"; server { listen 80; server_name static.example.com; root /data/www; location ~ .*\.(html|htm)$ { expires 1h; add_header Cache-Control "public, max-age=3600"; add_header Access-Control-Allow-Origin *; } location ~ .*\.(css|js)$ { expires max; add_header Cache-Control "public, immutable"; access_log off; gzip_static on; } location ~ .*\.(jpg|jpeg|png|gif|webp|svg)$ { expires 7d; add_header Cache-Control "public, immutable"; access_log off; valid_referers none blocked www.example.com *.example.com; if ($invalid_referer) { return 403; } } location ~ .*\.(woff|woff2|ttf|eot)$ { expires 30d; add_header Cache-Control "public, immutable"; access_log off; add_header Access-Control-Allow-Origin *; } location ~ .*\.(mp4|flv|avi)$ { expires -1; add_header Cache-Control "no-cache"; valid_referers none blocked www.example.com; if ($invalid_referer) { return 403; } } } }
|
9.2 CDN节点配置
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 30 31 32 33 34
| http { sendfile on; tcp_nopush on; tcp_nodelay on; gzip on; gzip_comp_level 6; gzip_types text/plain text/css application/json application/javascript; server { listen 80; server_name cdn.example.com; location / { root /data/cdn; expires 30d; add_header Cache-Control "public, immutable"; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS"; valid_referers none blocked *.example.com; if ($invalid_referer) { return 403; } } } }
|
10. 总结
10.1 核心要点
- 静态资源类型:图片、CSS、JS、字体、文档等
- 静态资源场景:CDN、动静分离、文件服务器
- 配置语法:sendfile、tcp_nopush、tcp_nodelay
- 文件压缩:gzip配置、压缩级别、压缩类型
- 浏览器缓存:Expires、Cache-Control、ETag、Last-Modified
- 跨域访问:CORS配置、OPTIONS预检请求
- 防盗链:valid_referers、Referer验证
10.2 架构师建议
性能优化:
- 启用sendfile零拷贝
- 启用gzip压缩
- 合理设置缓存时间
- 使用CDN加速
安全配置:
缓存策略:
- 版本化资源长期缓存
- 动态内容短期缓存
- 开发环境禁用缓存
10.3 最佳实践
- 标准化:统一静态资源配置标准
- 自动化:自动化压缩、缓存配置
- 监控化:监控静态资源访问情况
- 文档化:维护配置文档和架构图
相关文章: