Mail::...rectors(3pm)

✨ NAME ✨

okay so bestie, welcome to <pink>Mail::...rectors(3pm)</pink> β€” it's part of the User Contributed Perl Documentation, and it's literally the page for the Redirectors plugin: the thing that checks for redirected URLs. that's it, that's the whole vibe check. πŸ’…


🌸 SYNOPSIS 🌸

here's the whole starter pack, copy-paste style, no notes:

loadplugin    Mail::SpamAssassin::Plugin::Redirectors

url_redirector bing.com

body HAS_REDIR_URL          eval:redir_url()
describe HAS_REDIR_URL      Message has one or more redirected URLs

body REDIR_URL_VALID        eval:redir_url_valid()
describe REDIR_URL_VALID    Message has a redirector that returned a valid redirection

body REDIR_URL_CHAINED      eval:redir_url_chained()
describe REDIR_URL_CHAINED  Message has redirected URL chained to other redirectors

body REDIR_URL_CHAINED_DOM      eval:redir_url_chained_domain()
describe REDIR_URL_CHAINED_DOM  Message has redirected URL chained to another redirector on the same domain

body REDIR_URL_MAXCHAIN     eval:redir_url_maxchain()
describe REDIR_URL_MAXCHAIN Message has redirected URL that causes too many redirections

body REDIR_URL_LOOP         eval:redir_url_loop()
describe REDIR_URL_LOOP     Message has redirected URL that loops back to itself

body REDIR_URL_404          eval:redir_url_code('404') # Can check any non-redirect HTTP code
describe REDIR_URL_404      Message has redirected URL returning HTTP 404

πŸ¦‹ DESCRIPTION πŸ¦‹

so this plugin is out here looking for URLs that got redirected by a whole list of URL redirector services. the moment it finds a URL that matches, it sends an HTTP request off to the redirector service and grabs the Location-header, which points at the actual redirected URL. then it drops that URL into the list of URIs that SpamAssassin already extracted, so uri rules and plugins like URIDNSBL can get their hands on it. icons only fr 🫢

it also follows chained redirections β€” that's when a redirected URL redirects to another redirector. the depth limit for that is set with max_redir_url_redirections.

a maximum of max_redir_urls redirected URLs get checked per message (that's 10 by default btw). set it to 0 and HTTP requests are fully off, which means only the redir_url() test keeps working and reporting the redirectors it found.

every single supported rule type for checking redirector URLs and redirection status is documented right up in the SYNOPSIS β€” go look, it's all there. ✨


πŸ’œ NOTES πŸ’œ

this plugin runs before priority 0 so it can modify the parsed URI list before the normal uri rules or the URIDNSBL plugin ever see it. and it should run before the DecodeShortURLs plugin, so that redirected short uris get checked too. literally sequencing matters, iykyk. πŸ‘‘


πŸŽ€ USER SETTINGS πŸŽ€

  • url_redirector domain[/path] [domain[/path]...] β€” default: none. these are the domains that should be treated as a URL redirector. 🌈

domain matching, bestie, listen up:

  • a bare domain (like bing.com) matches that exact host and also www.bing.com.
  • a leading dot (like .sendgrid.com) matches any subdomain of the domain, to any depth. it does NOT match the bare domain itself β€” so if you want both, list the bare domain too (sendgrid.com).

an optional /path can follow the domain, and it restricts matching to URLs whose path begins with that string and ends at a path-segment boundary. /tr/op matches /tr/op, /tr/op/foo, and /tr/op?x=1, but NOT /tr/open β€” because the configured prefix is treated as one or more whole path segments. append a trailing slash (/tr/op/) if you want to require at least one more segment after it (that matches /tr/op/foo but not bare /tr/op). multiple entries for the same domain are additive (allowlist); a URL gets followed if it matches any entry.

a bare-domain entry without a path is equivalent to domain/, which matches any path.

example, slay:

url_redirector bing.com
url_redirector .sendgrid.com
url_redirector .sendibt2.com/tr/cl/

the last line follows https://x.y.sendibt2.com/tr/cl/abc but not https://x.y.sendibt2.com/tr/op/abc. periodt. πŸ’…

  • url_redirector_use_selenium β€” default: 0. turns on the Selenium subsystem. when it's 1, hosts listed in url_redirector_selenium get fetched using Selenium (headless Chrome). when it's 0 (the default), those hosts are skipped entirely β€” and nope, there is no fallback to LWP, because hosts only belong in url_redirector_selenium if their redirects can't be discovered via LWP (like JavaScript-driven redirects, ugh).

this is a runtime kill switch: an operator can disable the Selenium subsystem without touching the host allowlist. while it's off, those hosts just don't get probed β€” but redir_url() still fires for them, so detection rules keep working. 🫢

heads up: because of how Selenium works, redir_url_code() and the redir_url_404 subs will not fire for hosts handled by Selenium. Selenium only reports "redirected" (301) or "not a redirect" (200) β€” not real HTTP status codes.
  • url_redirector_selenium_host β€” default: 127.0.0.1. set the Selenium host to use.
  • url_redirector_selenium_port β€” default: 4444. set the Selenium port to use.
  • clear_url_redirector [domain[/path]] [domain[/path]...] β€” clear the configured url_redirector domains, like if you wanna override default settings from an update channel. give it no arguments and all entries get cleared. name some domains and only those get removed. 🧹

when an entry includes a /path, only that path is removed from that domain's allowlist β€” and the domain entry itself is only dropped once its path list is empty. use domain/ to remove the "match any path" entry that a bare-domain config added.

  • url_redirector_get domain[/path] [domain[/path]...] β€” default: none. domains that should be considered an URL redirector, accessed using the HTTP GET method instead of HEAD. syntax and matching rules are the same as url_redirector: bare domain matches that host and www., a leading dot matches the domain and any subdomain, and an optional /path prefix restricts the match.
  • url_redirector_selenium domain[/path] [domain[/path]...] β€” default: none. domains that should be fetched using Selenium (headless Chrome) instead of LWP. only use this for hosts whose redirects are NOT discoverable via LWP, like JavaScript-driven redirects. syntax and matching rules are the same as url_redirector.
real talk: Selenium executes JavaScript on the destination and always sends GET, which has weaker safety properties than the LWP path β€” so reserve this list for hosts that genuinely require browser-driven redirect unwrapping. 😭

if the same host shows up in both url_redirector_selenium and url_redirector/url_redirector_get, the directive parsed last sets the method, and a warning gets emitted.

the Selenium subsystem is gated by url_redirector_use_selenium. when that flag is off, hosts in this list are skipped entirely (no fallback to LWP, since these hosts wouldn't yield useful results via LWP anyway).

  • url_skip_redirect_to domain [domain...] β€” default: none. domains that, when found as a redirect destination, should cause the redirect chain to stop immediately. super useful for breaking loops caused by consent or gateway pages that redirect back into the chain. πŸ”₯

example:

url_skip_redirect_to consent.google.com
  • clear_url_skip_redirect_to [domain] [domain...] β€” clear configured url_skip_redirect_to domains. if you name domains, only those get removed from the list.
  • url_redirector_params regexp β€” default: (?:adurl|af_web_dp|cm_destination|continue|destination|destURL|goto|h|l|login|location|p1|pval|r|redir|redirect|redirectTo|return|returnUrl|referer|service|target|tid|u|url)=(.*). the regexp used to parse uri parameters so it can detect redirectors and get redirected domains. the regexp must match only the redirected domain.

πŸ‘‘ PRIVILEGED SETTINGS πŸ‘‘

  • url_redirector_cache_type β€” default: none. the cache type being utilized. right now the only supported value is dbi, which implies url_redirector_cache_dsn is a DBI connect string. and yes, the DBI module is required. ✨

example: url_redirector_cache_type dbi

  • url_redirector_cache_dsn β€” default: none. the DBI dsn of the database to use.

for SQLite, the database gets created automatically if it doesn't already exist, and the supplied path and file must be read/writable by the user running spamassassin or spamd.

for MySQL/MariaDB or PostgreSQL, check out sql-directory for the database table creation clauses.

you're gonna need the proper DBI module for your database. like DBD::SQLite, DBD::mysql, DBD::MariaDB or DBD::Pg.

minimum required SQLite version is 3.24.0 (available from DBD::SQLite 1.59_01).

examples:

url_redirector_cache_dsn dbi:SQLite:dbname=/var/lib/spamassassin/Redirectors.db
  • url_redirector_cache_username β€” default: none. the username that should be used to connect to the database. not used for SQLite.
  • url_redirector_cache_password β€” default: none. the password that should be used to connect to the database. not used for SQLite.
  • url_redirector_cache_ttl β€” default: 86400. how long a cache entry stays valid for, in seconds. default is 86400 (that's 1 day, bestie). see url_redirector_cache_autoclean for database cleaning. 🧚

⭐ ADMINISTRATOR SETTINGS ⭐

  • url_redirector_cache_autoclean β€” default: 1000. automatically purge old entries from the database. the value describes a random run chance of 1/x. the default value of 1000 means cleaning runs approximately once for every 1000 messages processed. a value of 1 would mean the database is cleaned every single time a message is processed. 😭

set it to 0 to disable automatic cleaning and do it manually instead.

  • url_redirector_loginfo β€” default: 0 (off). if this option is enabled (set to 1), then redirected URLs and the decoded URLs get logged with info priority.
  • url_redirector_timeout β€” default: 5. maximum time a redirection URL HTTP request can take, in seconds.
  • max_redir_urls β€” default: 10. maximum amount of redirector URLs that will be looked up per message. chained redirections are not counted β€” only the initial redirection URLs found.

setting it to 0 disables HTTP requests, letting only the redir_url() test work and report any found redirector URLs.

  • max_redir_url_redirections β€” default: 10. maximum depth of chained redirections that a redirector can generate.
  • url_redirector_user_agent β€” default: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36. sets the User-Agent header for HTTP requests. some services require it to look like a common browser, no cap. 🌈

and that's the whole page, bestie!! this was perl v5.40.1, dated 2026-07-23, straight out of the <pink>Mail::...rectors(3pm)</pink> page in the User Contributed Perl Documentation. go forth and de-redirect, you absolute icon. πŸ’–πŸ¦‹πŸŽ€

πŸ“¦ where this page comes from