Skip to content
Search visibility6 min read

Reading server logs to find out what crawlers actually do

Search Console tells you what Google concluded. Server logs tell you what it actually requested, when, and what your server returned. When indexing behaviour makes no sense from the reports alone, the logs almost always contain the answer.

Part ofTechnical SEO Engineering

Log analysis has a reputation for being specialist work requiring expensive tooling. It is neither. A few command-line utilities against a week of access logs answers questions no other data source can, and the questions it answers are usually the ones blocking a diagnosis.

What only logs can tell you

QuestionSearch ConsoleServer logs
Which URLs were crawled?Aggregated samplesEvery request, exactly
How often is this page crawled?Not available per URLPrecisely, with timestamps
What status did the crawler receive?Summarised by categoryPer request, including intermittent errors
Are crawlers wasting time on junk URLs?Partially inferableDirectly countable
Which pages are never crawled at all?Not availableBy comparing against your sitemap
Did that 5xx spike affect crawling?Delayed and aggregatedImmediately, correlated by minute

Verify the crawler before trusting the data

User-agent strings are trivially spoofed, and a meaningful share of traffic claiming to be Googlebot is not. Analysing unverified logs produces conclusions about scrapers rather than search engines.

bash
# Reverse DNS, then forward DNS on the result. Real Googlebot resolves
# to googlebot.com or google.com, and the forward lookup returns the
# original IP. Anything else is impersonating.

verify() {
  host=$(dig +short -x "$1" | sed 's/\.$//')
  case "$host" in
    *.googlebot.com|*.google.com)
      dig +short "$host" | grep -q "^$1$" && echo "$1 verified ($host)" \
                                         || echo "$1 FORWARD MISMATCH" ;;
    *) echo "$1 NOT Googlebot ($host)" ;;
  esac
}

# Check the busiest self-declared Googlebot IPs in the log.
awk '/Googlebot/ {print $1}' access.log | sort | uniq -c | sort -rn \
  | head -20 | while read -r count ip; do verify "$ip"; done

Where is crawl budget actually going?

The first genuinely useful output is a ranked list of what crawlers spend their requests on. It is frequently surprising, and it is where crawl waste becomes visible rather than theoretical.

bash
# Most-crawled URLs. If parameter or filter URLs dominate the top of
# this list, that is your crawl waste, quantified.
awk '/Googlebot/ {print $7}' access.log \
  | sort | uniq -c | sort -rn | head -40

# Crawl distribution by section — which parts of the site get attention.
awk '/Googlebot/ {split($7, p, "/"); print "/" p[2]}' access.log \
  | sort | uniq -c | sort -rn

# How much crawl goes to URLs carrying query strings at all.
awk '/Googlebot/ {print $7}' access.log \
  | awk '{if ($0 ~ /\?/) q++; else c++} END {
      printf "clean: %d (%.1f%%)  parameterised: %d (%.1f%%)\n", \
      c, 100*c/(c+q), q, 100*q/(c+q) }'

If parameterised URLs account for a large share of crawl on a site whose valuable content lives at clean URLs, you have found a concrete, quantified problem — and the robots.txt or faceting policy that fixes it.

Status codes the crawler received

Search Console reports errors in aggregate and with delay. Logs show exactly which URLs returned what, including intermittent failures that never accumulate enough to surface in a report.

bash
# Status code distribution for verified crawler traffic.
awk '/Googlebot/ {print $9}' access.log | sort | uniq -c | sort -rn

# Every URL that served a 5xx to a crawler — fix these first, since
# server errors actively suppress crawl capacity.
awk '/Googlebot/ && $9 ~ /^5/ {print $9, $7}' access.log \
  | sort | uniq -c | sort -rn | head -30

# 404s receiving crawler traffic: either unmapped redirects from a
# past migration, or links pointing at pages that never existed.
awk '/Googlebot/ && $9 == 404 {print $7}' access.log \
  | sort | uniq -c | sort -rn | head -30

Finding orphaned and never-crawled pages

Two opposite problems, both found by comparing your sitemap against the log: pages crawlers never visit, and pages crawlers visit that are not in your sitemap at all.

bash
# Extract sitemap paths and crawled paths, then diff the two sets.
curl -s https://example.com/sitemap.xml \
  | grep -oE '<loc>[^<]+' | sed 's|<loc>||' \
  | sed -E 's|https?://[^/]+||' | sort -u > sitemap-paths.txt

awk '/Googlebot/ {print $7}' access.log \
  | sed 's/?.*//' | sort -u > crawled-paths.txt

# In the sitemap but never crawled — discovery or demand problem.
comm -23 sitemap-paths.txt crawled-paths.txt | head -40

# Crawled but not in the sitemap — often orphans, old URLs, or
# URLs you did not know were indexable.
comm -13 sitemap-paths.txt crawled-paths.txt | head -40

The first list tells you which content is not being discovered, usually because nothing links to it prominently. The second frequently reveals entire URL patterns nobody intended to expose.

Crawl frequency as a proxy for perceived value

How often a page is recrawled correlates with how important a search engine currently considers it. Comparing recrawl frequency between templates shows where perceived value sits.

bash
# Distinct days on which each URL was crawled, over the log period.
# A commercially important page crawled once in a month is a signal.
awk '/Googlebot/ {
  split($4, d, ":"); gsub(/\[/, "", d[1]);
  print $7, d[1]
}' access.log | sort -u | awk '{print $1}' \
  | uniq -c | sort -rn | head -30

A realistic cadence

Log analysis does not need to be continuous. It needs to happen at the moments when it answers a question.

  1. After a migration — daily for two weeks, watching for 404s and unmapped URLs receiving crawler traffic.
  2. When indexing behaviour contradicts the reports — logs resolve the contradiction.
  3. Quarterly on large sites — to catch crawl waste that has accumulated since the last review.
  4. After any incident involving 5xx errors — to confirm crawl rate recovered rather than staying suppressed.

Questions

How do I verify a request really came from Googlebot?

Run a reverse DNS lookup on the IP, confirm it resolves to googlebot.com or google.com, then run a forward lookup on that hostname and confirm it returns the original IP. Both directions must agree. Alternatively, match against Google's published crawler IP ranges, which is faster for large log volumes.

How much log data do I need for useful analysis?

A week is enough to see crawl distribution and error patterns on most sites. A month is better for crawl-frequency analysis, since infrequently crawled pages need a longer window to appear at all. For migration monitoring, daily analysis of the previous day matters more than depth.

Do I need a dedicated log analysis tool?

Not to start. awk, sort, uniq and comm answer most questions against raw access logs, and everything in this article is doable that way. Dedicated tools help when log volume is large enough that command-line processing becomes slow, or when you want ongoing dashboards rather than point-in-time answers.

What if my host does not give me access logs?

Check the CDN first — Cloudflare, Fastly and most edge providers expose request logs even when the origin platform does not. Failing that, Search Console's crawl stats report is a much coarser substitute: it gives aggregate request counts, response times and status distribution, but no per-URL detail.

Want this applied to your systems?

Most of what is described here begins as an assessment: a fixed-scope review producing a prioritised written report you can act on with any supplier.

Prefer email? hello@kornspire.com