哪些字符会使 URL 无效?

Which characters make a URL invalid?

提问人:good 提问时间:10/10/2009 最后编辑:Michał Perłakowskigood 更新时间:4/8/2023 访问量:683686

问:

哪些字符会使 URL 无效?

这些是有效的 URL 吗?

  • example.com/file[/].html
  • http://example.com/file[/].html
验证 URL RFC3986

评论

62赞 mfx 12/3/2009
在验证时,你应该始终“积极思考”:问“什么是有效的”,其他一切都是无效的。针对(少数)有效字符进行测试比所有可能的无效字符更安全(也更容易!
4赞 DavidRR 9/18/2014
相关:检查字符串是否为有效 URL 的最佳正则表达式是什么?

答:

2赞 ChrisR 10/10/2009 #1

这并不是您问题的答案,但验证 URL 确实是一个严肃的 p.i.t.a。您可能最好验证域名并保留 URL 的查询部分。这是我的经验。

您也可以求助于pingURL并查看它是否产生有效的响应,但对于如此简单的任务来说,这可能太多了。

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

评论

0赞 DavidRR 9/18/2014
此答案建议 URL 验证不是针对正则表达式的工作,而是针对特定于语言/平台的库的工作。
12赞 CraigTP 10/10/2009 #2

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

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

此链接(HTML URL 编码参考)包含无效字符的编码列表。

评论

0赞 DavidRR 9/18/2014
对于Unicode字符,维基百科文章Percent-encoding说:“通用URI语法要求,在URI中提供字符数据表示的新URI方案实际上必须表示未保留集中的字符,而无需转换,并且应根据UTF-8将所有其他字符转换为字节,然后对这些值进行百分比编码
694赞 Gumbo 10/10/2009 #3

通常,RFC 3986 定义的 URI(请参阅第 2 节:字符)可以包含以下 84 个字符中的任何一个:

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

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

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

评论

41赞 Eamon Nerbonne 5/31/2011
(当然,字符列表没有说明它们可能出现在 URI 中的哪个位置
86赞 Leif Wickland 10/8/2011
下面是一个正则表达式,用于确定整个字符串是否仅包含上述字符:/^[!#$&-;=?-[]_a-z~]+$/
54赞 Leif Wickland 12/14/2011
@techiferous,是的,我忘了允许“%”转义字符。它应该看起来更像是:你发现它应该接受的还有什么吗?(需要明确的是,正则表达式仅检查字符串是否包含有效的 URL 字符,而不是字符串是否包含格式正确的 URL。/^([!#$&-;=?-[]_a-z~]|%[0-9a-fA-F]{2})+$/
12赞 Leif Wickland 1/5/2012
@Timwi RFC 3986 中,“百分比编码的八位字节被编码为字符三元组,由百分点字符”%“后跟代表该八位字节数值的两个十六进制数字组成。它还说,“由于百分比 (”%“) 字符用作百分比编码八位字节的指示符,因此必须将其百分比编码为”%25“,才能将该八位字节用作 URI 中的数据。我读到这句话是说“%”只有在后面跟着两个十六进制数字时才会出现。你怎么读?
13赞 Leif Wickland 7/3/2012
@Weeble 我的正则表达式使用范围包含这些字符。在“&”和“;”之间,在“?”和“[”之间,你会发现所有你没有看到的字符。
19赞 Dominic Sayers 12/3/2009 #4

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

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

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

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

如果您想完全了解该问题,值得仔细阅读 RFC 3986。

评论

0赞 skolima 12/14/2011
看完RFC后,我更倾向于同意@Stephen C更详细的解释。
0赞 Adam Gent 5/16/2013
URL 不是 URI 的子集。并且对我见过的几乎解析器都无效。这实际上在现实世界中搞砸了我:stackoverflow.com/questions/11038967/......[]
0赞 Mark Amery 4/17/2016
@AdamGent URL 在很大程度上是 URI 的子集。它们之间的唯一区别是它们是否描述了资源的位置 - 这是一个语义上的区别,而不是句法上的区别。如果您看到的将自己标记为“URI”解析器的解析器与将自己标记为“URL”解析器的解析器对方括号的处理方式不同,那么这纯粹是巧合,而不是由 URL 和 URI 之间的任何差异引起的。
0赞 Adam Gent 4/18/2016
@Mark Amery 来说,这类似于说 C++ 是 C 的超集。这在很大程度上是正确的,但并不完全正确,因为(URL 和 C)要古老得多,它们必须包含不那么严格的行为。问题是 URL 解析器会解析无效 URI 的内容......我的意思是他们中的大多数(坦率地说,我厌倦了在这么多语言中指出这一点)这不是巧合,而是向后兼容性。我们能同意 URL 规范至少更旧吗?
1赞 Erwin Bolwidt 5/10/2019
RFC3986对此非常清楚:“ 由 Internet 协议文字地址(版本 6 [RFC3513] 或更高版本)标识的主机通过将 IP 文字括在方括号内(”[“和”]“)来区分。这是 URI 语法中唯一允许使用方括号字符的地方。".无需其他考虑; 作为 URL 无效http://example.com/file[/].html
263赞 CodeMonkey 11/22/2012 #5

此示例中的“[”和“]”是“不明智”的字符,但仍然是合法的。如果 [] 中的“/”是文件名的一部分,那么它是无效的,因为“/”是保留的,应该正确编码:

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

为了澄清并直接解决上述问题,有几类字符会导致 URL 和 URI 出现问题。

有些字符是不允许的,不应该出现在 URL/URI 中,保留字符(如下所述)和其他字符在某些情况下可能会导致问题,但被标记为“不明智”或“不安全”。RFC-1738 (URL) 和 RFC-2396 (URI) 中清楚地说明了为什么字符受到限制。请注意,较新的 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 like 并且必须进行编码,就像使用 Java 和 URI 对象实例一样。http://api.google.com/q?exp=a|bhttp://api.google.com/q?exp=a%7Cb

评论

2赞 Bob Stein 7/9/2013
优秀,彻底的回答,唯一一个直接回答实际问题的人。保留部分可能需要工作,例如文字在查询部分很好,但在它之前是不可能的,我认为不属于这些列表中的任何一个。哦,而不是在最后一串中,你不是说吗??@%25%7C
1赞 CodeMonkey 7/9/2013
谢谢。好问题:%25 在示例中是一个错别字。直接从 RFC-2396 向“保留”语法描述添加了脚注。
2赞 Mark Amery 4/17/2016
这个答案还不错,但存在一些混淆和错误。您最初将不允许的字符和保留的字符混为一谈(非常不同的东西),您过多地区分了“不明智的”字符和其他不允许的字符(在 RFC 3986 中删除,甚至在语法上甚至在 RFC 2396 中也无关紧要),并且您令人困惑地将所有保留字符的列表显示为“在查询组件中”保留的列表。
1赞 CodeMonkey 4/18/2016
谢谢,并不是说不允许和保留的归类相同。更新了答案。恕我直言,RFC-2396 中的规则虽然较旧,但比 3986 中的更新规则更易于理解。答案更多地反映了哪些字符通常可能会带来麻烦,而不是确切地允许或不允许在哪个上下文中。
1赞 Philip 1/26/2017
值得注意的是,最近版本(7.0.73+、8.0.39+、8.5.7+)中的 Tomcat 已开始拒绝带有“不明智”类别字符的请求,并出现 HTTP 400 错误:“在请求目标中找到无效字符。有效字符在 RFC 7230 和 RFC 3986 中定义”
7赞 Bunyk 2/12/2014 #6

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

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

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

9赞 Ciro Santilli OurBigBook.com 8/29/2014 #7

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

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

和 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+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 字符

171赞 Mark Amery 4/17/2016 #8

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

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

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

根据现代规范,答案是“是”。WHATWG Living Standard 只是将以前称为“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 代码单元”中使用 。%%

我唯一能找到规范允许使用此集合中包含的任何字符的地方是主机,其中 IPv6 地址包含在 和 字符中。在 URL 中的其他任何地方,要么允许使用 URL 单元,要么允许使用一些限制性更强的字符集。[]

旧 RFC 允许使用哪些字符?

为了历史的缘故,并且由于此处的答案中没有在其他地方充分探讨它,因此让我们检查一下在较旧的规范对下是否允许。

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

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

上述任何保留字符都可以在不进行编码的情况下合法地在 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 的特定部分是合法的。例如,保留字符 和 作为 URL 中 IPv6 文本主机的一部分是合法的,例如 http://[1080::8:800:200C:417A]/foo,但在任何其他上下文中都是不合法的,因此 OP 的示例是非法的。[]http://example.com/file[/].html

评论

1赞 puchu 7/12/2020
"<>\^`{|}不是被禁止的,它们被标记为不安全。但这些字符经常在现实世界中使用。
0赞 Mark Amery 7/13/2020
@puchu不,它们是被禁止的。自 RFC 1738 以来,“不安全”的名称就没有用于这些字符(1994 年发布,二十多年前已经被 RFC 2368 淘汰),即使在那里,它也只是“禁止”的一个古怪的同义词;RFC 1738 说“所有不安全的字符必须始终在 URL 中编码。
0赞 puchu 7/14/2020
也许这些符号在某些 rfc perfective 中是被禁止的,但它们在现实世界中并未编码,并且经常被旧客户端和服务器按原样使用。
3赞 Deji 11/23/2020
+1 表示实际回答问题,而不是解释问题并回答另一个问题。我来自 Google,正在寻找一些可用于测试 URL 验证方法的无效字符。其他人正在回答如何编写 URL 验证方法......
1赞 Mark Amery 3/5/2021
@OfirD 至于 (2),我认为 PJ 在您链接到的答案中也以与我相同的方式解释 RFC。是的,RFC 3986 不介意您在查询字符串中的任何位置使用,但那是因为该格式不是其规范的一部分,因此它不会在查询字符串中将 和视为具有任何特殊语法目的。字符串 和 是 RFC 3986 下同样合法的查询字符串,但后两个不是通用格式的格式正确的示例,并且会混淆任何需要对的查询字符串解析器。=key=value=&foo=bar====foo====bar====!?@!?@key=valuekey=value
-7赞 relipse 12/27/2016 #9

我想出了几个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) );

评论

10赞 Mark Amery 9/11/2018
-1;除了它们都以某种身份涉及 URL 这一事实之外,这与所提出的问题无关。
3赞 puchu 4/13/2020 #10

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

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

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

\ { } < > | ` ^ "

这些字符在 RFC 1738 中被描述为不安全

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

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

评论

0赞 Sandeep Das 3/1/2021
是否有任何 API 可以从字符串中删除这些字符
0赞 BobMilton 7/13/2022 #11

我不能对上面的答案发表评论,但想强调一点(在另一个答案中),即不允许到处都允许字符。例如,域名不能有下划线,因此 http://test_url.com 无效。

1赞 Daniel Viglione 10/19/2022 #12

从源头(需要时添加强调):

不安全的:

由于多种原因,角色可能不安全。空格字符是不安全的,因为在转录或排版 URL 或进行文字处理程序处理时,重要的空格可能会消失,并且可能会引入无关紧要的空格。

字符“<”和“>”是不安全的,因为它们被用作 自由文本中 URL 周围的分隔符;引号 (“”“) 用于 在某些系统中分隔 URL。字符“#”不安全,应该 始终进行编码,因为它用于万维网和其他 系统从片段/锚标识符中分隔 URL,该标识符可能 跟着它。字符“%”是不安全的,因为它用于 其他字符的编码。其他角色不安全,因为 众所周知,网关和其他传输代理有时会修改 字符。这些字符是 “{”, “}”, “|”, “”, “^”, “~”, “[”, “]”和“'”。

所有不安全的字符必须始终在 URL 中编码。为 例如,即使在系统中,字符“#”也必须在 URL 中编码 通常不处理片段或锚点标识符,因此 如果将 URL 复制到另一个使用它们的系统中,它将 不需要更改 URL 编码。

-1赞 Eli O. 4/8/2023 #13

如果您需要进行更广泛的验证,包括表情符号(现在在 URL 中偶尔使用),例如:

http://factmyth.com/factoids/you-👏-can-👏-put-👏表情符号-👏 in-👏-urls-👏/

甚至在域名中,例如:😉.tld

那么这是一个有用的正则表达式:

[-a-zA-Z0-9\u1F60-\uFFFF@:%_\+.~#?&//=!'(),;*\$\[\]]*

PS :它不适用于编程语言中使用的所有正则表达式“风格”。它对 Python、Rust、Golang、现代 Javascript 有效,但对 PHP 无效。通过选择左侧的“flavors”并检查错误消息来检查此处:https://regex101.com/

评论

0赞 Mark Amery 11/14/2023
据我所知,您的正则表达式可能是正确的,但是如果没有看到它允许和不允许的字符的摘要以及您如何构建这些字符集的一些解释,我永远不会相信它足以使用它!