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静态服务:

  1. 静态资源类型:分类、特点、应用场景
  2. 静态资源场景:CDN、动静分离、文件服务器
  3. 静态资源配置语法:sendfile、tcp_nopush、tcp_nodelay
  4. 静态资源文件压缩:gzip压缩配置、压缩比率、压缩类型
  5. 静态资源浏览器缓存:Expires、Cache-Control、ETag、Last-Modified
  6. 静态资源跨域访问:CORS配置、跨域场景
  7. 静态资源防盗链: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 可缓存性

特点

  • 浏览器缓存
  • CDN缓存
  • 代理缓存

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. 启用压缩

  • gzip压缩
  • 减少传输数据量

3. 启用缓存

  • 浏览器缓存
  • 减少请求次数

4. CDN加速

  • 就近分发
  • 减少网络延迟

4. 静态资源配置语法

4.1 sendfile配置

4.1.1 sendfile原理

传统文件传输

1
磁盘 → 内核缓冲区 → 用户缓冲区 → 内核缓冲区 → 网络

sendfile零拷贝

1
磁盘 → 内核缓冲区 → 网络

优势

  • 减少数据拷贝次数
  • 减少CPU消耗
  • 提高传输效率

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; # location级别启用
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; # 配合sendfile使用

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

  • 禁用Nagle算法
  • 立即发送数据包
  • 提高实时性

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 压缩优势

优势

  • 减少带宽消耗
  • 提高传输速度
  • 改善用户体验

劣势

  • 增加CPU消耗
  • 压缩本身需要时间

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; # location级别启用
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; # 小于1KB不压缩
}

说明

  • 小文件压缩效果不明显
  • 压缩本身有开销
  • 建议设置最小长度

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"; # 禁用IE6压缩
}

原因

  • 某些旧浏览器不支持gzip
  • 避免压缩导致的问题

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; # 16个8KB缓冲区
}

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
# 压缩CSS文件
gzip -k style.css # 生成style.css.gz,保留原文件

# 批量压缩
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;

# 禁用压缩的User-Agent
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小时
}

不同资源不同缓存时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# JS、CSS、HTML缓存1小时
location ~ .*\.(js|css|html)$ {
root /soft/code/js;
expires 1h;
add_header Cache-Control "public";
}

# 图片缓存7天
location ~ .*\.(jpg|gif|png)$ {
root /soft/code/images;
expires 7d;
add_header Cache-Control "public, immutable";
}

# 字体文件缓存30天
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; # 基于文件修改时间+1小时
}

6.3 Cache-Control配置

6.3.1 手动设置Cache-Control

1
2
3
4
5
6
7
location ~ .*\.(js|css|html)$ {
root /soft/code;

# 设置Cache-Control头
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
# 带版本号的资源(如:style.v1.0.0.css)
location ~ .*\.(js|css)$ {
root /soft/code;
expires max; # 10年
add_header Cache-Control "public, immutable";
}

短期缓存(动态内容)

1
2
3
4
5
6
# HTML文件
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工作原理

  1. 服务器返回ETag: "5a1e2678-2a8eb"
  2. 浏览器下次请求发送If-None-Match: "5a1e2678-2a8eb"
  3. 服务器比较ETag,相同返回304 Not Modified

禁用ETag

1
2
3
4
location / {
root /soft/code;
etag off; # 禁用ETag
}

6.4.2 Last-Modified配置

Nginx默认启用Last-Modified,无需额外配置。

Last-Modified工作原理

  1. 服务器返回Last-Modified: Wed, 29 Nov 2017 03:16:08 GMT
  2. 浏览器下次请求发送If-Modified-Since: Wed, 29 Nov 2017 03:16:08 GMT
  3. 服务器比较时间,未修改返回304 Not Modified

禁用Last-Modified

1
2
3
4
location / {
root /soft/code;
if_modified_since off; # 禁用Last-Modified
}

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;

# HTML文件:短期缓存
location ~ .*\.(html|htm)$ {
expires 1h;
add_header Cache-Control "public, max-age=3600";
}

# CSS/JS文件:长期缓存(版本化)
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;
}

# API接口:不缓存
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.comapi.example.com
  • 不同协议:http://example.comhttps://example.com
  • 不同端口:example.com:80example.com:8080

7.1.2 跨域访问限制

浏览器限制

  • 禁止跨域AJAX请求
  • 禁止跨域Cookie访问
  • 禁止跨域DOM操作

安全原因

  • 防止CSRF攻击
  • 防止数据泄露
  • 保护用户隐私

7.2 CORS配置

7.2.1 add_header语法

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;

# 动态设置CORS
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;

# 处理OPTIONS预检请求
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;

# CORS头
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;

# 处理OPTIONS预检请求
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
<!-- /soft/code/http_origin.html -->
<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
# origin.conf
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;

# CORS配置
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;

# 处理OPTIONS预检请求
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文件 -->
<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)$ {
# 允许的Referer
valid_referers none blocked www.xuliangwei.com *.xuliangwei.com;

# 不允许的Referer返回403
if ($invalid_referer) {
return 403;
}

root /soft/code/images;
}
}

8.3.3 验证防盗链

1. 正常访问(允许的Referer)

1
2
3
4
5
6
7
8
# 使用允许的Referer访问
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
# 使用不允许的Referer访问
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
# 直接访问(无Referer)
curl -I http://192.168.69.113/test.jpg

# 如果配置了 none,允许访问
# 如果没配置 none,返回403

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;
# 或者返回自定义错误页面
# rewrite ^(.*)$ /403.html break;
}

root /soft/code/images;
}

# 自定义403错误页面
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;
}

# 空Referer处理
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)$ {
# 允许的Referer
valid_referers
none
blocked
www\.example\.com
~\.example\.com$
~^https?://.*\.example\.com;

# 不允许的Referer返回403
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压缩
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;

# HTML文件
location ~ .*\.(html|htm)$ {
expires 1h;
add_header Cache-Control "public, max-age=3600";

# CORS
add_header Access-Control-Allow-Origin *;
}

# CSS/JS文件(版本化)
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;

# CORS(字体文件需要跨域)
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 核心要点

  1. 静态资源类型:图片、CSS、JS、字体、文档等
  2. 静态资源场景:CDN、动静分离、文件服务器
  3. 配置语法:sendfile、tcp_nopush、tcp_nodelay
  4. 文件压缩:gzip配置、压缩级别、压缩类型
  5. 浏览器缓存:Expires、Cache-Control、ETag、Last-Modified
  6. 跨域访问:CORS配置、OPTIONS预检请求
  7. 防盗链:valid_referers、Referer验证

10.2 架构师建议

  1. 性能优化

    • 启用sendfile零拷贝
    • 启用gzip压缩
    • 合理设置缓存时间
    • 使用CDN加速
  2. 安全配置

    • 配置防盗链
    • 限制跨域访问
    • 记录访问日志
  3. 缓存策略

    • 版本化资源长期缓存
    • 动态内容短期缓存
    • 开发环境禁用缓存

10.3 最佳实践

  1. 标准化:统一静态资源配置标准
  2. 自动化:自动化压缩、缓存配置
  3. 监控化:监控静态资源访问情况
  4. 文档化:维护配置文档和架构图

相关文章