因为制作质量字体的人们应该得到报酬
在我重新设计的个人网站sidney.me工作时,我选择使用付费字体Klim Type Foundry s The Future Mono 。这是我第一次为项目使用付费网络字体,因此我阅读(掠过)web font licence agreement。奇怪的是,它包括此条款(强调我的):
3D。 Web字体文件保护
您同意使用合理的措施来确保仅用于网站上的造型文本的过程。至少,通过说明而不是限制,合理的措施包括a。)防止未经许可的第三方访问,即 hotlinking 和b。)不允许直接下载该字体与您网站的造型文本无关。
用更简单的话来说,您必须确保部署Web字体文件,以便没有人可以将URL粘贴到浏览器中以下载它,并且其他网站可以通过链接到该URL来使用Web字体。<<<<<<<<<<<<<<<<<< /p>
我多年来一直在使用Netlify来托管我的网站,因此我寻找了一种本地进行操作的方法。不幸的是,我遇到了这个Netlify support forum thread,我们还没有解决这个特定问题的解决方案,但它可能会在将来涵盖。敬请期待!2020年3月。
答复确实有一些建议,例如:
- 使用netlify函数(此用例的过度杀伤和潜在慢)检查http请求中的koude0 header,
- 使用一个更具性能的Cloudflare工人到do that check(但我不想注册另一项服务)或
- 从S3存储桶或单独的Apache服务器(打破无服务器堆栈的全部点)并使用
.htaccess
。
自从该线程的最后一个答复以来,Netlify已发布Edge Functions。他们使用Deno运行JavaScript或打字稿代码,这是旨在成功节点的热门JS运行时,与典型的无服务器功能运行的情况相比,CDN服务器更接近用户。换句话说:快速,快速,快速。
代码
在NetLify项目上设置边缘功能非常简单。首先创建一个netlify/edge-functions
目录并在其中制作JS或TS文件。然后,导出一个默认函数以使NetLify运行。让我们称其为hotlink-protection.ts
,然后返回“你好世界”作为基本回应:
// netlify/edge-functions/hotlink-protection.ts
export default () => new Response("Hello world");
然后,我们需要指定该功能应在哪些路由下运行。在这种情况下,我们可以将其限制为字体文件。我仅以browser support is excellent为woff2格式使用字体。在项目的根部,创建一个名为netlify.toml
的文件,然后将路径和函数名称设置为与您选择的文件名相同。
# netlify.toml
[[edge_functions]]
path = "/*.woff2"
function = "hotlink-protection"
当我们在这里时,让我们为字体添加一个缓存标头。它们是不变的,静态的资产,这些资产不会改变:我们需要向浏览器指定。
# netlify.toml
[[headers]]
for = "/*.woff2"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
现在让我们开始工作。我们有效地创建了修改和返回响应字体文件本身或403禁止HTTP错误的中间件。 Edge函数将获得两个参数,分别是request
和context
。我们需要使用context
来await
响应(字体数据),因此,让我们将其更改为async
函数。这是打字稿中的定义:
import type { Context } from "https://edge.netlify.com";
export default async (
request: Request,
context: Context
): Promise<Response> => {
// ...
};
然后,让我们从request
获取Referer
标头,并使用正则表达式来测试它来自我们的网站。如果没有Referer
或没有匹配,请返回403。
import type { Context } from "https://edge.netlify.com";
export default async (
request: Request,
context: Context
): Promise<Response> => {
const referer = request.headers.get("referer");
const regex = /^https?:\/\/(.*\.)?sidney\.me(\/.*)?$/;
if (!referer || !regex.test(referer)) {
return new Response("Forbidden", { status: 403 });
}
// ...
};
那条正则看起来很粗糙,但其大多数特殊字符都用于逃避特殊字符/
和.
,它们也是URL的一部分。它与sidney.me
和任何子域和目录相匹配,因此在复制此片段时,请确保更改这些字符(不包括\.
)。有关说明正则的说明,请在regex101.com上查看。右上方面板突出了每个角色的作用。
然后,要返回字体文件,我们可以使用await context.next()
获取下一个HTTP响应。这是最终代码:
// netlify/edge-functions/hotlink-protection.ts
import type { Context } from "https://edge.netlify.com";
export default async (
request: Request,
context: Context
): Promise<Response> => {
const referer = request.headers.get("referer");
const regex = /^https?:\/\/(.*\.)?sidney\.me(\/.*)?$/;
if (!referer || !regex.test(referer)) {
return new Response("Forbidden", { status: 403 });
}
const response = await context.next();
return response;
};
现在,您可以进行更改并部署项目以照常网络化。部署后,导航到字体URL应显示“禁止”。
注意:如果您希望您的字体在 Branch部署中工作,则需要单独检查NetLify域名。就我而言,它的sidney-me.netlify.app
和分支名称和--
的前缀是:branch-name--sidney-me.netlify.app
。这是我的言论:
const prodRegex = /^https?:\/\/(.*\.)?sidney\.me(\/.*)?$/;
const devRegex = /^https?:\/\/(.*\--)?sidney-me\.netlify\.app(\/.*)?$/;
if (
!referer ||
!(prodRegex.test(referer) || devRegex.test(referer))
) {
return new Response("Forbidden", { status: 403 });
}
将您的字体文件子设置为额外的保护和性能提升
当然,这种保护本身并非防弹。有人可以轻松地欺骗Referer
标头或使用DevTools下载字体文件。字体数据必须发送给用户以供其浏览器显示字体。一旦在用户的计算机上,他们总是有一种方法来提取字体文件。
减轻这种情况的一种方法是将字体文件征用,仅包括您在网站上使用的字符,不包括其他语言或写作系统的字符。此外,字体通常具有像alternate characters这样的OpenType features,主要是出于风格目的,我们可以关注Google Fonts’ example从我们使用的文件中删除它们。
对于我的网站,我使用了Fonttools的pyftsubset
进行子集。请关注these instructions安装(请注意,您也需要安装Python)。这是一个例子:
pyftsubset the-future-mono-regular.woff2 \
--output-file="the-future-mono-regular--subset.woff2" \
--flavor=woff2 \
--unicodes="U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD"
此命令子集与拉丁字符集和一些标点符号符号,共385个字符。如subset docs中所述,它还消除了可自由的Opentype特征。就我而言,这使字体文件的大小减少了一半,从30.1 kb到14.7 kb。这可以加起来并提高您的灯塔性能得分。
然后指定CSS中的Unicode范围,以帮助浏览器在必要时使用适当的后备字体。
@font-face {
font-family: "The Future Mono";
src: url("the-future-mono-regular--subset.woff2") format("woff2");
font-display: swap;
font-weight: 400;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
U+FEFF, U+FFFD;
}
提供另一个项目的字体
如果您将网站的代码作为开源代码发布,请确保 not 将您的字体文件包括在该存储库中。您不想成为Github成为web’s largest font piracy site的问题的一部分。
- 使用
*.woff2
将字体文件添加到.gitignore
文件中。 - 如果您已经公开发布了仓库,请从历史记录中永久删除文件(instructions)。
- 然后,将您的字体文件部署在新的私有存储库中作为单独的NetLify项目。
-
您可以将文件保留在本地存储库中进行测试,并使您的CSS保持不变,指向同一域名的文件。然后使用rewrite指向其他项目的域名:
# netlify.toml [[redirects]] from = "/fonts/the-future-mono-*" to = "https://your-project.netlify.app/the-future-mono/the-future-mono-:splat" status = 200 force = true
重要:您必须在两个项目的匹配URL上具有热链接保护边缘功能。如果有人去您的主要URL获取字体,则私人项目的边缘功能将获得正确的Referer
标题,并让任何人下载或热链接。
您可以将相同的功能复制到主存储库中,并将其设置为netlify.toml
中的适当路径:
# netlify.toml
[[edge_functions]]
path = "/fonts/the-future-mono-*"
function = "hotlink-protection"
现在,您拥有两全其美的最好:该字体可用于部署后的本地开发。
。其他资源
- 这是一些针对Web字体的其他保护措施的suggestions from TypeKit。
- 另外,您可以根据网站上使用的字符使用glyphhanger来子集Web字体。
- 您可以看到哪些字符在Unicode范围内,并使用Unicode Range Interchange在范围内进行数学。
- 您可以使用Wakamai Fondue中的字体文件中包含哪些OpenType功能和所有字符。
- 如果您对Web字体性能感到好奇,Zach Leatherman在CSS-Tricks上的实施中写了一个excellent guide。
当然,请查看我的个人网站sidney.me,让我知道您对Mastodon的想法。