Referer 说「我是从哪个页面点过来的」。字段名少了一个 r——1996 年写错,只能一直错下去。
它有用,但会泄露 URL 里的查询串。
上一节是技术性的分段,这一节是个既实用又有隐私问题的字段。
一、那个拼写错误
正确英文是 referrer,两个 r。但 1996 年的规范里写成了 Referer,一个 r。
为了向后兼容,这个错误被永久保留。
有意思的是响应端的策略字段拼对了:Referrer-Policy。
所以同一个概念,请求头里是错的,响应头里是对的,两个拼法并存。
写代码时这是个高频 typo 来源:在服务端读 Referer 写成 Referrer,永远读不到值,
而且不会有任何报错——只是取不到。
二、它到底有用在哪
来源统计:知道流量从哪个页面来,是分析的基础。
防盗链:图片只允许从自己的域名引用,别家引用就回 403 或换成占位图。这是它最经典的用途。
CSRF 防护的辅助判断:检查请求是不是从自己的页面发起的。
注意只能当辅助——因为 Referer 可以被伪造,也可以被省略。
Demo 里五组请求覆盖了同站、带查询串、外站、跨协议、直接访问五种情况。
最后一组是空的,说明用户直接在地址栏输入了地址。
三、它会泄密,所以被削了
Demo 里那个 https://shop.example.com/list?uid=42&token=abc 如果原样发到外站,等于把令牌送人了。
所以现代浏览器默认用 strict-origin-when-cross-origin 策略:同站时发完整 URL,
跨站时只发「协议加域名」,路径和查询串全部砍掉。
服务端也可以主动声明策略,就是在响应里加 Referrer-Policy。
常见取值从松到紧:no-referrer-when-downgrade、strict-origin-when-cross-origin、no-referrer。
最严的 no-referrer 表示一个字节都不发。
代价是来源统计和防盗链都失效——所以选择策略时要想清楚,你到底需要它的哪一部分功能。
四、动手 Demo:五种来源,看服务端读到什么
服务端原样回显收到的 Referer,
并在响应里声明 Referrer-Policy: strict-origin-when-cross-origin。客户端依次模拟五种来源场景。
def responder(raw, st):
first, hd, _b = H.parse_headers(raw)
st[u'ref'] = hd.get(u'referer', u'') # 注意只有一个 r
return reply(u'HTTP/1.1 200 OK',
[(u'Referrer-Policy', # 这个是两个 r
u'strict-origin-when-cross-origin')],
(u'来源:' + (st[u'ref'] or u'(空)')).encode(u'utf-8'))
实跑输出:
--------------------------------------------------------------------
Referer:从哪个页面点过来的
--------------------------------------------------------------------
【同站跳转】
Referer https://shop.example.com/list
服务端读到 https://shop.example.com/list
【带查询串】
Referer https://shop.example.com/list?uid=42&token=abc
服务端读到 https://shop.example.com/list?uid=42&token=abc
【从外站过来】
Referer https://search.example.net/?q=book
服务端读到 https://search.example.net/?q=book
【从 https 跳到 http】
Referer https://secure.example.com/page
服务端读到 https://secure.example.com/page
【直接输入地址】
Referer (空,说明是直接访问)
服务端读到 (空)
注意字段名的拼写:**Referer 少了一个 r**。
正确英文是 referrer,但 1996 年的规范里就写错了,
为了兼容只能一直错下去。所以响应端的策略字段叫 `Referrer-Policy`(拼对了),
请求端的字段叫 `Referer`(拼错了)——两个拼法并存,这是历史遗留。
它的实际用途:统计来源、防盗链、CSRF 防护的辅助判断。
风险也很直接:URL 里的查询串会被带出去。
上面那个 `?uid=42&token=abc` 如果原样发到外站,等于把令牌送人了。
所以现代浏览器默认用 `strict-origin-when-cross-origin` 策略:
跨站时只发协议加域名,路径和查询串全部砍掉。
同站跳转:https://shop.example.com/list,完整路径都在。
带查询串:https://shop.example.com/list?uid=42&token=abc —— 查询串完整泄露。
这就是要收紧策略的原因。
外站过来:https://search.example.net/?q=book,同样带查询串。
从 https 跳到 http:https://secure.example.com/page。
这种情况历史上会不发 Referer(避免向不安全的页面泄露信息),本 Demo 是自己拼的请求,
所以照发了——真实浏览器会砍掉。
直接输入地址:空。服务端读到「(空)」,这就是「没有来源」的信号。
30 秒实验
- 把响应的
Referrer-Policy改成no-referrer,想想浏览器会怎么变。 - 写一个防盗链判断:
Referer不是本站就回 403,试试会不会误伤直接访问。 - 想一想:为什么 CSRF 防护不能只靠
Referer?
常见误区
- 「字段名是 Referrer」——请求头里是
Referer,只有一个 r。 - 「
Referer一定会发」——可以被策略禁止,也可以被用户关掉。 - 「它适合做严格的访问控制」——可以伪造可以省略,只能当辅助判断。
自测三问(点开对答案)
1. 为什么字段名叫 `Referer` 而不是 `Referrer`? —— 1996 年的规范里拼错了,为了兼容只能一直错下去。 2. 它最经典的用途是什么? —— 防盗链:图片只允许从自己的域名引用。 3. 它有什么隐私风险? —— 会泄露来源 URL 的完整路径和查询串,可能带出令牌之类的敏感参数。来源说完了,只剩两个:一个管传输编码协商,一个是客户端的自我介绍。
- ☐ (本节完,下一节 6.4.18 TE)