如何使用 HTML/CSS/JS 覆盖 HTTP header 中指定的 Content-Type/charset

How to override Content-Type/charset specified in HTTP header using HTML/CSS/JS

测试用例

我在这里提供了一个实时测试用例:https://lonelearner.github.io/charset-issue/index.html

因为 HTML 有 non-ASCII 个字符,如果你想在你的系统上可靠地重现这个测试用例,这里是重现它的方法。您可以使用以下任何一种方法来重现它:

  1. 从上面获取页面 URL。

    curl https://lonelearner.github.io/charset-issue/index.html -O
    
  2. 运行 这个命令:

    echo "
    3c21444f43545950452068746d6c3e0a3c68746d6c3e0a20203c68656164
    3e0a202020203c7469746c653e636861727365742069737375653c2f7469
    746c653e0a202020203c6d65746120687474702d65717569763d22436f6e
    74656e742d547970652220636f6e74656e743d22746578742f68746d6c3b
    20636861727365743d69736f2d383835392d31223e0a20203c2f68656164
    3e0a20203c626f64793e0a202020203c703ea93c2f703e0a20203c2f626f
    64793e0a3c2f68746d6c3e0a
    " | xxd -p -r > index.html
    

有趣的字节

让我们看看本题中我们关心的ISO-8859-1编码字符

$ curl -s https://lonelearner.github.io/charset-issue/index.html | xxd -g1
00000000: 3c 21 44 4f 43 54 59 50 45 20 68 74 6d 6c 3e 0a  <!DOCTYPE html>.
00000010: 3c 68 74 6d 6c 3e 0a 20 20 3c 68 65 61 64 3e 0a  <html>.  <head>.
00000020: 20 20 20 20 3c 74 69 74 6c 65 3e 63 68 61 72 73      <title>chars
00000030: 65 74 20 69 73 73 75 65 3c 2f 74 69 74 6c 65 3e  et issue</title>
00000040: 0a 20 20 20 20 3c 6d 65 74 61 20 68 74 74 70 2d  .    <meta http-
00000050: 65 71 75 69 76 3d 22 43 6f 6e 74 65 6e 74 2d 54  equiv="Content-T
00000060: 79 70 65 22 20 63 6f 6e 74 65 6e 74 3d 22 74 65  ype" content="te
00000070: 78 74 2f 68 74 6d 6c 3b 20 63 68 61 72 73 65 74  xt/html; charset
00000080: 3d 69 73 6f 2d 38 38 35 39 2d 31 22 3e 0a 20 20  =iso-8859-1">.  
00000090: 3c 2f 68 65 61 64 3e 0a 20 20 3c 62 6f 64 79 3e  </head>.  <body>
000000a0: 0a 20 20 20 20 3c 70 3e a9 3c 2f 70 3e 0a 20 20  .    <p>.</p>.  
000000b0: 3c 2f 62 6f 64 79 3e 0a 3c 2f 68 74 6d 6c 3e 0a  </body>.</html>.

在最后一行的前一行(偏移000000a0处的行),第9个字节是a9。那是我们有趣的字节。那是 copyright sign 的 ISO-8859-1 表示。请注意,这是 ISO-8859-1 编码符号,而不是 UTF-8。如果它是 UTF-8 编码的,字节将是 c2 a9.

元标签

为了确保这个 HTML 文件的内容被解释为 ISO-8859-1 编码数据,在 HTML 代码中有这个 <meta> 标签:

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

本地行为

如果您在本地系统上使用浏览器打开此文件,您很可能会看到如下输出:

这是预料之中的,因为在本地打开文件时,没有 HTTP 服务器发送 HTTP headers。所以在 <meta> 标签中指定的 iso-8859-1 编码被接受。

GitHub 行为

如果您使用浏览器访问 URL https://lonelearner.github.io/charset-issue/index.html,您很可能会看到如下输出:

这也在意料之中。如果您注意到该页面由 GitHub 页面和 GitHub 页面服务器提供,始终 returns 指定 ISO-8859-1 编码的 HTTP header。

$ curl -sI https://lonelearner.github.io/charset-issue/index.html | grep -i content-type
content-type: text/html; charset=utf-8

由于 HTTP header 指定了字符编码,因此不再支持 <meta> 标记中的字符编码。

问题

我是否可以使用 HTML、JavaScript 或 CSS 覆盖 HTTP header 中指定的字符编码来告诉浏览器该内容应该是解释为 ISO-8859-1 编码,即使 HTTP header 另有说明?

我知道我总是可以将版权符号写成 &copy; 或在文件中以 UTF-8 编码符号,但让我们考虑这样的解决方案不在这个问题的范围内,因为这里是我正在处理的约束:

Is there anyway I can override the character encoding specified in the HTTP header using HTML, JavaScript or CSS to tell the browser that this content should be interpreted as ISO-8859-1 encoding even if the HTTP header says otherwise?

没有。 HTTP header 是权威的 w3:

"...The HTTP header has a higher precedence than the in-document meta declarations, content authors should always take into account whether the character encoding is already declared in the HTTP header. If it is, the meta element must be set to declare the same encoding."