New to Kestra?
Use blueprints to kickstart your first workflows.
Follow HTTP redirects, detect loops and excessive hops, save the chain, and alert Slack on failures.
id: http-redirect-chain-audit
namespace: company.team
description: |
Follow an HTTP redirect chain, detect loops and excessive hops, and preserve
the final destination and every intermediate URL for operations review.
triggers:
- id: daily
type: io.kestra.plugin.core.trigger.Schedule
cron: "30 6 * * *"
disabled: true
inputs:
- id: url
type: STRING
description: Starting URL to inspect.
defaults: https://example.com
required: true
- id: max_hops
type: INT
description: Maximum permitted redirects.
defaults: 3
required: true
- id: notify_slack
type: BOOL
description: Alert when the chain fails or exceeds max_hops.
defaults: true
tasks:
- id: inspect_redirects
type: io.kestra.plugin.scripts.python.Script
description: Follow redirects without hiding intermediate responses.
taskRunner:
type: io.kestra.plugin.core.runner.Process
env:
URL: "{{ inputs.url }}"
MAX_HOPS: "{{ inputs.max_hops }}"
script: |
import json, os, urllib.error, urllib.request
current, max_hops = os.environ["URL"], int(os.environ["MAX_HOPS"])
chain, seen, error = [], set(), None
try:
for _ in range(max_hops + 2):
if current in seen:
error = "redirect loop detected"
break
seen.add(current)
request = urllib.request.Request(current, method="GET", headers={"User-Agent": "kestra-redirect-chain-audit"})
opener = urllib.request.build_opener(urllib.request.HTTPRedirectHandler())
with opener.open(request, timeout=20) as response:
next_url = response.geturl()
chain.append({"url": current, "status": response.status, "resolved_url": next_url})
if next_url == current:
break
current = next_url
else:
error = "redirect chain exceeded safety limit"
except (urllib.error.URLError, TimeoutError, OSError) as exc:
error = f"{type(exc).__name__}: {exc}"
redirects = max(len(chain) - 1, 0)
if redirects > max_hops:
error = f"redirect count {redirects} exceeds maximum {max_hops}"
report = {"start_url": os.environ["URL"], "final_url": current, "redirects": redirects, "chain": chain, "error": error, "passed": error is None}
print("::" + json.dumps({"outputs": {"report": report, "failed_count": int(error is not None)}}) + "::")
- id: write_report
type: io.kestra.plugin.core.storage.Write
description: Persist the complete redirect chain.
content: "{{ outputs.inspect_redirects.vars.report | toJson }}"
extension: ".json"
- id: alert_if_failed
type: io.kestra.plugin.core.flow.If
description: Alert when redirects loop, fail, or exceed the hop budget.
condition: "{{ outputs.inspect_redirects.vars.failed_count > 0 and
inputs.notify_slack }}"
then:
- id: alert_slack
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
messageText: "Redirect audit failed for {{ inputs.url }}: {{
outputs.inspect_redirects.vars.report.error }}. Evidence: {{
outputs.write_report.uri }}"
outputs:
- id: report
type: URI
value: "{{ outputs.write_report.uri }}"
- id: final_url
type: STRING
value: "{{ outputs.inspect_redirects.vars.report.final_url }}"
Catch broken migrations and redirect regressions before search engines or
customers encounter them. Configure a starting url and max_hops; the
report records every intermediate URL and the final destination.
Prerequisites: outbound HTTPS access and SLACK_WEBHOOK_URL for alerts.
Run manually before enabling the daily schedule. A loop, network error, or
chain longer than the configured budget fails the audit.
Links: Python Script, Slack. Created by yashspidey.
