Technical SEO
Server log file analysis for SEO: complete guide

Log files are the single source of truth about how search bots see your site. Without analysing them you are flying blind: you don't know which pages Googlebot crawls, which it ignores, where it hits errors, and how often it comes back. We cover the full pipeline: from obtaining logs to concrete changes in robots.txt and sitemap.
Why analyse log files
Google Search Console only shows aggregated data with a three-day delay. Screaming Frog simulates a browser, not Googlebot. Web server log files are the only source that records every real request from a search bot: the exact timestamp, URL, response status, and crawler IP address.
Log analysis answers questions you cannot solve any other way: which pages Googlebot crawls hourly and which it has not visited for months; where the crawler encounters 404s and 500s; whether there are infinite redirect loops; whether budget is being wasted on parametric duplicates or pages behind a login wall. This data is exactly what you need to make informed decisions about crawl budget management.
What log analysis delivers
Real requests
Every HTTP request from a crawler is captured in the log file — no sampling, no aggregation delays
Time accuracy
Second-level timestamps let you track crawl patterns by day of week and time of day
Bots in one source
Googlebot, Bingbot, Yandex and others — all crawlers in one file, comparable in a single view
Data delay
Logs are available immediately — no waiting for a recrawl or GSC report refresh
How to access log files
The path to your logs depends on the web server and hosting provider. On most VPS and dedicated servers, log files are accessible directly via SSH. For analysis you typically need access logs for the last 30–90 days: that window is long enough to reveal seasonal patterns while keeping data current.
| Server / platform | Default log path | Notes |
|---|---|---|
| Nginx | /var/log/nginx/access.log | Rotated via logrotate. Archives: access.log.1, access.log.2.gz etc. |
| Apache | /var/log/apache2/access.log (Debian/Ubuntu) or /var/log/httpd/access_log (RHEL) | Format may be CLF or Combined — check the LogFormat directive |
| cPanel / WHM | ~/access-logs/<domain> or File Manager → Logs | Often split by domain. Raw Access available in cPanel interface |
| Cloudflare Workers / Enterprise | Logpush API → S3, R2, GCS | Enterprise plan only. For Free/Pro — Workers Analytics Engine |
| AWS CloudFront | S3 bucket configured in Distribution settings → Logging | Logs every 5–10 minutes, gzip compressed |
| Google Cloud / GCS | Cloud Logging → export to BigQuery or GCS | Ideal for large volumes — run SQL queries directly in BigQuery |
# Nginx: view the last 1000 lines of the access log
tail -n 1000 /var/log/nginx/access.log
# Size of the current log file
du -sh /var/log/nginx/access.log
# Decompress and merge rotated logs
zcat /var/log/nginx/access.log.*.gz | cat - /var/log/nginx/access.log > all_access.log
# Line count (≈ number of requests) in the merged file
wc -l all_access.logrotate 30 in /etc/logrotate.d/nginx in advance — deleted logs cannot be recovered.Log record structure: CLF format
Most servers use the Combined Log Format (CLF) by default — an extended version of Common Log Format. Here are the fields that matter for SEO analysis.
# Combined Log Format record (Nginx/Apache)
66.249.66.1 - - [19/May/2026:14:32:01 +0000] "GET /blog/seo-guide/ HTTP/1.1" 200 45231 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
│ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ └─ User-Agent
│ │ │ │ │ │ │ └───── Referer
│ │ │ │ │ │ └─────────── Bytes sent
│ │ │ │ │ └──────────────── HTTP status
│ │ │ │ └─────────────────────────────────────────── Request
│ │ │ └────────────────────────────────────────────────────────────────────────── Timestamp
│ │ └───────────────────────────────────────────────────────────────────────────── Auth user
│ └─────────────────────────────────────────────────────────────────────────────── Ident
└─────────────────────────────────────────────────────────────────────────────────────────── IP address| Field | SEO relevance | What to look for |
|---|---|---|
| IP address | High | IP ranges of Googlebot, Bingbot, Yandex — used to filter real crawlers |
| Timestamp | High | Crawl frequency, time of last visit to specific URLs |
| Request (method + URL) | Critical | Which URLs are crawled: needed pages, duplicates, parameters, non-existent paths |
| HTTP status | Critical | 200 (good), 301/302 (redirects), 404 (dead links), 500 (server errors) |
| Bytes sent | Medium | Abnormally small size — sign of an empty page or stub response |
| User-Agent | Critical | Distinguish Googlebot from other bots and real users |
Filtering: isolating crawlers
Raw logs contain a mix of user requests, monitoring pings, spam bots, and search crawlers. For SEO analysis you need to extract only search robots. The most reliable approach is to filter by User-Agent string and then verify the IP via DNS.
# Extract all Googlebot requests
grep -i 'googlebot' /var/log/nginx/access.log > googlebot.log
# Extract all SEO crawlers
grep -iE '(googlebot|bingbot|yandex|ahrefsbot|semrushbot|mj12bot)' \
/var/log/nginx/access.log > all_bots.log
# Count requests by crawler
grep -oiE '(googlebot|bingbot|yandexbot|ahrefsbot|semrushbot)' \
/var/log/nginx/access.log | sort | uniq -c | sort -rn
# Verify IP via reverse DNS (confirm it's the real Googlebot)
host 66.249.66.1
# Should return: crawl-66-249-66-1.googlebot.com
# Then forward DNS:
host crawl-66-249-66-1.googlebot.com
# Should return the same IP — then it's a legitimate GooglebotGooglebot string. For accurate filtering, verify IPs via reverse DNS: the real Googlebot will resolve to *.googlebot.com. Google publishes official IP ranges through the Special IP Addresses API.Key SEO metrics
After filtering, move to analysis. Here are six metrics that deliver the most insights for the least effort.
How many times Googlebot visited a specific URL over the period. Pages with high PageRank and frequent updates are crawled more often. If an important page was visited just once in 30 days, Google considers it low-value.
Target: key pages ≥ once every 3 daysShare of 4xx and 5xx in Googlebot requests. 404s waste crawl budget. 500s signal systemic problems. A high error rate reduces Google's trust in the site.
Benchmark: 4xx < 3%, 5xx < 0.5%What fraction of Googlebot requests end in a 301 or 302. Redirects consume budget and slow re-indexing. Redirect chains (A → B → C) are especially damaging.
Benchmark: 3xx < 10% of all bot requestsSame content under different URLs: with and without www, with and without trailing slash, with UTM parameters. Each variant gets crawled separately — a direct leak of crawl budget.
Target: one canonical URL per piece of contentKey pages (categories, landing pages, new articles) that Googlebot never requested in 30 days. This indicates problems with internal linking or a block in robots.txt.
Target: 0 important pages without a crawl in 30 daysPages Google is crawling that it shouldn't: admin panels, session pages, test environments, parametric duplicates. They waste budget and sometimes introduce security risk.
Target: 0 sensitive URLs in Googlebot logsAnalysis tools
Three tools cover 95% of log analysis tasks: the command line for quick checks, a specialised GUI for deep analysis, and Python/BigQuery for large data volumes and automation.
Bash: quick analysis without installation
# Top 20 URLs Googlebot crawls most often
grep -i 'googlebot' access.log \
| awk '{print $7}' \
| sort | uniq -c | sort -rn | head -20
# Response status distribution for Googlebot
grep -i 'googlebot' access.log \
| awk '{print $9}' \
| sort | uniq -c | sort -rn
# URLs returning 404 to Googlebot
grep -i 'googlebot' access.log \
| awk '$9 == 404 {print $7}' \
| sort | uniq -c | sort -rn | head -50
# Googlebot request count by day
grep -i 'googlebot' access.log \
| awk '{print $4}' \
| cut -d: -f1 | tr -d '[' \
| sort | uniq -c
# Parametric URLs in Googlebot requests
grep -i 'googlebot' access.log \
| awk '{print $7}' \
| grep '?' \
| sort | uniq -c | sort -rn | head -30Screaming Frog Log File Analyser
A paid tool ($259/year) but indispensable for regular analysis. It loads the log file, parses it, and builds reports: Bot vs Browser segmentation, status dashboards, directory-level crawl heat maps, and a list of pages with no crawl. Supports Nginx, Apache, IIS, CloudFlare, Fastly, and custom CSV formats.
Python: flexible analysis for large files
import re
from collections import Counter
LOG_PATTERN = re.compile(
r'(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] '
r'"(?P<method>\S+) (?P<url>\S+) \S+" '
r'(?P<status>\d{3}) (?P<size>\S+) '
r'"(?P<referer>[^"]*)" "(?P<ua>[^"]*)')
def parse_log(path: str, bot_pattern: str = 'googlebot') -> list[dict]:
entries = []
with open(path) as f:
for line in f:
m = LOG_PATTERN.match(line)
if not m:
continue
if bot_pattern.lower() not in m.group('ua').lower():
continue
entries.append({
'ip': m.group('ip'),
'url': m.group('url'),
'status': int(m.group('status')),
'ua': m.group('ua'),
})
return entries
entries = parse_log('access.log')
# Top 20 URLs by crawl frequency
url_counts = Counter(e['url'] for e in entries)
for url, count in url_counts.most_common(20):
print(f'{count:>6} {url}')
# 404 errors
errors = [e['url'] for e in entries if e['status'] == 404]
for url, count in Counter(errors).most_common(20):
print(f'{count:>6} 404 {url}')Crawl patterns: what to look for
Raw metrics are meaningless without interpretation. Here are specific patterns worth hunting for in Googlebot logs.
Pattern 1: crawled but not indexed
Googlebot visits a page regularly, but it never appears as "Indexed" in GSC. Causes: noindex meta tag, X-Robots-Tag block in HTTP headers, thin content, canonical pointing to another page. Compare the list of regularly crawled URLs against indexed pages from the GSC API — discrepancies expose the problem pages.
Pattern 2: crawl storms
A sudden spike of Googlebot requests in a short window is often a sign of infinitely generated URLs: facets, paginated parameter URLs, session tokens in URLs. A crawl storm can exhaust your entire budget in hours and put real load on the server. Plot requests as a histogram by hour — abnormal spikes are immediately visible.
Pattern 3: new pages ignored
A new page was created three weeks ago, added to the sitemap, but Googlebot shows zero requests in the logs. Causes: no internal links to the page, the sitemap is not being re-crawled by the bot, the page is at too deep a nesting level. Check new URLs with the GSC inspect tool and look for the first appearance date in the logs.
Pattern 4: resources blocking rendering
Googlebot requests not just HTML pages but also CSS, JS, and images. If critical resources are blocked in robots.txt, the bot cannot render the page and may undervalue it. Check in the logs: what percentage of Googlebot requests target static resources, and whether any required JS files are blocked.
Common issues and fixes
| Issue in logs | Root cause | Fix |
|---|---|---|
| Many 404s from Googlebot | Deleted pages that still have external or internal links pointing to them | 301 to the nearest live page + remove broken internal links |
| Parametric URLs in crawl | UTM tags, sorts, and sessions not blocked in robots.txt | Disallow in robots.txt + canonical to clean URL on the pages themselves |
| Redirect chains (A→B→C) | Accumulated redirects after restructuring | Reduce to a single hop — update all outgoing links |
| /admin, /staging pages in logs | Service sections not blocked | Disallow /admin/ in robots.txt + HTTP auth on staging |
| 5xx responses to Googlebot | Server overload during traffic peaks or backend errors | Optimise TTFB, add caching, configure crawler rate limiting |
| Important page not crawled | No internal links, too deep in hierarchy, not in sitemap | Add to sitemap + add internal links from key pages |
| www / non-www duplicates | No 301 redirect configured to the canonical domain version | 301 from non-www to www (or vice versa) at nginx/Apache level |
| robots.txt blocks needed JS/CSS | Template Disallow /assets/ or /static/ | Remove the block for critical resources needed for rendering |
# Detect redirect chains in logs
# Step 1: extract all URLs with 301/302 status for Googlebot
grep -i 'googlebot' access.log \
| awk '$9 ~ /^30[12]$/ {print $7}' | sort -u > redirects.txt
# Step 2: check if any of those URLs are also redirect sources
while read url; do
if grep -q "GET $url " redirects.txt 2>/dev/null; then
echo "CHAIN: $url"
fi
done < redirects.txt
# Find 5xx errors grouped by URL and hour
grep -i 'googlebot' access.log \
| awk '$9 ~ /^5/ {print $4" "$7}' \
| cut -d: -f1-2 \
| sort | uniq -c | sort -rn | head -20FAQ
Summary
Log file analysis is the most honest SEO audit available. GSC shows aggregates, Screaming Frog simulates, but logs capture every real decision Googlebot makes. The pipeline is straightforward: obtain logs for 30–90 days → filter crawlers by User-Agent with DNS verification → calculate six key metrics → cross-reference with the GSC index → fix discrepancies.
Start with three Bash commands: top 20 crawled URLs, status code distribution, top 404s. Within an hour you will have a concrete list of actions: redirects to set up, URLs to block in robots.txt, pages to add to the sitemap. That is faster and cheaper than any paid audit — the data is already on your server.