Path rules
editInside a group, Allow and Disallow rules decide which paths a crawler may fetch. A path that no rule matches is allowed.
The longest rule wins
When several rules match a path, the one with the longest pattern wins, wherever it is in the group. When an Allow and a Disallow rule of the same length match, Allow wins:
var robots = RobotsFile.Parse("""
User-agent: *
Disallow: /docs/
Allow: /docs/public/
Allow: /shop
Disallow: /shop
""");
// The longest matching rule wins
Assert.False(robots.IsAllowed("MyCrawler", "/docs/internal"));
Assert.True(robots.IsAllowed("MyCrawler", "/docs/public/guide"));
// Allow wins over a Disallow of the same length
Assert.True(robots.IsAllowed("MyCrawler", "/shop/cart"));
// A path that no rule matches is allowed
Assert.True(robots.IsAllowed("MyCrawler", "/about"));The length is the length of the pattern as written in the file, wildcards included.
Prefixes, case and query strings
A pattern without wildcards matches the paths that start with it:
var robots = RobotsFile.Parse("""
User-agent: *
Disallow: /search
Disallow: /*?sessionid=
""");
// A rule is a prefix of the path, compared case-sensitively
Assert.False(robots.IsAllowed("MyCrawler", "/search"));
Assert.False(robots.IsAllowed("MyCrawler", "/search-results"));
Assert.True(robots.IsAllowed("MyCrawler", "/Search"));
// The query string is part of what the rules match
Assert.False(robots.IsAllowed("MyCrawler", "/products?sessionid=42"));
Assert.False(robots.IsAllowed("MyCrawler", new Uri("https://example.com/products?sessionid=42")));
Assert.True(robots.IsAllowed("MyCrawler", new Uri("https://example.com/products")));Disallow: /searchblocks/search-resultstoo. End the pattern with/or$to block less.- The comparison is case-sensitive and character by character: the parser does not decode percent-encoded characters, so write the path as your crawler requests it.
- The query string is part of the path. With a
Uri, IsAllowed checks its path and query; with a relativeUri, it checks the original string.
Wildcards and end anchors
* matches any sequence of characters, including none, and a $ at the end of a pattern anchors it to the end of the path:
var robots = RobotsFile.Parse("""
User-agent: *
Disallow: /*.php$
Allow: /index.php
""");
Assert.True(robots.IsAllowed("AnyBot", "/index.php"));
Assert.False(robots.IsAllowed("AnyBot", "/admin.php"));/*.php$ blocks the paths that end with .php, and the longer Allow: /index.php lets one of them through. Only a $ at the end of a pattern is an anchor.
Allow everything
An empty Disallow value allows everything:
var robots = RobotsFile.Parse("""
User-agent: *
Disallow:
""");
// An empty Disallow allows everything
Assert.True(robots.IsAllowed("MyCrawler", "/"));
Assert.True(robots.IsAllowed("MyCrawler", "/any/page"));To block everything, write Disallow: /.
Read the rules
RobotsGroup.Rules lists the rules of a group in file order. Each RobotsRule has a Kind (Allow or Disallow), the pattern as written in Value, and a Matches method that tests a path against the pattern alone:
var robots = RobotsFile.Parse("""
User-agent: *
Disallow: /private/
Allow: /private/public/
""");
var rules = robots.Groups[0].Rules.Select(rule => $"{rule.Kind}: {rule.Value}");
Assert.Equal(["Disallow: /private/", "Allow: /private/public/"], rules);
var rule = robots.Groups[0].Rules[0];
Assert.True(rule.Matches("/private/data"));RobotsGroup.IsAllowed applies the precedence above to the rules of one group, and RobotsFile.IsAllowed selects the group of the user agent first.