Links and Redirects
Every redirect your public web applications emit is a place an attacker can send your users. Links and Redirects inventories those Location hops across your scanned web surface, classifies each as internal or external, scores it for impersonation and off-site risk, and gives you a status workflow to triage and accept them.
Overview

A redirect is an HTTP response from one of your web applications that sends the browser somewhere else — a 301/302 to another path, another host, a shortener, or an external domain. ShadowMap captures these during the same web-application scan that powers Web Applications, then surfaces them here as a reviewable list.
The page is a table-first triage workspace:
- A metrics strip at the top (collapsible under the Analytics toggle) summarises total redirects, malicious/suspicious/safe counts, and external count.
- An analytics panel (in the same collapsible section) charts a 30-day detection trend, an internal-vs-external donut, and a status-distribution bar.
- Status tabs (All / New / Reviewed / Flagged / Accepted) split the list by triage state.
- The table lists one row per redirect endpoint, with source URL, redirect target, type, threat status, risk score, review state, and last-scanned date.
- A detail drawer opens on row click with the full risk breakdown and triage actions.
Each redirect carries three independent classifications that are easy to conflate — keep them straight:
| Dimension | Values | What it answers |
|---|---|---|
| Type | Internal, External | Does the redirect stay on your own domain (same eTLD+1 / relative path) or send the user off-site? |
| Status | Safe, Suspicious, Malicious | Threat level of the destination, computed by the redirect scorer. |
| Review | New, Reviewed, Flagged, Accepted | Your analyst triage decision — separate from the automated threat status. |
How it works
Most of the mechanics here are not visible in the UI. This section is the part you cannot infer by clicking around.
Where redirects come from
Links and Redirects is not a separate crawl. It is built from the raw HTTP responses (http_apps) collected by your scheduled web-application scan, during the build:inventory step.
Each scanned HTTP response is sorted into exactly one of three buckets:
- Promoted to an application — it serves real content and becomes a row in Web Applications.
- Captured as a redirect — it carries a non-empty
Locationtarget and the app-inventory promotion gate rejects it as a redirect rather than a real app. These land here. - Dropped as noise — everything else.
So a redirect in this module is, by definition, a response your scanner saw that pointed the browser elsewhere instead of serving a page. The source_url column is the application URL that emitted the redirect; the redirect column is where it was sending traffic.
Stable identity across scans
A redirect endpoint is keyed by a hash of proto + host + port + path (IP-less, the same formula used for the application inventory's cdn_hash). This matters because the underlying scan-response ID is regenerated on every session — the hash is what lets ShadowMap recognise "the same redirect endpoint" week over week even as the raw scan rows churn.
A consequence: if the same source URL starts pointing at a new target, the existing row is updated in place — the redirect target, type, and threat status are re-scored, but the row's identity and your review/triage decision on it are preserved. You do not lose your triage state when the destination changes.
Lifecycle: open, reopened, closed
Each redirect has a lifecycle state, set by the sync step, that is distinct from both its threat status and your review status:
| Lifecycle state | Meaning | On the list? |
|---|---|---|
| Open | Observed in the latest scan. | Yes |
| Reopened | Previously closed, then seen again. | Yes |
| Closed | No longer observed. Kept for history; review row preserved. | No |
Miss-smoothing prevents flapping
A redirect is only closed after it has been missed in 2 consecutive scans (CLOSE_AFTER_MISSES = 2). Scanner coverage per session is stochastic, so a single missed scan will not flap a live redirect off your list. The summary metrics, status tabs, and analytics all count only live (open/reopened) redirects — closed ones drop out of every view but their records survive.
How the threat status is scored
The Status (Safe / Suspicious / Malicious) and the Risk Score (0–100) are computed by a deterministic URL scorer at scan time, by comparing the source URL against the redirect target. There is no external reputation lookup in this scorer — it is heuristic and runs entirely on the URL structure.
Absolute rules are evaluated first and short-circuit everything else:
| Condition | Result |
|---|---|
| Empty redirect target | Safe, score 0 |
| Internal redirect (relative path, or same eTLD+1 as the source) | Safe, score 0 |
Destination host is a known-trusted brand domain (e.g. google.com, microsoft.com, apple.com, cloudflare.com) | Safe, score 0 |
Target matches a known phishing/impersonation pattern (e.g. obfuscated paypal…com, punycode xn--, login-verify, free-gift) | Malicious, score 100 |
If none of those fire, the scorer adds up weighted risk signals on the destination:
| Signal | Weight | Why it's risky |
|---|---|---|
| Redirect to a raw IP address | +80 | Legitimate sites rarely redirect to bare IPs |
Punycode / IDN (xn--) | +70 | Homograph / lookalike-domain attacks |
Suspicious path token (login, verify, secure, wallet, bank, payment, checkout, …) | +50 | Credential-harvest / payment-lure indicators |
URL shortener (bit.ly, t.co, tinyurl.com, goo.gl, …) | +45 | Hides the true destination |
Risky TLD (.ru, .cn, .tk, .ml, .xyz, .zip, .top, …) | +30 | Disproportionately abused TLDs |
Protocol-relative URL (//host) | +30 | Inherits the page's scheme; common in injection |
| HTTP destination (no TLS) | +20 | Downgrades the user to an unencrypted hop |
| Deep subdomain nesting (4+ labels) | +15 | a.b.c.d.example.com-style obfuscation |
| Unknown (non-whitelisted) destination | +10 | Small baseline weight for unrecognised hosts |
A trusted-brand destination subtracts 50 from the total. The score is clamped to 0–100, then mapped to a status by threshold:
- Score ≥ 70 → Malicious
- Score 30–69 → Suspicious
- Score < 30 → Safe
The detail drawer's Risk Factors list shows exactly which signals fired for a given redirect (e.g. shortener, risky_tld, http_no_tls), so you can see why a row was scored the way it was.
Internal redirects are always Safe
Any redirect that stays on the same registrable domain as its source (same eTLD+1) or uses a relative path is classified Internal and forced to Safe with score 0 — it never runs the weighted rules. The risk model is specifically about sending users off your domain. Review external redirects first.
Review status vs. threat status
The automated Status is the scanner's opinion. The Review status is yours, stored in a separate review record per redirect:
- A redirect with no review record is implicitly New.
- Marking it Reviewed / Flagged / Accepted writes your decision and records the reviewer and timestamp.
- Your review state survives re-scans, target changes, and even a close → reopen cycle.
The status-tab counts come from these review records: New is the live total minus everything you've reviewed, flagged, or accepted.
Understanding the data
Each table row is one redirect endpoint.
| Column | Description | Sortable |
|---|---|---|
| Source URL | The application URL that emitted the redirect (standard ports omitted, path normalised). | Yes |
| Redirect URL | The Location target the browser is sent to. | Yes |
| Type | Internal (blue) or External (orange). | Yes |
| Status | Threat level — Safe (green), Suspicious (orange), Malicious (red). | Yes |
| Risk Score | 0–100 numeric score behind the status. Coloured by band: ≥70 critical, 30–69 high, <30 low. | Yes |
| Review | Your triage state — new / reviewed / flagged / accepted. | No |
| Last Scanned | When the redirect was last observed. Default sort (newest first). | Yes |
Sort and the metrics tell different stories
The metrics strip and analytics count live redirects only and reflect the whole company. The table reflects the active status tab plus any filters. A high External or Malicious count in the strip is your cue to filter the table down and triage.
Filtering and search
The search-filter bar supports four fields, each combinable into AND-conditions:
| Filter | Type | Notes |
|---|---|---|
| Source URL | Free text (contains) | Find redirects originating from a specific app or path. |
| Redirect URL | Free text (contains) | Find everything pointing at a given destination host. |
| Redirect Type | Internal / External | Pick from the dropdown. |
| Redirect Status | Safe / Suspicious / Malicious | Pick from the dropdown. |
Additional quick-filter entry points:
- Metrics strip cards — clicking Malicious, Suspicious, or Safe filters the table to that status.
- Analytics charts — clicking a slice of the type donut, a bar in the status chart, or a point on the trend line applies the matching filter (type, status, or date).
- Status tabs — switching tabs scopes the list to that review state and clears the active filters.
Switching status tabs resets the field filters; metric and chart clicks set a single filter rule.
Detail view
Clicking any row opens the Redirect Detail drawer, which expands on the table row:
- URLs — full source and redirect target (untruncated, monospace).
- Classification — Type, Status, and the Risk Score shown as
score / 100. - Risk Factors — the named signals that contributed to the score (only shown when the scorer flagged at least one).
- Review — current review status and the timestamp it was last reviewed.
- Timestamps — last scanned, and when the redirect was first discovered.
- Actions (write permission only) — Mark Reviewed, Flag, Accept, and Bookmark/Unbookmark.
Taking action
Triage is the point of this module. Actions are available inline on each row, from the detail drawer, and in bulk.
Triage a single redirect
Hover a row (or open its drawer) and use the inline buttons, or use keyboard shortcuts on the focused row:
| Action | Effect |
|---|---|
| Mark Reviewed | You've looked at it; it's understood/expected. |
| Flag | Needs attention or escalation. |
| Accept | Acknowledged and accepted as legitimate/known. |
| Bookmark | Pins it for later — independent of review status. |
Bulk triage
Select rows via the checkboxes (or select all on the page) to reveal the bulk action bar:
- Tick the redirects you want to act on.
- In the bar, choose Reviewed, Flag, Accept, or Reset (back to New).
- Or click Export to download the current selection's view as XLSX.
Status-change and bookmark actions require the write permission on this module; read-only users see the data but not the triage buttons. Every triage and bookmark action is written to the central audit trail (visible under Settings → Audit Logs).
Keyboard shortcuts
The list is fully keyboard-drivable. Press ? to toggle the shortcut overlay.
| Key | Action |
|---|---|
j / ↓ | Next row |
k / ↑ | Previous row |
Enter | Open detail drawer |
Esc | Close drawer / deselect |
Space | Toggle selection |
r | Mark reviewed |
f | Flag |
a | Accept |
b | Toggle bookmark |
? | Toggle this help |
Export
The Export button (in the bulk bar) queues an asynchronous XLSX job using the current filters. A progress toast tracks the job; when it finishes, download the file from the toast. Exports run server-side, so large result sets don't block the page. Use this for compliance evidence or to share a redirect inventory with a GRC team.
Common questions
Is this a separate scan or crawl? No. Redirects are extracted from the same scheduled web-application scan that builds your application inventory. There's no separate schedule to configure — new redirects appear after the next web scan completes.
Why is an external redirect marked Safe? Because its destination host is on the trusted-brand whitelist (e.g. a redirect to microsoft.com or cloudflare.com), which forces Safe and zeroes the score. Trusted brand destinations are not treated as impersonation.
A redirect to a brand we use is flagged Malicious. Why? The malicious-pattern rule catches obfuscated brand impersonation (e.g. microsoft-com.ru or a punycode lookalike). A genuine *.microsoft.com apex is whitelisted and short-circuits to Safe before that rule runs; a lookalike is not, and falls through to Malicious. If you've confirmed the destination is legitimate, Accept it to record that decision.
The risk scoring uses URL heuristics only — is there reputation data? The scorer in this module is deterministic and structural (IP destinations, shorteners, risky TLDs, punycode, suspicious path tokens, missing TLS). It does not call an external reputation service. Treat the score as a prioritisation signal, then verify the destination before acting.
A redirect disappeared from the list — was it deleted? No. If its endpoint wasn't observed in 2 consecutive scans it's marked Closed and hidden from the live list, but its record and your review decision are retained. If the same endpoint is seen again, it reopens.
Does changing my review status change the threat Status? No. Review status (New/Reviewed/Flagged/Accepted) is your triage decision and is stored separately from the scanner-computed threat Status (Safe/Suspicious/Malicious). Accepting a Malicious redirect records your acknowledgement without re-scoring it.
Why do the metric counts not match the visible rows? The metrics strip and analytics count every live redirect for the whole company; the table reflects the active status tab and any filters you've applied. Clear the filters and select the All tab to reconcile.
Related
- Web Applications — the same scan that inventories your apps produces these redirects; the source URL of every redirect is one of your web applications.
- Phishing URLs — where an external redirect target is an impersonation domain attacking your brand, that is phishing, not your own attack surface.
- SSL Certificates — redirects that downgrade to plain HTTP (the
http_no_tlssignal) often correlate with TLS gaps tracked here. - Alerts — high-risk findings across modules surface here for unified triage.
- Severity Levels — how ShadowMap normalises status and risk scoring across modules.