Researchai crawlers
Back to writing

Crawler policy

Robots.txt wildcards: which path does the rule match?

The slash, the query string and the selected crawler group can change which rule wins. Read the decision beside the exact input.

Monochrome generative point study “Parity Bloom,” a mathematical form rendered in black and white.

A robots.txt block on /shop also blocks /shopping. A final $ ends the match, while * matches zero or more characters, including slashes. To test a rule, select the crawler’s group, then compare the exact path and query. The most specific matching rule wins, with Allow winning an equivalent tie. Matching is case-sensitive.

The 18-case fixture below tests the details that break policy generators: directory separators, empty wildcard matches, suffixes, query strings, and encoded characters. It extends the eight-case policy example.

Method & limits

Primary documentation checked October 26, 2026. The example.com responses and local fixture are authored teaching examples; they do not report a Google indexing result or observed provider compliance.

The path and query decide the match

A rule is not a shell glob over filenames or a regular expression over a full URL. It applies to the path and query of a URL on the service that supplied the policy. A fragment is not sent in the HTTP request. Choose the User-agent group before comparing paths.

A trailing slash is consequential: /drafts/ names a directory prefix, while /drafts also matches /draftsmanship. A final $ makes the endpoint exact. In /*.pdf$, the wildcard can include slashes, but the suffix must end the path-and-query string; report.pdf?download=1 does not end in .pdf.

RFC 9309’s special characters define * and $. Google’s path examples show their use with case and query strings.

Selected cases from the downloadable fixture. ExampleBot uses the * group; these decisions permit or reject fetching, not indexing.
Rule in the fixtureRequest path and queryExpected decisionWhy
Disallow: /shop/shoppingBlockedA prefix does not imply a directory boundary.
Disallow: /drafts//draftsmanshipAllowedThe slash is part of the required prefix.
Disallow: /assets/*.pdf$/assets/report.pdf?download=1AllowedThe query means .pdf is no longer at the end.
Disallow: /assets/*.pdf$/assets/.pdfBlocked* can match zero characters.
Allow: /drafts/public$/drafts/public?lang=enBlockedThe exact exception misses; /drafts/ still blocks.
Allow and Disallow: /tie/tieAllowedEqually specific Allow wins.

The runner checks all 18 local decisions

Download the executable fixture bundle, extract it, and run node run.mjs with Node 22 or later. It reads only its adjacent robots.txt and cases.csv, prints each expected and actual decision, and exits unsuccessfully if a case differs. It makes no network requests and installs no packages.

The policy, expected results and runner are also available separately. Run from any directory: inputs resolve relative to the script, not your current working directory.

The runner models exact product-token groups, combined equal groups, fallback *, path matching, and normalization. It’s a reference for these published cases, with HTTP retrieval, caching, and partial user-agent matching outside its scope. Compare your production parser against the same cases, including the October 23 policy, before relying on it.

No install or outbound crawl required

Expected summary: 18/18 fixture decisions match. Change one expected result to exercise the failure exit.

unzip robots-wildcard-fixtures.zip -d robots-wildcard-fixtures
cd robots-wildcard-fixtures
node run.mjs

Encoding and group selection change the answer

The fixture treats /cafe and /%63afe as equivalent because c is an unreserved character. It keeps an encoded slash distinct from a path separator: /folder%2Fsecret does not become /folder/secret. Decode only the octets the protocol says to decode; decoding every escape can invent a directory boundary.

FixtureBot has two exact named groups whose rules combine. It doesn’t inherit the fallback group, so a request allowed for FixtureBot can be blocked for ExampleBot. The two-group worked example shows that selection before the paths are compared.

The runner gives normalization, group selection, matching, and precedence their own functions, so a disagreement with a production parser can be traced to the decision that changed instead of ending at one unexplained “blocked” badge.

A correct match can still use an expired policy

Add the exact deployed file to your release check, select the real crawler product token and evaluate a handful of representative paths. Include one intended block, one exception, a query variant, a capitalized path and a prefix neighbor. Preserve the returned policy when a release changes its effective result.

A crawler must also decide when that decision expires and what to do after a failed robots fetch. Keep the match result separate from those scheduling states in a crawl-frontier state machine. A correct string match against yesterday’s file can still produce the wrong request today.

Google’s open-source parser supplies another implementation and test corpus. I would put both the generator and production parser through the intended paths: the policy that matters is the one the next request will use.

Sources

  1. RFC 9309: matching and special charactersVerified Oct 26, 2026

    Sections 2.2.1–2.2.3 cover groups, longest matches, encoding and special characters.

  2. Google: robots.txt specificationVerified Oct 26, 2026

    Provider-specific group selection and path/query examples.

  3. Google’s open-source robots.txt parserVerified Oct 26, 2026

    An independent production-oriented implementation and its test corpus.

Supporting material

Read next