Pinterest URLs look simple until an application has to accept them from users.
A Pin can arrive on a country-specific domain, contain tracking parameters, or use a short pin.it URL. A loose check such as hostname.includes("pinterest") also accepts lookalike domains. Fetching every URL to determine what it represents adds latency and creates an avoidable server-side request forgery risk.
This article shows a stricter, network-free approach.
Define the result before writing the parser
The useful output is not just true or false. A consuming application usually needs to know:
- whether the URL is supported;
- whether it represents a Pin, profile, board, Ideas page, or short link;
- the numeric Pin or Ideas ID when one exists;
- a canonical URL with tracking parameters removed.
A discriminated result makes those decisions explicit:
type PinterestUrlKind = "pin" | "short" | "profile" | "board" | "ideas";
interface ParsedPinterestUrl {
kind: PinterestUrlKind;
originalUrl: string;
normalizedUrl: string;
pinId?: string;
shortcode?: string;
username?: string;
boardSlug?: string;
ideaId?: string;
}
Validate the URL object first
Use the platform URL parser before applying path rules:
function parseHttpsUrl(input) {
const value = input.trim();
if (!value) throw new Error("URL is empty");
const url = new URL(value);
if (url.protocol !== "https:") {
throw new Error("Only HTTPS URLs are supported");
}
if (url.username || url.password || (url.port && url.port !== "443")) {
throw new Error("Credentials and custom ports are not supported");
}
return url;
}
This rejects inputs such as http://pinterest.com/..., URLs containing credentials, and unexpected ports before any classification occurs.
Use an exact host allow list
Do not use suffix or substring matching alone. These checks are unsafe:
hostname.includes("pinterest");
hostname.endsWith("pinterest.com");
The first accepts pinterest.example; the second accepts notpinterest.com unless the dot boundary is handled correctly. An explicit set is easier to audit:
const PINTEREST_HOSTS = new Set([
"pinterest.com",
"www.pinterest.com",
"de.pinterest.com",
"fr.pinterest.com",
"pinterest.co.uk",
"www.pinterest.co.uk",
"pinterest.com.au",
"www.pinterest.com.au",
]);
function isAllowedHost(hostname) {
return PINTEREST_HOSTS.has(hostname.toLowerCase());
}
In production, the set can include every Pinterest country domain that the application intentionally supports. Unknown hosts should fail closed.
Match paths by type
Once the scheme and host are trusted, classify the pathname. A numeric Pin path can be handled without looking at query parameters:
const pinMatch = url.pathname.match(/^\/pin\/(\d{1,20})\/?$/);
if (pinMatch) {
const pinId = pinMatch[1];
return {
kind: "pin",
originalUrl: input,
normalizedUrl: `https://www.pinterest.com/pin/${pinId}/`,
pinId,
};
}
The normalized URL deliberately discards parameters such as utm_source, fragments, and country-specific hosts. Profile and board paths can be classified from their segment count, while reserved paths such as /search/ and /settings/ should be rejected before treating a single segment as a username.
Treat pin.it as a separate type
Short links are valid input, but resolving one requires a network request. A pure parser should classify and normalize the short URL without pretending to know its final Pin:
const shortMatch = url.pathname.match(/^\/([A-Za-z0-9_-]+)\/?$/);
if (url.hostname === "pin.it" && shortMatch) {
return {
kind: "short",
originalUrl: input,
normalizedUrl: `https://pin.it/${shortMatch[1]}/`,
shortcode: shortMatch[1],
};
}
The consuming application can decide whether it is allowed to follow redirects. Keeping that policy outside the parser makes the library deterministic and safe to use in build tools, CLIs, and server validation.
Use a tested package when the edge cases matter
I extracted these rules into the small MIT-licensed pinterest-url-normalizer package. It recognizes Pin, pin.it, profile, board, and Ideas URLs across supported country domains and performs no network requests.
npm install pinterest-url-normalizer
import {
isPinterestUrl,
normalizePinterestUrl,
parsePinterestUrl,
} from "pinterest-url-normalizer";
const parsed = parsePinterestUrl(
"https://de.pinterest.com/pin/987654321/?utm_source=share",
);
console.log(parsed.kind); // pin
console.log(parsed.pinId); // 987654321
console.log(parsed.normalizedUrl); // https://www.pinterest.com/pin/987654321/
isPinterestUrl("https://pin.it/AbC123"); // true
normalizePinterestUrl("https://pinterest.co.uk/example/media-tools/");
// https://www.pinterest.com/example/media-tools/
The parser is maintained alongside SavePinner's Pinterest downloader, where canonical URL handling is needed before a user starts a media workflow. The library itself contains no downloader, browser automation, analytics, or remote code.
A short validation checklist
Before accepting a Pinterest URL in an application, verify that you:
- parse it with the platform
URLclass; - require HTTPS;
- reject credentials and nonstandard ports;
- compare the hostname against an exact allow list;
- classify known path shapes and reject reserved paths;
- remove queries and fragments from canonical output;
- keep short-link resolution outside the pure parser;
- test lookalike hosts and malformed paths as aggressively as valid examples.
URL normalization is a small boundary with security consequences. Making the accepted forms explicit is usually simpler than trying to repair permissive parsing later.
Top comments (0)