All articles
SEO & GrowthAugust 10, 2026 6 min read

Log File Analysis for Programmatic SEO: What Googlebot Actually Does With Your 100k URLs

GSC tells you what Google indexed. Log files tell you what Googlebot actually did on the way there. Here's how we pull signal from server logs to fix crawl waste on large programmatic sites.

Google Search Console will tell you which pages got indexed. It won't tell you that Googlebot spent 40% of its crawl budget last month re-fetching a faceted URL parameter you thought was blocked. For that, you need the logs.

On programmatic sites — anything north of ~50k URLs — log file analysis stops being a nice-to-have and becomes the only honest source of truth about what search engines are doing on your infrastructure. This is how we approach it.

Why GSC Isn't Enough at Scale

GSC's Crawl Stats report is sampled, aggregated, and lags by a few days. It groups requests into buckets ("By response", "By file type", "By purpose") but doesn't let you slice by URL pattern, template, or query string. On a site with 200k programmatic pages spread across 12 templates, that aggregation hides everything that matters.

A few things we've found only in raw logs:

  • Googlebot hammering a ?sort= parameter that was supposed to be canonicalised (it was, in HTML — but the internal links still pointed at the parameterised version)
  • A 15% share of crawl going to /api/ routes that returned 200 OK with a JSON body
  • One template's URLs getting crawled 8x more often than a template with similar traffic, because of a stray sitemap entry
  • Fake Googlebot traffic inflating what looked like healthy crawl activity

None of these show up cleanly in GSC. All of them cost money in wasted crawl and, worse, delay indexation of pages that actually matter.

What a Useful Log Pipeline Looks Like

You don't need a fancy vendor tool to start. You need:

  1. Access to raw access logs from your edge (Cloudflare, Fastly, CloudFront) or origin (Nginx, Apache, ALB).
  2. A place to store them cheaply — S3 or GCS with lifecycle rules.
  3. A query engine — Athena, BigQuery, DuckDB, or just Pandas if the volume is small enough.
  4. A verification step for bot identity.
  5. A join back to your URL inventory so you know which template each hit belongs to.

That last point is the one most teams skip, and it's the one that makes the analysis useful.

Verifying Googlebot Is Actually Googlebot

User-agent strings lie. Anyone can send Googlebot/2.1 in a header. Google publishes IP ranges you can match against, and the classic verification is a reverse DNS lookup followed by a forward lookup — the hostname should end in googlebot.com, google.com, or googleusercontent.com.

import socket

def verify_googlebot(ip: str) -> bool:
    try:
        host = socket.gethostbyaddr(ip)[0]
        if not host.endswith(('.googlebot.com', '.google.com')):
            return False
        forward = socket.gethostbyname(host)
        return forward == ip
    except socket.herror:
        return False

Do this once per unique IP, cache the result for a week, and store a verified_bot boolean alongside every log row. In one audit we ran, roughly a fifth of hits claiming to be Googlebot failed verification. Filter those out before you do anything else, or your "crawl budget" numbers will be fiction.

The Join That Makes Logs Useful

Raw log rows look like this:

66.249.66.1 - - [12/Mar/2026:03:14:22 +0000] "GET /listings/berlin/2-bed/mitte HTTP/2" 200 18432

On its own, that's one URL. Multiply by 40 million rows a month and you have noise. The trick is to tag every row with a template and a priority tier by joining against your URL inventory — the same table your sitemap generator uses.

A minimal schema:

CREATE TABLE url_inventory (
  url            STRING,
  template       STRING,   -- e.g. 'city_bedrooms_area'
  tier           INT,      -- 1 = money page, 3 = long tail
  indexable      BOOLEAN,
  last_updated   TIMESTAMP
);

Once you can group log hits by template and tier, questions get answerable:

  • How is Googlebot's time distributed across templates versus how our traffic is distributed?
  • What share of crawl is going to indexable = false URLs? (This should be near zero. It never is.)
  • Which templates have a worsening crawl-to-index ratio month over month?

The Metrics That Actually Matter

Forget "total Googlebot hits." That number moves for reasons unrelated to your work. Track these instead.

Crawl Share vs Traffic Share by Template

For each template, compute its share of verified Googlebot requests and its share of organic clicks (from GSC). Templates where crawl share massively exceeds traffic share are candidates for pruning, noindex, or robots.txt disallow. Templates where traffic share exceeds crawl share are being under-served — usually a sign of weak internal linking or slow response times causing Google to back off.

Response Code Distribution

On a healthy programmatic site, verified Googlebot should see:

  • 200s dominating (typically 85%+)
  • 301s in the low single digits
  • 404s under 2%
  • 5xx effectively zero

When 404s creep past 5%, something is generating URLs that shouldn't exist — often a template that renders links to pages that were deleted, or a sitemap that's out of sync with the inventory. When 5xx appears at all, Googlebot will slow its crawl rate, which quietly delays indexation of new pages for weeks.

Time to First Crawl

For every new URL you publish, measure the gap between publication timestamp and first verified Googlebot hit. Segment by template. If tier-1 pages are seeing first crawl in hours but tier-3 pages take three weeks, that's your indexation bottleneck, and no amount of on-page optimisation will fix it until you address discoverability.

Orphan Crawl

URLs that appear in logs but not in your inventory. These are almost always a problem: old redirects being re-fetched, parameterised URLs from external links, or — occasionally — pages your CMS is exposing that your team didn't know about.

A Concrete Workflow

Here's the loop we run monthly for programmatic SEO clients:

  1. Ingest last 30 days of edge logs into object storage, partitioned by day.
  2. Filter to verified Googlebot (and Bingbot if it matters for the market).
  3. Join each hit to the URL inventory.
  4. Compute crawl share, response mix, and time-to-first-crawl per template.
  5. Diff against last month.
  6. Produce a ranked action list — usually 5 to 10 items — with an owner and an expected impact.

The action list is the deliverable. Everything before it is plumbing.

An Example Action Item

Finding: Template location_service_price accounts for 22% of verified Googlebot requests but 3% of organic clicks. 41% of its crawl hits return a soft-404 (empty state page).

Action: Add noindex to empty-state renders, remove empty URLs from sitemap, and disallow the low-inventory sub-pattern in robots.txt.

Expected impact: Reclaim ~18% of crawl budget for tier-1 templates; monitor time-to-first-crawl on new tier-1 URLs over the next 30 days.

That's the shape of output that actually gets shipped, because it names a template, a number, and a specific change.

Common Traps

Sampling too aggressively. If you sample logs at 1%, you'll miss the long tail — which on a programmatic site is where most of the interesting behaviour lives. Store everything, sample at query time if needed.

Trusting user agents. Already covered, but worth repeating. Always verify.

Ignoring CDN cache. If your CDN serves Googlebot from cache without hitting origin, your origin logs will undercount. Either analyse edge logs or make sure your CDN passes bot requests through.

Confusing crawl with indexation. A page being crawled doesn't mean it's indexed. Cross-reference with the URL Inspection API or GSC's Index Coverage export to close that loop.

One-off analysis. Running this once is theatre. The value compounds when you run it every month and can diff against the previous state.

Where We'd Start

If you've never looked at your logs, spend a day doing this: pull one week of edge logs, verify Googlebot properly, join to whatever URL list you have, and just look at the response code distribution by URL pattern. You will find something broken. It's almost guaranteed on any site over 50k URLs.

From there, build the monthly loop before you build dashboards. Dashboards get stale; a queryable partitioned table in BigQuery or Athena stays useful for years. Once the pipeline is stable, wire the top-line metrics into whatever your team already reads — Slack, a weekly digest, or a page on your internal wiki.

If you'd rather not build this from scratch, our team does this kind of technical SEO work as part of engagements on our services page. But honestly, most engineering-led teams can stand up a workable version in a week. The hard part isn't the code — it's committing to actually reading the output every month.

#SEO#Programmatic SEO#Technical SEO#Analytics#Log Analysis

Want a team like ours?

72Technologies builds production software for the kind of teams who actually read this blog.

Start a project