587

哪些字符使 URL 无效?

这些是有效的 URL 吗?

  • example.com/file[/].html
  • http://example.com/file[/].html
4

10 回答 10

660

一般来说,由RFC 3986(参见第 2 节:字符)定义的 URI 可以包含以下 84 个字符中的任何一个:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=

请注意,此列表并未说明这些字符在 URI 中可能出现的位置。

任何其他字符都需要使用百分比编码 ( %hh) 进行编码。URI 的每一部分对哪些字符需要用百分比编码的词来表示有进一步的限制。

于 2009-10-10T13:26:14.147 回答
233

为了添加一些说明并直接解决上述问题,有几类字符会导致 URL 和 URI 出现问题。

有一些字符是不允许的并且永远不应出现在 URL/URI 中、保留字符(如下所述)以及在某些情况下可能会导致问题但被标记为“不明智”或“不安全”的其他字符。RFC-1738 (URLs) 和RFC-2396 (URIs)中清楚地说明了字符受限的原因。请注意,较新的RFC-3986(对 RFC-1738 的更新)定义了在给定上下文中允许哪些字符的构造,但较旧的规范提供了一个更简单、更通用的描述,说明哪些字符在以下规则中是不允许的。

URI 语法中不允许使用的排除的 US-ASCII 字符:

   control     = <US-ASCII coded characters 00-1F and 7F hexadecimal>
   space       = <US-ASCII coded character 20 hexadecimal>
   delims      = "<" | ">" | "#" | "%" | <">

字符“#”被排除在外,因为它用于将 URI 与片段标识符分隔开。百分比字符“%”被排除在外,因为它用于转义字符的编码。换句话说,“#”和“%”是必须在特定上下文中使用的保留字符。

不明智的字符列表是允许的,但可能会导致问题:

   unwise      = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"

在查询组件中保留的字符和/或在 URI/URL 中具有特殊含义的字符:

  reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","

上面的“保留”语法类是指在 URI 中允许使用但在通用 URI 语法的特定组件中可能不允许使用的那些字符。“保留”集中的字符并非在所有上下文中都保留。例如,主机名可以包含可选的用户名,因此它可能类似于ftp://user@hostname/“@”字符具有特殊含义的地方。

下面是一个 URL 示例,其中包含无效和不明智的字符(例如,'$'、'['、']')并且应该正确编码:

http://mw1.google.com/mw-earth-vectordb/kml-samples/gp/seattle/gigapxl/$[level]/r$[y]_c$[x].jpg

URI 和 URL 的一些字符限制取决于编程语言。例如,“|” (0x7C) 字符虽然在 URI 规范中仅标记为“不明智”,但将在 Java java.net.URI构造函数中引发URISyntaxException ,因此不允许使用类似 URL,并且必须像使用带有 URI 对象实例的 Java 一样进行编码。http://api.google.com/q?exp=a|bhttp://api.google.com/q?exp=a%7Cb

于 2012-11-21T18:50:11.747 回答
145

这里的大多数现有答案都是不切实际的,因为它们完全忽略了地址在现实世界中的使用,例如:

首先,题外话。这些地址什么?它们是有效的 URL 吗?

从历史上看,答案是“不”。根据RFC 3986,从 2005 年开始,此类地址不是 URI(因此也不是 URL,因为 URL是 URI 的一种)。根据 2005 IETF 标准的术语,我们应该正确地将它们称为 IRI(国际化资源标识符),如RFC 3987中定义的那样,它们在技术上不是 URI,但可以通过对 IRI 中的所有非 ASCII 字符进行百分比编码简单地转换为 URI .

根据现代规范,答案是“是”。WHATWG 生活标准简单地将以前称为“URI”或“IRI”的所有内容归类为“URL” 。这使规范的术语与没有阅读规范的普通人如何使用“URL”一词保持一致,这是规范的目标之一。

WHATWG 生活标准允许哪些字符?

根据“URL”的新含义,允许使用哪些字符?在 URL 的许多部分,例如查询字符串和路径,我们可以使用任意的“URL 单元”,它们是

URL 代码点百分比编码字节

什么是“URL 代码点”?

URL 代码点为 ASCII 字母数字、U+0021 (!)、U+0024 ($)、U+0026 (&)、U+0027 (')、U+0028 左括号、U+0029 右括号、U+ 002A (*), U+002B (+), U+002C (,), U+002D (-), U+002E (.), U+002F (/), U+003A (:), U+003B (;)、U+003D (=)、U+003F (?)、U+0040 (@)、U+005F (_)、U+007E (~) 以及 U+00A0 到 U 范围内的代码点+10FFFD,包括在内,不包括代理和非字符。

(请注意,“URL 代码点”列表不包括%,但%如果它们是百分比编码序列的一部分,则“URL 代码单元”中允许使用 s。)

我能发现规范允许使用不在此集合中的任何字符的唯一地方是在host中,其中 IPv6 地址包含在[]字符中。在 URL 的其他任何地方,要么允许 URL 单元,要么允许使用更严格的字符集。

旧 RFC 允许使用哪些字符?

为了历史起见,并且由于这里的答案中没有在其他地方充分探索,让我们检查一下在旧的规范对下是允许的。

首先,我们有两种 RFC 3986保留字符

  • :/?#[]@,它们是 RFC 3986 中定义的 URI 的通用语法的一部分
  • !$&'()*+,;=,它们不是 RFC 通用语法的一部分,但保留用作特定 URI 方案的语法组件。例如,分号和逗号用作数据 URI语法的一部分,&并且=用作?foo=bar&qux=baz查询字符串中普遍存在的格式的一部分( RFC 3986指定)。

上面的任何保留字符都可以在 URI 中合法使用而无需编码,以服务于它们的句法目的,或者在某些地方作为数据中的文字字符,这样的使用不能被误解为服务于其句法目的的字符。(例如,虽然/在 URL 中具有句法含义,但您可以在查询字符串中未编码地使用它,因为它在查询字符串中没有意义。)

RFC 3986 还指定了一些未保留的字符,它们总是可以简单地用于表示没有任何编码的数据:

  • abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~

最后,%字符本身允许进行百分比编码。

只留下以下禁止出现在 URL 中的 ASCII 字符:

  • 控制字符(字符 0-1F 和 7F),包括换行符、制表符和回车符。
  • "<>^`{|}

来自 ASCII 的每个其他字符都可以合法地出现在 URL 中。

然后 RFC 3987 使用以下 unicode 字符范围扩展了该组未保留字符:

  %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
/ %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
/ %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
/ %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
/ %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
/ %xD0000-DFFFD / %xE1000-EFFFD

鉴于最新的 Unicode块定义,旧规范中的这些块选择似乎很奇怪且随意;这可能是因为自 RFC 3987 编写以来的十年中已添加了这些块。


最后,可能值得注意的是,仅仅知道哪些字符可以合法地出现在 URL 中并不足以识别某个给定字符串是否是合法 URL,因为某些字符仅在 URL 的特定部分是合法的。例如,在 http://[1080::8:800:200C:417A]/foo 这样的 URL 中,保留字符[]作为 IPv6 文字主机的一部分是合法的,但在任何其他上下文中都是不合法的,因此OP的例子http://example.com/file[/].html是非法的。

于 2016-04-16T17:17:49.437 回答
19

在您的补充问题中,您询问是否www.example.com/file[/].html是有效的 URL。

该 URL 无效,因为 URL 是一种 URI,并且有效的 URI 必须具有类似的方案http:(请参阅RFC 3986)。

如果您要询问是否http://www.example.com/file[/].html是有效的 URL,那么答案仍然是否定的,因为方括号字符在那里无效。

方括号字符是为这种格式的 URL 保留的:(http://[2001:db8:85a3::8a2e:370:7334]/foo/bar即 IPv6 文字而不是主机名)

如果您想完全理解这个问题,值得仔细阅读 RFC 3986。

于 2009-12-03T15:46:05.540 回答
12

可以在 URI 中使用的所有有效字符( URLURI的一种)在RFC 3986中定义。

所有其他字符都可以在 URL 中使用,前提是它们首先是“URL 编码”的。这涉及更改特定“代码”的无效字符(通常以百分号 (%) 后跟十六进制数字的形式)。

此链接HTML URL Encoding Reference包含无效字符的编码列表。

于 2009-10-10T13:22:26.247 回答
9

几个 Unicode 字符范围是有效的 HTML5,尽管使用它们可能仍然不是一个好主意。

例如,href文档说http://www.w3.org/TR/html5/links.html#attr-hyperlink-href

a 和 area 元素的 href 属性的值必须是一个可能被空格包围的有效 URL。

然后“有效 URL”的定义指向http://url.spec.whatwg.org/,它表示它的目的是:

使 RFC 3986 和 RFC 3987 与当代实施保持一致,并在此过程中废弃它们。

该文档将URL 代码点定义为:

ASCII 字母数字、“!”、“$”、“&”、“'”、“(”、“)”、“*”、“+”、“”、“-”、“.”、“/” , ":", ";", "=", "?", "@", "_", "~" 和 U+00A0 到 U+D7FF, U+E000 到 U+FDCF 范围内的代码点, U+FDF0 到 U+FFFD, U+10000 到 U+1FFFD, U+20000 到 U+2FFFD, U+30000 到 U+3FFFD, U+40000 到 U+4FFFD, U+50000 到 U+5FFFD, U +60000 到 U+6FFFD,U+70000 到 U+7FFFD,U+80000 到 U+8FFFD,U+90000 到 U+9FFFD,U+A0000 到 U+AFFFD,U+B0000 到 U+BFFFD,U+C0000至 U+CFFFD,U+D0000 至 U+DFFFD,U+E1000 至 U+EFFFD,U+F0000 至 U+FFFFD,U+100000 至 U+10FFFD。

然后在语句中使用术语“URL 代码点”:

如果 c 不是 URL 代码点且不是“%”,则解析错误。

在解析算法的几个部分,包括模式、权限、相对路径、查询和片段状态:所以基本上是整个 URL。

此外,验证器http://validator.w3.org/会传递类似 的 URL "你好",但不会传递带有空格等字符的 URL"a b"

当然,正如 Stephen C 所提到的,这不仅与字符有关,还与上下文有关:您必须了解整个算法。但是,由于在算法的关键点上使用了类“URL 代码点”,它可以很好地了解您可以使用或不使用什么。

另请参阅:URL 中的 Unicode 字符

于 2014-08-29T14:19:07.957 回答
6

我需要选择字符来拆分字符串中的 URL,所以我决定创建一个我自己在 URL 中找不到的字符列表:

>>> allowed = "-_.~!*'();:@&=+$,/?%#[]?@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
>>> from string import printable
>>> ''.join(set(printable).difference(set(allowed)))
'`" <\x0b\n\r\x0c\\\t{^}|>'

因此,可能的选择是换行符、制表符、空格、反斜杠和"<>{}^|. 我想我会使用空格或换行符。:)

于 2014-02-11T17:57:16.227 回答
2

这并不是您问题的真正答案,但验证 URL 确实是一个严肃的问题。您最好验证域名并将 URL 的查询部分保留。这就是我的经验。

您还可以使用 ping URL 并查看它是否会产生有效响应,但这对于如此简单的任务来说可能太多了。

检测URL的正则表达式很丰富,google一下:)

于 2009-10-10T13:19:42.460 回答
1

我正在实现一个旧的 HTTP(0.9、1.0、1.1)请求和响应读取器/写入器。请求 URI 是最有问题的地方。

您不能直接使用 RFC 1738、2396 或 3986。有许多旧的 HTTP 客户端和服务器允许更多字符。因此,我根据意外发布的 Web 服务器访问日志进行了研究:"GET URI HTTP/1.0" 200.

我发现 URI 中经常使用以下非标准字符:

\ { } < > | ` ^ "

这些字符在RFC 1738中被描述为unsafe

如果您想与所有旧的 HTTP 客户端和服务器兼容 - 您必须在请求 URI 中允许这些字符。

请在oghttp-request-collector中阅读有关这项研究的更多信息。

于 2020-04-12T16:37:17.900 回答
-5

我想出了几个用于 PHP 的正则表达式,它们可以将文本中的 URL 转换为锚标记。(首先它将所有www. URL 转换为http://,然后将所有带有https?://的 URL 转换为href=... HTML 链接

$string = preg_replace('/(https?:\/\/)([!#$&-;=?\-\[\]_a-z~%]+)/sim', '<a href="$1$2">$2</a>', preg_replace('/(\s)((www\.)([!#$&-;=?\-\[\]_a-z~%]+))/sim', '$1http://$2', $string) );

于 2016-12-26T18:36:55.427 回答