Robots.txt tester
- Matching runs on your device
- Fetch from site uses our server, nothing stored
- Free, no sign-up
| URL path | Verdict | Deciding rule |
|---|
Every rule in the chosen group, checked against the first URL
| Line | Rule | Matches? | Length |
|---|
Matching runs in your browser. Fetch from site is the only step that uses ToolPetal's server, which requests the file as ToolPetalBot and does not store it. Matching follows Google's published robots.txt rules (RFC 9309, longest match wins, ties go to Allow). Other crawlers may read edge cases differently.
On this page
One file, eight URLs, two crawlers
This is the sample the Load sample button fills in. It looks harmless, and most of it is. The trap is the second group.
User-agent: *
Disallow: /cart
Disallow: /*?sort=
Allow: /blog/
Disallow: /blog/drafts/
User-agent: Googlebot
Disallow: /search
Allow: /search/help$
Sitemap: https://example.com/sitemap.xml
Here is what this page reports for each URL, with the line that decides it:
| Crawler | Path | Verdict | Why |
|---|---|---|---|
| Googlebot | /cart/checkout | Allowed | Googlebot has its own group (line 7), so it never reads Disallow: /cart on line 2. No rule in its group matches. |
| Googlebot | /search?q=shoes | Blocked | Disallow: /search (line 8) is a prefix match. |
| Googlebot | /search/help | Allowed | Allow: /search/help$ (line 9) is 13 characters and beats the 7 character Disallow. |
| Googlebot | /search/help?lang=fr | Blocked | The $ means the Allow only matches the exact path, so line 8 wins. |
| Bingbot | /cart | Blocked | No Bingbot group, so it uses *. Line 2 matches. |
| Bingbot | /blog/drafts/q4-plan | Blocked | Disallow: /blog/drafts/ (13 characters) beats Allow: /blog/ (6). |
| Bingbot | /blog/?sort=new | Blocked | Disallow: /*?sort= is 8 characters, Allow: /blog/ is 6. Longer wins. |
| Bingbot | /blog/robots-guide | Allowed | Only Allow: /blog/ (line 4) matches. |
The first row is the one that bites people. Someone adds a Googlebot group to block internal search, and the cart, checkout, and every other * rule quietly stop applying to Google. If you want both, copy the shared rules into the Googlebot group.
How a verdict gets decided
- Pick one group. The crawler looks for a
User-agentline with its own name, case-insensitive. Google's specialised crawlers such as Googlebot-Image and Googlebot-News fall back to agooglebotgroup if there is no group for them. Everyone else falls back to*. If several groups name the same crawler, their rules are merged. - Trim the URL. Only the path and query string are compared.
https://example.com/a/b?x=1#topbecomes/a/b?x=1. Non-ASCII characters are percent-encoded on both sides, so/caféin a rule matches/caf%C3%A9in a URL. - Find every rule that matches. Rules match from the start of the path.
*stands for any run of characters, and a trailing$pins the rule to the end. Matching is case-sensitive, so/Admindoes not cover/admin. - Longest rule wins. Length is counted in characters of the rule as written, wildcards included. On an exact tie, Allow wins.
- No match means allowed. So does an empty
Disallow:line, and so does/robots.txtitself.
Patterns that do not mean what they look like
| Rule | Matches | Does not match |
|---|---|---|
Disallow: /fish | /fish, /fish.html, /fishheads | /Fish.asp, /catfish |
Disallow: /fish/ | /fish/, /fish/salmon | /fish (no trailing slash) |
Disallow: /*.php | /index.php, /a/b.php?x=1 | /windows.PHP |
Disallow: /*.php$ | /file.php | /file.php?x=1, /file.php5 |
Allow: /$ | / only | /anything-else |
Disallow: (empty) | nothing | everything, so it allows all |
The trailing slash row catches folder blocks: Disallow: /private/ leaves /private crawlable. If that bare URL exists and redirects, it usually does not matter. If it serves content, block it too.
What this tester will not catch
- The file Google actually has. You test what you paste or what ToolPetalBot fetched just now. Google caches robots.txt for up to a day, some servers answer Googlebot differently, and a 5xx or redirect loop on the live file changes behaviour. Search Console's robots.txt report shows the version Google fetched.
- Indexing. Allowed means crawlable, nothing more. A page can be allowed and still carry noindex, or be blocked and still show up as a bare URL.
- Non-Google quirks. Some crawlers ignore wildcards, read rules top to bottom, or treat Crawl-delay as binding. The verdicts here use Google's documented logic.
- Subdomains and protocols. Each host has its own file.
shop.example.comdoes not readexample.com/robots.txt, and the http and https versions can differ. - Bad actors. robots.txt is a polite request. Scrapers that ignore it are not stopped by anything on this page.
The tester does flag lines Google skips: rules before any User-agent, unknown directives, Crawl-delay, and the long-dead Noindex: directive. It also warns when a file passes Google's 500 KiB read limit.
Test robots.txt rules in code
All three libraries follow the Google and RFC 9309 rules: * and $ wildcards, and the longest matching rule wins. Each prints false, true, false for the three URLs.
JavaScript: robots-parser
import robotsParser from "robots-parser"; // npm i robots-parser
const txt = `User-agent: *
Disallow: /admin/
Allow: /admin/public/
Disallow: /*.pdf$`;
const robots = robotsParser("https://example.com/robots.txt", txt);
console.log(robots.isAllowed("https://example.com/admin/users", "Googlebot")); // false
console.log(robots.isAllowed("https://example.com/admin/public/a", "Googlebot")); // true
console.log(robots.isAllowed("https://example.com/files/report.pdf", "Googlebot")); // false
console.log(robots.getMatchingLineNumber("https://example.com/admin/users", "Googlebot")); // 2
Python: Protego
from protego import Protego # pip install protego (supports * and $ like Google)
txt = """User-agent: *
Disallow: /admin/
Allow: /admin/public/
Disallow: /*.pdf$"""
rp = Protego.parse(txt)
print(rp.can_fetch("https://example.com/admin/users", "Googlebot")) # False
print(rp.can_fetch("https://example.com/admin/public/a", "Googlebot")) # True
print(rp.can_fetch("https://example.com/files/report.pdf", "Googlebot")) # False
Go: grobotstxt, a port of Google's parser
package main
import (
"fmt"
"github.com/jimsmart/grobotstxt" // Go port of Google's robots.txt parser
)
func main() {
txt := "User-agent: *\nDisallow: /admin/\nAllow: /admin/public/\nDisallow: /*.pdf$\n"
fmt.Println(grobotstxt.AgentAllowed(txt, "Googlebot", "https://example.com/admin/users")) // false
fmt.Println(grobotstxt.AgentAllowed(txt, "Googlebot", "https://example.com/admin/public/a")) // true
fmt.Println(grobotstxt.AgentAllowed(txt, "Googlebot", "https://example.com/files/report.pdf")) // false
}
Python's built-in urllib.robotparser is not a good stand-in here. It has no * or $ wildcards and applies the first matching rule instead of the most specific one, so with this file it reports report.pdf as allowed and /admin/public/a as blocked.
Each snippet was run on October 14, 2026 with robots-parser 3.0.1, Protego 0.7.0 and grobotstxt v1.0.3.
Robots Exclusion Protocol sources
- RFC 9309: Robots Exclusion Protocol
- RFC 9309, section 2.2.2: allow and disallow lines: the most specific (longest) match wins
- Google: how Google interprets robots.txt
- google/robotstxt: Google's open-source C++ parser
- robots-parser (JavaScript)
- Protego (Python)
- grobotstxt (Go)
FAQ
Why does Googlebot ignore my User-agent: * rules?
Because a crawler obeys only one group: the most specific one that names it. If your file has a User-agent: Googlebot group, Googlebot reads that group and skips the * group completely. Rules you want Googlebot to follow have to be repeated inside its own group.
When an Allow and a Disallow both match, which one wins?
The rule with the longer path wins, counted in characters, wildcards included. If the lengths are equal, Google picks the less restrictive rule, which is Allow. Line order in the file does not matter.
What does the $ at the end of a rule do?
It anchors the rule to the end of the URL. Allow: /search/help$ matches /search/help and nothing longer, so /search/help?lang=fr and /search/help/faq fall through to whatever else matches.
Does the query string count when rules are matched?
Yes. Rules are compared against the path plus the query string, so Disallow: /*?sort= blocks /shoes?sort=price. The #fragment is dropped, and the scheme and host are not part of the match.
Is a URL blocked by robots.txt kept out of Google?
No. Disallow stops crawling, not indexing. A blocked URL can still appear in results with no snippet if other pages link to it. To keep a page out of the index, let Google crawl it and serve a noindex meta tag or X-Robots-Tag header.
Can this page fetch my live robots.txt?
Yes. Type a domain or page URL under Fetch from site and ToolPetal's server requests https://that-host/robots.txt once, identifying itself as ToolPetalBot/1.0. It follows up to 5 redirects, stops after 8 seconds or 1 MB, and hands the text to your browser. The file is not stored or logged. Matching then runs in your browser as usual. Private, local, and internal addresses are refused, and each visitor gets 20 fetches a minute.
Google retired its robots.txt Tester. What replaced it?
Search Console now has a robots.txt report that shows which robots.txt files Google found, when it last fetched them, and any parse errors. It does not test a single URL against your rules. This page fills that gap, and URL Inspection in Search Console tells you whether a live URL was blocked.
What happens if the robots.txt fetch returns 404 or 500?
The tester shows the status code and what Google does with it. A 4xx such as 404 means Google treats the site as having no robots.txt, so every URL is crawlable. A 5xx means Google treats the whole site as disallowed for a while and retries, and after about 30 days it uses its last cached copy or crawls as if there were no file. A site that blocks unknown bots may answer ToolPetalBot differently than it answers Googlebot, so confirm a surprising result in Search Console's robots.txt report.