> 前置学习要点:
- 输入url打开网页、ajax获取数据、img标签加载图片
- cache-control:max-age、public、private、must-revalidate、no-cache、no-store
- 缓存验证(缓存是存在客户端的,客户端不知道服务端是否有改变):last-modified配合if-modified-since、etag配合if-none-match
缓存是对web性能提升最大的一环,深入理解http缓存 - 更多有意义的头:Content-type、Content-Encoding等用来约束数据类型、Cookie保持会话信息、CORS实现跨域并保持安全限制
- 深入到TCP:什么是三次握手、HTTPS链接的过程,以及为什么HTTPS就是安全的、什么是长链接,为什么需要长链接、HTTP2的信道复用为什么能提高性能
> 缓存:
- 区分浏览器缓存和代理缓存
- 缓存Cache-Control:
- public:所有用户都可以缓存
- private:指定用户可以缓存
- max-age:缓存保存时间
-
no-cache:每次都必须向服务器校验,才能判断是否可以用缓存。即使此时max-age值大于0,浏览器也必须先向服务器发送请求进行校验。客户端首次请求时,服务器会向客户端返回
Etag
或者Last-Modified
,客户端接收到数据之后,会把这两个数据保存下来,客户端再次请求的时候,请求头会自动携带If-Modified-Since
或者If-None-Match
字段,服务器根据接受到的数据,判断是否过期,如果不过期返回Code:304,通知客户端直接去缓存的数据,否则返回新的数据。
if (request.url === '/script.js') {
const etag = request.headers['if-none-match']
if (etag === '777') {
response.writeHead(304, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=2000000, no-cache',
'Last-Modified': '123',
'Etag': '777'
})
response.end()
} else {
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=2000000, no-cache',
'Last-Modified': '123',
'Etag': '777'
})
response.end('console.log("script loaded twice")')
}
}
image.png
- must-revalidate:告诉浏览器、缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验。
- proxy-revalidate:指令要求所有的缓存服务器在接收到客户端带有该指令的请求返回响应之前,必须再次验证缓存的有效性。
- no-transform:使用 no-transform 指令规定无论是在请求还是响应中,缓存都不能改变实体主体的媒体类型。这样做可防止缓存或代理压缩图片等类似操作。
Nginx缓存模块proxy_cache_path使用及说明
- nginx缓存配置
proxy_cache_path
需要和s-maxage
一起使用
// 首次请求,访问服务器,二次请求(本地缓存十秒以内),获取本地缓存;超过十秒小于二十秒,请求代理服务器(nginx)缓存,超过二十秒,再次请求服务器,以此循环下去
response.writeHead(200, {
'Cache-Control': 'max-age=10, s-maxage=20, public',
})
// public和private的区别,private只有浏览器才能缓存,十秒之后,直接请求服务器跳过代理服务器缓存
response.writeHead(200, {
'Cache-Control': 'max-age=10, s-maxage=20, private',
})
// 本地和代理服务器都不缓存
response.writeHead(200, {
'Cache-Control': 'max-age=10, s-maxage=20, no-store',
})
- 验证缓存(Vary)
验证不通过,本地和代理服务器缓存都不执行,每次请求直接请求服务器;
用来区分缓存: user-agent、language
response.writeHead(200, {
'Cache-Control': 'max-age=10, s-maxage=20, public',
'Vary': 'X-Test-Cache'
})
<script>
var index = 0
function doRequest () {
var data = document.getElementById('data')
data.innerText = ''
fetch('/data', {
headers: {
'X-Test-Cache': index++
}
}).then(function (resp) {
return resp.text()
}).then(function (text) {
data.innerText = text
})
}
document.getElementById('button').addEventListener('click', doRequest)
</script>
> HTTP协议:
请求报文和相应报文(起始行、首部、主体 )> HTTP方法:
- 用来定义对于资源的操作
- 常用有GET、POST等
- 从定义上讲有各自的语义
> HTTP CODE
- 定义服务器对请求的处理结果
- Chrome插件 HostAdmin Host Editor
- 各个区间的CODE有各自的语义(如301、302、304)
- 好的HTTP服务可以通过CODE判断结果
- rediret:(只有300的头才代表要跳转)
301和302区别: 301是永久跳转(需要慎重使用,浏览器会记录跳转历史,下次想更改,就需要客户端去清缓存,但是这种行为不实际,用户这么多,所以这个方法需要慎重),302临时跳转
if (request.url === '/') {
response.writeHead(302, { // or 301
'Location': '/new' // 同域名跳转可以不用加域名, /new ==> localhost:88888/new'
})
response.end()
}
if (request.url === '/new') {
response.writeHead(200, {
'Content-Type': 'text/html',
})
response.end('<div>this is content</div>')
}
- 这里的键值对格式用 = 号分割,注意不要用 : 号
response.writeHeader(200, {
'Content-Type' : 'text/plain;charset=UTF-8'
});
> 长链接:
- http1.0链接默认为短链接,即每次客户端跟服务端进行一次http请求,都需要创建一次TCP链接,一次请求结束之后,即关闭TCP链接,因此每次http请求都会造成tcp创建和关闭的性能消耗。
- 从http1.1开始,http请求默认为长链接,不同浏览器,一个域名下可以同时开启的tcp数量也不一样,在谷歌浏览器内,最多可以开启6个tcp。即如果同时超过6次请求,其余的请求(第7个、8个...请求),依序等待上面6次请求结束之后,再复用tcp通道。
- 开启和关闭长链接:
response.writeHead(200, {
'Content-Type': 'image/jpg',
'Connection': 'keep-alive' // or close
})
> http2.0
优势: 信道复用、分帧传输、sever Push
通过观察,可以发现在谷歌的域名下,只打开了一个tcp
http2.0参考:
- 详解 HTTP/2 Server Push——进一步提升页面加载速度
- demo
- http2实验室
-
关于 http2 下 push 接口请求的正确姿势
nginx配置http2:
建议通过nginx配置http2.0,相比通过后端配置http2.0,nginx配置http2.0会简单很多。
listen 443 http2;
http2_push_preload on;
> 跨域:
- 跨域的解决方法有多种,常见的有:
- 前端通过jsonp跨域,但是缺点是只能get请求;
- (后端服务器java、php或者代理服务器node、nginx)设置cors;
- nginx反向代理,将不同端口的请求代理到同一端口下;
- node.js负责中间层处理业务逻辑,将不同域名下的接口在node.js层请求回来进行处理之后,再返回给客户端。
- cors的限制:
- cors默认只支持GET、POST、HEAD方法,如果需要支持PUT、DELETE等方法,需要在请求头
'Access-Control-Allow-Methods
声明; - 添加自定义声明的请求头字段,需要在请求头
Access-Control-Allow-Headers
声明; - 在cors跨域时,浏览器和服务端进行http请求会有两次请求,第一次请求为预检查检响应(OPTION),第二次才是真正的请求。设置
Access-Control-Max-Age
可以告诉客户端在这个时间内,从第二次请求开始,可以不用进行预检查(OPTION); - Content-Type默认只支持以下MIME类型
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
response.writeHead(200, {
'Access-Control-Allow-Origin': '*', // Access-Control-Allow-Origin: http://127.0.0.1:8888
'Access-Control-Allow-Headers': 'X-Test-Cors,Content-Type', // Content-Type用于解决Content-Type不支持application/json等MIME类型
'Access-Control-Allow-Methods': 'POST, PUT, DELETE',
'Access-Control-Max-Age': '1000'
})
预检查
第二次才是真正的请求
不支持application/json报错提示
nginx解决跨域问题
cors官方文档
fetch能不能关闭post请求前的options请求?
Nginx配置跨域请求 Access-Control-Allow-Origin *
> Cookie属性:
- cookie存在失效 默认不设置 则是关闭窗口之后 消失
- max-age和expires设置过期时间(两者的区别)
var millisecond = new Date().getTime();
var expiresTime = new Date(millisecond + 60 * 1000 * 15);
response.writeHead(200, {
'Content-Type': 'text/html',
'Set-Cookie': ['id=123; expires=expiresTime ', 'abc=456;domain=test.com']
})
response.writeHead(200, {
'Content-Type': 'text/html',
'Set-Cookie': ['id=123; max-age= 5; HttpOnly', 'abc=456;domain=royluck.com;Secure'] // 5秒后失效
})
如果expires无效,记得留意下服务器的时间是否正确的
- Secure只在https的时候发送
- HttpOnly无法通过document.cookie访问
- domain:指定域名或者二级域名共享cookie
如:子域名a.test.com 能获取到主域名test.com的cookie
> CSP(内容安全与策略):
背景:避免例如:XSS攻击 -> 注入脚本 <- 通过富文本
- 作用:
- 限制资源获取;
- 报告资源越权;
- 限制方式:
- default-src (限制全局跟链接有关系的资源)
- 制定资源类型:
connect-src、img-src、manifest-src、font-src、media-src、style-src、frame-src、script-src
内容安全策略
Content Security Policy 入门教程
<body>
<script>
console.log('inline js')
</script>
<script src="test.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/core.js"></script>
</body>
- 禁止内联script执行脚本
// 支持外联js,禁止内联script执行脚本
response.writeHead(200, {
'Content-Type': 'text/html',
'Content-Security-Policy': 'default-src http: https'
})
// 禁止内联script执行脚本,外联js必须同域
response.writeHead(200, {
'Content-Type': 'text/html',
// Content-Security-Policy: script-src \self\ 全局限制,img、script都不能引用外链
'Content-Security-Policy': 'script-src \'self\' '
// Content-Security-Policy: script-src \self\ https://cdn.bootcss.com/ 支持该域名的外联域名
})
- 发送report请求
response.writeHead(200, {
'Content-Type': 'text/html',
'Content-Security-Policy': ' report-uri /report' // /report为服务器url地址
// Content-Security-Policy-Report-Only: report-uri /report // 向服务端报告,但是前端还是能继续加载执行
})
- form-action限制form表单提交地址
<from action="http://baidu.com">
<button type="submit">提交</button>
</from>
// 限制form表单提交域名(不跳转百度网站)
response.writeHead(200, {
'Content-Type': 'text/html',
// 上述from 点击提交不会跳转百度网站
'Content-Security-Policy': ' form-action \'self\''
})
- 虽然也可以通过前端meta标签进行CSP配置,但是还是建议统一后端通过http header控制
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; form-action 'self';">
- connect-src限制ajax请求
<body>
<div>This is content</div>
<script>
fetch("https://www.baidu.com")
</script>
</body>
response.writeHead(200, {
'Content-Type': 'text/html',
'Access-Control-Allow-Origin': '*',
'Content-Security-Policy': 'connect-src \'self\''
})
> nginx:
- 纯粹的http协议的工具(不做业务)
- 其他语言扩展功能
- 开启停止nginx:
start nginx
、nginx -s stop
- 主要功能:1.代理功能; 2.缓存功能;
- 配置文件:nginx.conf
- 运营商相当于一个代理服务器,早期通过http协议,拦截修改body载入广告
# nginx.conf配置文件
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
...
}
include server.conf; # include导入配置
...
}
# server.conf配置文件
proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;
server {
listen 80;
server_name royluck.com; # 匹配域名
location / {
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
proxy_cache my_cache;
}
}
server {
listen 80;
server_name child.royluck.com;
location / {
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
proxy_cache my_cache;
}
}
- 代理设置头:proxy-set-header Host $host;
http.createServer(function (request, response) {
// 获取 sever_name 域名
// 没配置代理设置头: 127.0.0.1:8888
// 配置了代理设置头: royluck.com
console.log('request host', request.headers['host'])
if (request.url === '/') {
response.writeHead(200, {
'Content-Type': 'text/html',
})
response.end(html)
}
}).listen(8888)
proxy_cache_path配置
Nginx缓存模块proxy_cache_path使用及说明
- nginx缓存配置
proxy_cache_path
需要和s-maxage
一起使用
// 首次请求,访问服务器,二次请求(本地缓存十秒以内),获取本地缓存;超过十秒小于二十秒,请求代理服务器(nginx)缓存,超过二十秒,再次请求服务器,以此循环下去
response.writeHead(200, {
'Cache-Control': 'max-age=10, s-maxage=20, public',
})
// public和private的区别,private只有浏览器才能缓存,十秒之后,直接请求服务器跳过代理服务器缓存
response.writeHead(200, {
'Cache-Control': 'max-age=10, s-maxage=20, private',
})
// 本地和代理服务器都不缓存
response.writeHead(200, {
'Cache-Control': 'max-age=10, s-maxage=20, no-store',
})
- 验证缓存(Vary)
验证不通过,本地和代理服务器缓存都不执行,每次请求直接请求服务器;
用来区分缓存: user-agent、language
response.writeHead(200, {
'Cache-Control': 'max-age=10, s-maxage=20, public',
'Vary': 'X-Test-Cache'
})
<script>
var index = 0
function doRequest () {
var data = document.getElementById('data')
data.innerText = ''
fetch('/data', {
headers: {
'X-Test-Cache': index++
}
}).then(function (resp) {
return resp.text()
}).then(function (text) {
data.innerText = text
})
}
document.getElementById('button').addEventListener('click', doRequest)
</script>
> https:
- 加密: 私匙、公匙
- 公匙 https三次握手
- https配置:
server {
listen 80;
server_name test.com;
return 302 https://$server_name$request_uri;
location / {
proxy_cache my_cache;
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
}
}
server {
listen 443 http2;
server_name test.com;
ssl on;
ssl_certificate_key ../certs/localhost-privkey.pem;
ssl_certificate ../certs/localhost-cert.pem;
location / {
proxy_cache my_cache;
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
}
}
> 据协商:
- 分类: 请求、返回
- Accept: Accept、Accept-Encoding、Accept-language、User-Agent
- Content:Content-type、Content-Encoding、Content-Language
- MIME 类型
- 'X-Content-Type-Options': 'nosniff'
// console.js文件
console.log('测试nosniff')
// server.js文件
const JSFile = fs.readFileSync('console.js')
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html)
}
if (request.url === '/script.js') {
response.writeHead(200, {
'Content-Type': 'application/javascript',
// Content-Type: text/xml, 报错:because its MIME type (text/xml) is not executable
// , And strict MIME type checking is enabled.
'X-Content-Type-Options': 'nosniff'
})
response.end(JSFile)
}
<!--test.html文件-->
<body>
<p>nosniff测试</p>
</body>
<script src="/script.js"></script>
response.writeHead(200, {
'Content-Type': 'text/html',
'Content-Encoding': 'gzip'
})
// response.end(zlib.gzipSync(html))
- from enctype只支持三种:multipart/form-data、text/plain、application/x-www-form-urlencoded
<form action="/form" id="form" enctype="application/x-www-form-urlencoded"></form>
!!!!!!!.jpg
!!!!!!.jpg
!!!!.jpg
网友评论