{% extends "paxalia/base.html" %} {% load static i18n %} {% block page_title %}{% trans "Broken Links" %}{% endblock %} {% block page_subtitle %}{% trans "Investigate real 404s, crawler probes, and the clients reaching missing paths" %}{% endblock %} {% block extra_css %} {% endblock %} {% block filter_bar %} {% include 'paxalia/includes/filter_bar.html' %} {% endblock %} {% block dashboard_content %}
{% trans "All 404s" %}{{ total_404s }}
{% trans "Real / normal" %}{{ human_404s }}
{% trans "Bot / scanner" %}{{ bot_404s }}
{% trans "Unique missing paths" %}{{ unique_missing_paths }}

{% trans "Broken pages to fix" %}

{% trans "Normal 404 requests are separated from automation. Hits, clients, referrers, and first/last sightings help distinguish a genuine broken link from background scanning." %}

{{ date_range_label }}
{% if top_broken_paths %}
{% if can_manage_bot_paths %}{% endif %} {% for row in top_broken_paths %} {% if can_manage_bot_paths %} {% endif %} {% endfor %}
{% trans "Path" %}{% trans "Hits" %}{% trans "IPs" %}{% trans "Referrers" %}{% trans "First seen" %}{% trans "Last seen" %}{% trans "Rule" %}
{{ row.path }} {{ row.count }} {{ row.unique_ips }} {{ row.unique_referrers }} {{ row.first_seen|date:"Y-m-d H:i" }} {{ row.last_seen|date:"Y-m-d H:i" }} {% if row.path != "/" %}
{% csrf_token %}
{% else %}{% trans "Root path" %}{% endif %}
{% else %}
{% trans "No normal 404 paths were recorded in this date range." %}
{% endif %} {% if broken_paths_page.paginator.num_pages > 1 %} {% endif %}

{% trans "Clients reaching real 404s" %}

{% trans "Each client is grouped with the missing paths it requested, making repeated broken-link attempts easier to investigate." %}

{{ human_clients|length }}
{% if human_clients %}
{% for row in human_clients %}
{{ row.ip_hash }}
{{ row.hits }} {% trans "404s" %} · {{ row.unique_paths }} {% trans "missing paths" %}
{% trans "First" %} {{ row.first_seen|date:"Y-m-d H:i" }} · {% trans "Last" %} {{ row.last_seen|date:"Y-m-d H:i" }}
{% for path in row.paths %}{% endfor %}
{% trans "Missing path" %}{% trans "Hits" %}{% trans "Last seen" %}
{{ path.path }}{{ path.count }}{{ path.last_seen|date:"Y-m-d H:i" }}
{% endfor %}
{% else %}
{% trans "No identifiable client IP/hash was recorded for normal 404s." %}
{% endif %}

{% trans "Referrers" %}

{% trans "A referrer indicates another page or external source actively points at the missing URL." %}

{% if top_referrers %}{% for row in top_referrers %}{% endfor %}
{% trans "Referrer" %}{% trans "Hits" %}{% trans "Missing paths" %}{% trans "Last seen" %}
{{ row.referrer }}{{ row.count }}{{ row.unique_paths }}{{ row.last_seen|date:"Y-m-d H:i" }}
{% else %}
{% trans "No referred normal 404s were recorded." %}
{% endif %}

{% trans "Scanner / crawler 404s" %}

{% trans "Automation is kept separate from the work queue. A path rule makes future classification deterministic; the one-click cleanup removes matching historical normal-page records in the current filtered scope." %}

{% if scanner_paths %}
{% if can_manage_bot_paths %}{% endif %} {% for row in scanner_paths %} {% if can_manage_bot_paths %}{% endif %} {% endfor %}
{% trans "Path" %}{% trans "Category" %}{% trans "Hits" %}{% trans "IPs" %}{% trans "First seen" %}{% trans "Last seen" %}{% trans "Rule" %}
{{ row.path }} {{ row.bot_category|default:"Unclassified" }} {{ row.count }}{{ row.unique_ips }} {{ row.first_seen|date:"Y-m-d H:i" }}{{ row.last_seen|date:"Y-m-d H:i" }}{% if row.path != "/" %}
{% csrf_token %}
{% endif %}
{% else %}
{% trans "No bot/scanner 404s were recorded in this date range." %}
{% endif %}

{% trans "Automation clients" %}

{% trans "See which IP/hash is responsible for repeated automated 404 probes and which paths it targeted." %}

{% if bot_clients %}
{% for row in bot_clients %}
{{ row.ip_hash }}
{{ row.hits }} {% trans "404s" %} · {{ row.unique_paths }} {% trans "paths" %}
{% trans "First" %} {{ row.first_seen|date:"Y-m-d H:i" }} · {% trans "Last" %} {{ row.last_seen|date:"Y-m-d H:i" }}
{% for path in row.paths %}{% endfor %}
{% trans "Target" %}{% trans "Hits" %}
{{ path.path }}{{ path.count }}
{% endfor %}
{% else %}
{% trans "No identifiable bot/scanner client IPs were recorded." %}
{% endif %}

{% trans "404 category mix" %}

{% if category_breakdown %}{% for row in category_breakdown %}{% endfor %}
{% trans "Category" %}{% trans "Hits" %}
{{ row.label }}{{ row.count }}
{% else %}
{% trans "No classified bot 404 traffic." %}
{% endif %}
{% endblock %} {% block page_scripts %} {% endblock %}