Parse a URL comprehensively, extracting and deriving all relevant components.
Source:R/parse.R
safe_parse_url.RdThis function serves as the core URL processing engine. It parses a URL, handles protocol and www prefix modifications, detects IP addresses, and derives components like the registered domain and top-level domain (TLD). Results are memoized for performance when processing large datasets.
Usage
safe_parse_url(
url,
protocol_handling = c("keep", "none", "strip", "http", "https"),
www_handling = c("none", "strip", "keep", "if_no_subdomain"),
tld_source = c("all", "private", "icann"),
case_handling = c("lower_host", "keep", "lower", "upper"),
trailing_slash_handling = c("none", "keep", "strip"),
index_page_handling = c("keep", "strip"),
path_normalization = c("none", "collapse_slashes", "dot_segments", "both"),
scheme_relative_handling = c("keep", "http", "https", "error"),
subdomain_levels_to_keep = NULL,
host_encoding = c("keep", "idna", "unicode"),
path_encoding = c("keep", "encode", "decode"),
query_handling = c("drop", "filter", "allow", "keep"),
params_keep = NULL,
params_drop = NULL,
sort_params = FALSE,
empty_param_handling = c("keep", "drop"),
params_case_sensitive = FALSE,
decode_plus = FALSE,
port_handling = c("exclude", "keep", "strip_default", "strip_all"),
scheme_policy = c("infer", "require"),
scheme_acceptance = c("web", "general"),
url_standard = NULL,
engine = NULL,
profile = NULL
)Arguments
- url
A single URL string to be parsed. For vectors, use
safe_parse_urls.- protocol_handling
A character string specifying how to handle protocols. Defaults to "keep". Regardless of this option, rurl only processes authority-based URLs whose scheme is one of http, https, ftp, or ftps; a scheme-bearing input with any other scheme (e.g.
mailto:,tel:,ws:) yieldsparse_status = "error". Scheme inference (below) also requires the input to be host-shaped: a scheme-less string that is not a host (e.g."asdfghjkl","12345","/path") or is a non-canonical IP literal (integer/hex/octal/short forms, or leading-zero octets like"192.168.010.1") is rejected as"error"rather than having a scheme fabricated for it."keep": If a supported scheme exists (http, https, ftp, ftps), it's used. If no scheme and the input is host-shaped, "http://" is added; otherwise the input is not a URL and yields
"error"."none": If a supported scheme exists, it's used. If no scheme, then no scheme is used (scheme component will be NA).
"strip": Any existing scheme is removed (scheme component will be NA).
"http": The scheme is forced to be "http".
"https": The scheme is forced to be "https".
- www_handling
A character string specifying how to handle "www" and
www[number]prefixes in the host. Defaults to "none"."none": (Default) Leaves the host's www prefix (or lack thereof) untouched.
"strip": Removes any "www." or
www[number].prefix."keep": Ensures the host starts with "www.". If it has
www[number]., it's normalized to "www.". If no www prefix, "www." is added. An empty input host remains empty."if_no_subdomain": If the host is a bare registered domain (e.g., "example.com"), "www." is added. If the host already has a "www." or
www[number].prefix, it is normalized to "www." (e.g., "www1.example.com" becomes "www.example.com"; "www1.sub.example.com" becomes "www.sub.example.com"). If a non-www subdomain exists (e.g., "sub.example.com" or the normalized "www.sub.example.com"), the host is not further altered. An empty input host remains empty.
- tld_source
Which TLD source to use for TLD extraction: "all", "icann", or "private". Defaults to "all".
- case_handling
A character string specifying how to handle the case of the cleaned URL. Defaults to "lower_host", the RFC 3986 §6.2.2.1 normalization (scheme and host are case-insensitive and folded to lowercase; the path is case-sensitive and preserved).
"lower_host": (Default) Lowercases scheme and host only; the path keeps its original casing.
"keep": Preserves casing of the reconstructed URL.
"lower": Converts the cleaned URL to lowercase.
"upper": Converts the cleaned URL to uppercase.
- trailing_slash_handling
A character string specifying how to handle trailing slashes in the path component of the cleaned URL. Defaults to "none".
"none": (Default) No specific handling is applied. Path remains as is after initial parsing.
"keep": Ensures a trailing slash. If a path exists and doesn't end with one, it's added. If path is just "/", it's kept.
"strip": Removes a trailing slash if present, unless the path is solely "/".
- index_page_handling
A character string specifying how to handle index/default pages. Defaults to "keep".
"keep": (Default) Leave index/default page segments untouched.
"strip": Remove a trailing index.* or default.* segment (case-insensitive).
- path_normalization
How to normalize path structure. Defaults to "none". rurl owns dot-segment resolution: the path is read from the input verbatim (not from libcurl's pre-normalized path), so
"none"preserves./..segments (/a/../bstays/a/../b) and only the settings below change them. Resolution follows RFC 3986 section 5.2.4 and acts on literal./..segments only — a percent-encoded%2eis a normal path byte, never a dot segment, so it is never treated as traversal."none": (Default) No normalization; dot and slash structure is preserved exactly as written.
"collapse_slashes": Collapse duplicate slashes in the path.
"dot_segments": Resolve . and .. segments per RFC 3986.
"both": Apply both collapse_slashes and dot_segments.
- scheme_relative_handling
How to handle URLs starting with "//". Defaults to "keep".
"keep": Parse using http but return scheme as NA and set status to "ok-scheme-relative".
"http": Assume http for parsing and output.
"https": Assume https for parsing and output.
"error": Treat scheme-relative URLs as invalid.
- subdomain_levels_to_keep
An integer or NULL. Determines how many levels of subdomains are kept, in addition to any 'www.' prefix handled by
www_handling.NULL: (Default) No specific subdomain stripping is performed beyondwww_handling.0: All subdomains are stripped. Ifwww_handlingpreserved or added 'www.', it remains (e.g., 'www.sub.example.com' becomes 'www.example.com'; 'sub.example.com' becomes 'example.com').N > 0: Keeps up to N levels of subdomains, counted from right-to-left (closest to the registered domain), in addition to any 'www.' prefix. E.g., if N=1, 'three.two.one.example.com' becomes 'one.example.com'; 'www.three.two.one.example.com' (post www_handling) becomes 'www.one.example.com'.
- host_encoding
How to present the host in
clean_url. Defaults to "keep"."keep": Leave host as parsed by curl (may preserve original case).
"idna": Convert Unicode host labels to Punycode (IDNA) for the cleaned URL.
"unicode": Decode Punycode labels to Unicode for the cleaned URL.
- path_encoding
How to present the path percent-encoding in
clean_url— the readable-vs-browser rendering choice (the path analog ofhost_encoding). Defaults to "keep". This is an orthogonal presentation knob: it is independent ofurl_standardand layers on top of any profile (e.g.url_standard = "whatwg", path_encoding = "encode"emits the WHATWG-parsed path in browser form), exactly likehost_encoding. Only "keep" preserves a profile's canonical identity path verbatim; "encode" and "decode" are presentation forms that may re-encode or decode reserved octets (so%2Fmay fold to a path-separating/), independent of whether a profile is set."keep": Leave the path percent-encoding untouched (the path is preserved as written in the URL, so
%2Fstays%2Frather than decoding into a path-separating/). With nourl_standard, rurl keeps its historical RFC-style percent-hex case canonicalization, so%2fbecomes%2F. Underurl_standard = "whatwg", existing percent-triplet spelling is preserved byte-for-byte. Use "encode" to additionally normalize which bytes are encoded."encode": The browser/percent-encoded rendering. Decodes the path first, then percent-encodes each segment (slashes preserved), so a readable non-ASCII path is emitted in its percent-encoded UTF-8 form.
"decode": The readable rendering. Percent-decodes UTF-8 sequences in the path, so a percent-encoded segment is shown as readable text.
- query_handling
A character string controlling whether (and how) the query string is included in
clean_url. Defaults to "drop", which preserves the historical query-freeclean_url. The rawqueryresult field is never affected by this option — it always reports the faithful original query."drop": (Default)
clean_urlcarries no query, exactly as before."filter": Keep contentful params, dropping known trackers via a built-in denylist (e.g.
utm_*,fbclid,gclid).params_dropextends the denylist;params_keeprescues names (winning over both the denylist and empty-dropping)."allow": Keep only params whose names match
params_keep; all others are dropped. Hereparams_keepis an inclusion criterion only, not an empty-rescue."keep": Keep every param, re-encoded into canonical form (not the verbatim original — that stays on the
queryfield).
In every non-"drop" mode the surviving query is re-encoded canonically (uppercase percent-hex, spaces as
%20) and appended after the path. The query is intentionally EXEMPT fromcase_handling(query values are case-sensitive — tokens, IDs, signatures), so undercase_handling = "lower"or"upper"theclean_urlis no longer uniformly cased: scheme/host/path fold but the query keeps its original case. Becauseclean_urlis thecanonical_joinkey, any non-"drop" mode also brings the query into that join key (so?id=1and?id=2stop collapsing, whileutm-only differences still collapse under "filter").- params_keep
Character vector of parameter-name globs (only
*is special), orNULL(default). In "filter" mode this is the rescue list; in "allow" mode it is the allowlist. Ignored in "drop"/"keep".- params_drop
Character vector of parameter-name globs to add to the built-in denylist in "filter" mode, or
NULL(default). Ignored in "drop"/"allow"/"keep".- sort_params
Logical (default
FALSE). WhenTRUE, surviving params are stably sorted by decoded key. Active in "filter"/"allow"/"keep".- empty_param_handling
One of "keep" (default) or "drop". "drop" removes empty-valued params (e.g.
?ref=), except those rescued byparams_keepin "filter" mode.- params_case_sensitive
Logical (default
FALSE). Controls whether the denylist andparams_keep/params_dropmatching is case-sensitive.- decode_plus
Logical (default
FALSE). WhenTRUE,+in query values is treated as a space (HTML-form decoding) before percent-decoding.FALSEkeeps+literal (RFC 3986 generic behavior).- port_handling
A character string controlling whether the port appears in
clean_url. Defaults to "exclude", today's only historical behavior. This knob is standalone and standard-independent (editorial, likewww_handling) –url_standardnever governs whether it may be set."exclude": (Default) The port never appears in
clean_url."strip_all": Explicit alias of "exclude".
"keep": Include the syntactic port when present, including a default port under
url_standard = "whatwg". This is an explicit non-parity override for callers that need the input's port spelling."strip_default": Keep only non-default ports (using the same scheme-default table), independent of
url_standard.
- scheme_policy
Controls whether scheme-less, host-shaped input is accepted (an input-acceptance axis, distinct from
protocol_handling, which only controls how the scheme is presented, and fromurl_standard, which controls interpretation). Defaults to "infer"."infer": (Default) Fabricate
http://for scheme-less host-shaped input (e.g.example.comparses ashttp://example.com), a browser-omnibox-style affordance. This is the historical behavior."require": Reject scheme-less input — a scheme-less host-shaped value becomes
parse_status = "error"rather than gaining a fabricated scheme. Use this for a strict, pure-parser posture. Note this governs only bare host input; scheme-relative//hostinput is governed separately byscheme_relative_handling.
- scheme_acceptance
Which scheme tokens may enter parsing (a scheme-acceptance axis, distinct from
scheme_policy, which governs scheme-less input, and fromurl_standard, which governs interpretation). Defaults to "web"."web": (Default) Only the curated web-scheme allowlist (
http/https/ftp/ftps/file) is admitted; a scheme-bearing input outside it isparse_status = "error". This is the historical, byte-for-byte compatible behavior."general": Admit any syntactically valid scheme token and parse opaque (
mailto:x), non-special (foo://host), and RFC-generic URLs. Requires an expliciturl_standard("rfc3986"or"whatwg"), which decides the interpretation;generalwithurl_standard = NULLis an error. Non-special / opaque hosts receive no www-stripping, no domain/TLD derivation, and are never run through the IDNA/punycode helpers. A non-special scheme with no//is an opaque path: it has no authority, sohost,user,portand thedomain/tldcolumns are allNAand the entire remainder is thepath(query/fragmentare still split off). This includesmailto:— the recipient's@never re-triggers authority parsing. To decompose amailto:recipient, use the accessors (get_host()/get_domain()/get_user(), ADR 0012 D7) orget_mailto_recipients(); those deliberately return a recipient's parts where this table presentsNA, because a recipient domain is extraction metadata, not the URL's authority.
- url_standard
Optional top-level standard profile:
NULL(default),"rfc3986", or"whatwg". WithNULLthe behavior is exactly what the individual low-level options select (fully backward compatible). When set, it selects a coherent set of standard-conformant behaviors for the axes it governs — path percent/dot handling, the host IPv4/reg-name model, andcase_handling— so callers do not have to hand-assemble the low-level knobs. Passing a governed low-level knob (path_normalizationorcase_handling) with a value the selected profile would not choose is an error; passing the value the profile would pick is accepted (onlycase_handling = "lower_host"is accepted under a selector —"keep","lower", and"upper"all conflict, since"lower"also lowercases the path, which neither standard sanctions). Added as the last argument so existing positional calls keep their meaning; always pass it by name. Under"whatwg"the selector additionally recognizes a literal backslash as a path separator for WHATWG-special schemes (http/https/ftp) and nulls default ports in parse output; useport_handling = "strip_default"for spec-style clean URL port rendering. Seeresolve_urlforurl_standard-governed reference resolution. The selector does not govern whetherport_handlingmay be set (it is a standalone editorial knob), nor does it governpath_encoding(an orthogonal path-presentation knob that layers on any profile), IDNA rendering, or query handling.- engine
Optional pslr engine controlling which Public Suffix List backs domain / TLD / subdomain extraction:
NULL(default) resolves against pslr's session-global default list — exactly the historical behavior — while apslr::psl_engine()snapshot resolves against that specific list, per request, without mutating any global state (never callpslr::psl_use()for this). Use it to pin a particular list version or to load an alternate list viapslr::psl_engine(source = "path", path = ...). Process-local: an engine holds a C++ external pointer that does not serialize across R sessions or parallel workers — build it in the process that uses it; never cache it to disk or send it to a worker (rebuild one per process instead). Only the domain-derived outputs (domain,tld, and the subdomain-trimmed host /clean_url) depend on it.- profile
Optional named profile bundling several knobs at once:
NULL(default; behaves exactly as the individual arguments select, fully backward compatible),"browser","whatwg","rfc-syntax","seo", or the"seo"alias"canonical". A profile is separate fromurl_standard(it bundles acceptance, interpretation, leniency, and canonicalization together) and expands only into arguments you did not supply explicitly — an explicit argument always overrides the profile."browser"is a browser-like fix-up posture (http-prepending; not Chrome-faithful);"whatwg"is the absolute-URL no-base posture that rejects scheme-less input (unlike a bareurl_standard = "whatwg");"rfc-syntax"is RFC 3986 generic syntax as parsing, not normalization (case and dot-segments are preserved);"seo"/"canonical"is rurl's origin-cleaning intent (https, strip www / trailing slash / index page, filter tracking params). Inspect the resolved bundle withurl_profile. Also accepted bycanonical_join(forwarded through its...).
Value
A named list with the following components:
original_url: The original URL string provided.scheme: The scheme (e.g., "http", "https").host: The host (e.g., "www.example.com"). NA if the host becomes empty after processing.port: The port number.path: The path component (e.g., "/path/to/resource").query: The query string (e.g., "name=value"); never percent-decoded. Underurl_standard = "whatwg"it carries the standard's percent-encoded spelling (the query percent-encode set is applied, so a literal space reports as "%20"); underurl_standard = "rfc3986"or no selector it is the raw source spelling, preserved byte-for-byte exactly as written in the URL (a bare key such as "flag" stays "flag", not "flag="). A present-but-empty query (e.g. from a trailing "?") is reported as NA.fragment: The fragment identifier (e.g., "section"); never percent-decoded, with the same two-branch contract asquery(the fragment percent-encode set is applied underurl_standard = "whatwg", so a double-quote inside the fragment reports as "%22"). Empty is reported as NA.user: The user name for authentication; never percent-decoded. Underurl_standard = "whatwg"it carries the standard's percent-encoded spelling (the userinfo percent-encode set is applied, so "http://a^b@host/" reports "a%5Eb"); underurl_standard = "rfc3986"or no selector it is the raw source spelling, exactly as written in the URL. Empty is reported as NA.password: The password for authentication, with the same encoding contract asuser(so a ":" inside a WHATWG password is reported as "%3A"). Empty is reported as NA.domain: The registered domain name (e.g., "example.com"). NA if host is an IP, empty, or derivation fails.tld: The top-level domain (e.g., "com"). NA if host is an IP, empty, or derivation fails.domain_ascii,domain_unicode: The registered domain in both canonical spellings, independent ofhost_encoding. For an internationalized domain,domain_asciiis the Punycode/A-label form (e.g., "xn–mnchen-3ya.de") anddomain_unicodethe decoded Unicode form (e.g., "münchen.de"); for an ASCII-only domain the two are equal. Unlikedomain(which followshost_encoding, a rendering choice), these are stable identity keys — a Unicode host and its A-label share onedomain_ascii— so consumers can build an encoding-independent key from a single parse. NA under the same conditions asdomain.tld_ascii,tld_unicode: The public suffix (TLD) in both canonical spellings, thetldanalogue ofdomain_ascii/domain_unicode. NA under the same conditions astld.is_ip_host: Logical, TRUE if the host is an IP address.clean_url: A normalized canonical key reconstructed from scheme, host, and path, after processing and with case handling applied. The query is included only whenquery_handling != "drop"(the default is "drop", so by default the query is excluded); when included it is filtered/canonicalized per the query options and appended case-unfolded. The port is included only whenport_handling != "exclude"(the default is "exclude", so by default the port is excluded, as before); fragment and userinfo are always excluded (use the dedicated components above to retrieve them). Withpath_encoding = "decode"the path is shown decoded, soclean_urlis human-readable rather than guaranteed URL-safe. NA if host is empty/NA.parse_status: Character string indicating parsing outcome ("ok", "ok-ftp", "ok-scheme-relative", "error", "warning-no-tld", "warning-invalid-tld", "warning-public-suffix", "warning-userinfo"). "warning-userinfo" marks a scheme-less input carrying userinfo (e.g. "user@example.com"): host/domain/tld/user still resolve, butclean_urlis NA (rurl will not fabricate a canonical URL from an ambiguous, email-shaped, scheme-less string).
Returns NULL if the URL is fundamentally unparseable (e.g., NA, empty)
or uses a disallowed scheme.
Examples
safe_parse_url(
"http://www.Example.com/Path?q=1#Frag",
protocol_handling = "keep",
case_handling = "lower"
)
#> $original_url
#> [1] "http://www.Example.com/Path?q=1#Frag"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "www.example.com"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/path"
#>
#> $query
#> [1] "q=1"
#>
#> $fragment
#> [1] "Frag"
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "http://www.example.com/path"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url(
"Example.com/Another",
protocol_handling = "none",
www_handling = "keep",
case_handling = "upper",
trailing_slash_handling = "keep"
)
#> $original_url
#> [1] "Example.com/Another"
#>
#> $scheme
#> [1] NA
#>
#> $host
#> [1] "WWW.EXAMPLE.COM"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/ANOTHER/"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "WWW.EXAMPLE.COM/ANOTHER/"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url(
"example.com",
www_handling = "if_no_subdomain"
) # -> www.example.com
#> $original_url
#> [1] "example.com"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "www.example.com"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "http://www.example.com/"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url(
"sub.example.com",
www_handling = "if_no_subdomain"
) # -> sub.example.com
#> $original_url
#> [1] "sub.example.com"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "sub.example.com"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "http://sub.example.com/"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url(
"www1.example.com",
www_handling = "if_no_subdomain"
) # -> www.example.com
#> $original_url
#> [1] "www1.example.com"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "www.example.com"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "http://www.example.com/"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url(
"www1.sub.example.com",
www_handling = "if_no_subdomain"
) # -> www.sub.example.com
#> $original_url
#> [1] "www1.sub.example.com"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "www.sub.example.com"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "http://www.sub.example.com/"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url(
"http://www.example.com/path/",
trailing_slash_handling = "strip"
)
#> $original_url
#> [1] "http://www.example.com/path/"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "www.example.com"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/path"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "http://www.example.com/path"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url("192.168.1.1/test")
#> $original_url
#> [1] "192.168.1.1/test"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "192.168.1.1"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/test"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] NA
#>
#> $tld
#> [1] NA
#>
#> $domain_ascii
#> [1] NA
#>
#> $domain_unicode
#> [1] NA
#>
#> $tld_ascii
#> [1] NA
#>
#> $tld_unicode
#> [1] NA
#>
#> $is_ip_host
#> [1] TRUE
#>
#> $clean_url
#> [1] "http://192.168.1.1/test"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url("ftp://user:pass@ftp.example.co.uk:21/file.txt")
#> $original_url
#> [1] "ftp://user:pass@ftp.example.co.uk:21/file.txt"
#>
#> $scheme
#> [1] "ftp"
#>
#> $host
#> [1] "ftp.example.co.uk"
#>
#> $port
#> [1] 21
#>
#> $path
#> [1] "/file.txt"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] "user"
#>
#> $password
#> [1] "pass"
#>
#> $domain
#> [1] "example.co.uk"
#>
#> $tld
#> [1] "co.uk"
#>
#> $domain_ascii
#> [1] "example.co.uk"
#>
#> $domain_unicode
#> [1] "example.co.uk"
#>
#> $tld_ascii
#> [1] "co.uk"
#>
#> $tld_unicode
#> [1] "co.uk"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "ftp://ftp.example.co.uk/file.txt"
#>
#> $parse_status
#> [1] "ok-ftp"
#>
safe_parse_url(
"http://deep.sub.domain.example.com",
subdomain_levels_to_keep = 0
)
#> $original_url
#> [1] "http://deep.sub.domain.example.com"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "example.com"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "http://example.com/"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url(
"http://deep.sub.domain.example.com",
subdomain_levels_to_keep = 1
)
#> $original_url
#> [1] "http://deep.sub.domain.example.com"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "domain.example.com"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "http://domain.example.com/"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url(
"http://www.deep.sub.domain.example.com",
www_handling = "keep",
subdomain_levels_to_keep = 0
)
#> $original_url
#> [1] "http://www.deep.sub.domain.example.com"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "www.example.com"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "http://www.example.com/"
#>
#> $parse_status
#> [1] "ok"
#>
safe_parse_url(
"http://www.deep.sub.domain.example.com",
www_handling = "keep",
subdomain_levels_to_keep = 1
)
#> $original_url
#> [1] "http://www.deep.sub.domain.example.com"
#>
#> $scheme
#> [1] "http"
#>
#> $host
#> [1] "www.domain.example.com"
#>
#> $port
#> [1] NA
#>
#> $path
#> [1] "/"
#>
#> $query
#> [1] NA
#>
#> $fragment
#> [1] NA
#>
#> $user
#> [1] NA
#>
#> $password
#> [1] NA
#>
#> $domain
#> [1] "example.com"
#>
#> $tld
#> [1] "com"
#>
#> $domain_ascii
#> [1] "example.com"
#>
#> $domain_unicode
#> [1] "example.com"
#>
#> $tld_ascii
#> [1] "com"
#>
#> $tld_unicode
#> [1] "com"
#>
#> $is_ip_host
#> [1] FALSE
#>
#> $clean_url
#> [1] "http://www.domain.example.com/"
#>
#> $parse_status
#> [1] "ok"
#>
# Query handling: keep contentful params, drop known trackers.
safe_parse_url(
"http://example.com/watch?v=abc&utm_source=nl",
query_handling = "filter"
)$clean_url
#> [1] "http://example.com/watch?v=abc"
# -> "http://example.com/watch?v=abc"
# params_keep is a RESCUE in "filter" (wins over the denylist) ...
safe_parse_url(
"http://example.com/?utm_source=nl&id=1",
query_handling = "filter", params_keep = "utm_source"
)$clean_url
#> [1] "http://example.com/?utm_source=nl&id=1"
# -> "http://example.com/?utm_source=nl&id=1"
# ... but an ALLOWLIST in "allow" (only listed names survive).
safe_parse_url(
"http://example.com/?a=1&id=2",
query_handling = "allow", params_keep = "id"
)$clean_url
#> [1] "http://example.com/?id=2"
# -> "http://example.com/?id=2"
# "allow" empty-handling asymmetry: params_keep does NOT rescue empties, so
# an allowed empty param still drops under empty_param_handling = "drop".
safe_parse_url(
"http://example.com/?id=&keep=1",
query_handling = "allow", params_keep = c("id", "keep"),
empty_param_handling = "drop"
)$clean_url
#> [1] "http://example.com/?keep=1"
# -> "http://example.com/?keep=1"