Um einen URL-Kürzer zu erstellen, benötigst du vier Dinge: einen Ort zum Speichern der Zuordnung von einem Kurzcode zu einer Ziel-URL, eine Möglichkeit, einen eindeutigen Code für jeden neuen Link zu generieren, einen Weiterleitungs-Handler, der den Code nachschlägt und eine HTTP-Weiterleitung zurückgibt, und einen Cache vor dem Nachschlag, weil Lesevorgänge Schreibvorgänge um eine große Marge überwiegen. Das ist der gesamte Kern, und du kannst ihn an einem Nachmittag aufbauen.
Die Falle ist zu denken, die Nachmittagsversion sei das Produkt. Eine Weiterleitung, die auf deinem Laptop funktioniert, und ein URL-Kürzungsdienst, der überlebt, wenn Fremde ihn auf Malware richten, mit Traffic bombardieren und vier Neunen Verfügbarkeit erwarten, sind unterschiedliche Engineering-Probleme. Das erste ist ein Algorithmus. Das zweite ist eine Betriebsverpflichtung.
Diese Anleitung baut den Kern ehrlich auf und verbringt dann den größten Teil der Zeit mit dem Teil, den die System-Design-Tutorials überspringen: was du noch bauen musst, nachdem die Weiterleitung funktioniert. Wenn du zuerst die konzeptionelle Einführung möchtest, behandelt wie URL-Kürzer funktionieren die Mechanik ohne den Code.
Die kurze Version: Was ein URL-Kürzer eigentlich tut#
Ein URL-Kürzer ist eine Schlüssel-Wert-Suche mit einer HTTP-Weiterleitung. Der Schlüssel ist der Kurzcode, der Wert ist die lange URL, und die gesamte Aufgabe besteht darin, example.com/aB3x9 in ein 302 umzuwandeln, das auf die ursprüngliche Adresse zeigt.
Das Datenmodell ist eine Tabelle:
CREATE TABLE links (
id BIGSERIAL PRIMARY KEY,
short_code TEXT NOT NULL UNIQUE,
long_url TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX idx_links_short_code ON links (short_code);
Es gibt zwei Pfade durch sie. Der Schreibpfad nimmt eine lange URL, generiert einen Kurzcode und fügt die Zeile ein. Der Lesepfad nimmt einen Kurzcode, schlägt die Zeile nach und gibt eine Weiterleitung zurück. Lesevorgänge dominieren in einem Verhältnis, das üblicherweise bei 1000 zu 1 liegt, also gehört fast deine gesamte Engineering-Aufmerksamkeit dahin, die Suche schnell und günstig zu machen. Der eindeutige Index auf short_code ist das, was diese Suche zu einer Indexsuche statt eines Scans macht. Das ist der gesamte Kern.
Den Kurzcode generieren: Base62, Zufällig oder Hash#
Der Kurzcode ist die Stelle, an der die interessante Entscheidung liegt. Du hast drei realistische Strategien, und sie tauschen Länge, Vorhersehbarkeit und die Schwierigkeit der Kollisionsbehandlung gegeneinander aus.
Base62 einer eindeutigen ID ist der Klassiker. Nimm die automatisch inkrementierende Zeilen-ID und kodiere sie in base62, die 62 Zeichen a-z, A-Z und 0-9. Die Codes sind kurz, sie kollidieren nie, weil jede ID eindeutig ist, und sie werden ungefähr alle 62x im Volumen um ein Zeichen länger. Der Nachteil ist, dass sie sequenziell und erratbar sind, sodass jeder deinen Namensraum durchlaufen kann.
const alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
// encode turns a positive integer ID into a base62 short code.
func encode(id uint64) string {
if id == 0 {
return string(alphabet[0])
}
var b []byte
for id > 0 {
b = append(b, alphabet[id%62])
id /= 62
}
// reverse, since we built the digits least-significant first
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
return string(b)
}
Zufällige Zeichenfolgen beheben die Erratbarkeit. Generiere einen kurzen zufälligen Code, zum Beispiel mit einer Bibliothek wie nanoid, und überprüfe ihn gegen den eindeutigen Index, bevor du speicherst. Bei sieben Zeichen von base62 hast du Billionen von Möglichkeiten, sodass Kollisionen selten sind, aber du musst immer noch den seltenen Einfügevorgang behandeln, der die Eindeutigkeitsbedingung verletzt, indem du mit einem neuen Code wiederholst.
Das Hashen der URL ist die dritte Option und meist die schlechteste. Ein Hash der langen URL ist deterministisch, was praktisch klingt, aber du musst ihn trotzdem kürzen, du bekommst trotzdem Kollisionen, und identische URLs werden auf identische Codes abgebildet, was Informationen preisgibt. Die meisten Produktionsdienste wählen base62 für interne IDs oder zufällige Codes für öffentliche. Benutzerdefinierte oder gebrannte Slugs, die Codes, die ein Benutzer von Hand eingibt, werden gegen denselben eindeutigen Index validiert, bevor sie akzeptiert werden.
Der Weiterleitungspfad: 301 vs 302 und warum er über deine Analysen entscheidet#
Der Weiterleitungsstatuscode ist keine kosmetische Wahl. Er entscheidet, ob du jemals einen zweiten Klick siehst.
Ein 301 Moved Permanently sagt Browsern und Proxies, dass der Umzug dauerhaft ist, sodass sie ihn cachen. Nach dem ersten Besuch kann der Browser zukünftige Klicks direkt an das Ziel senden, ohne deinen Server zu berühren. Großartig für rohe Geschwindigkeit, fatal für Analysen, weil die Klicks, die du am meisten zählen möchtest, diejenigen sind, die dich nie erreichen. Die HTTP-Semantik ist in RFC 9110 festgelegt, das sowohl die permanenten als auch die temporären Weiterleitungen definiert.
Ein 302 Found oder 307 Temporary Redirect wird jedes Mal neu angefordert. Der Browser fragt deinen Server bei jedem Klick, was bedeutet, dass du jeden Besuch zählen kannst und das Ziel später ändern kannst, ohne mit veralteten Caches zu kämpfen. Für einen Link-Kürzer, dessen gesamter Wert editierbare Links und Klickdaten sind, ist das der richtige Standard. Die Kosten sind ein Netzwerk-Round-Trip pro Klick, den ein Cache-Treffer vernachlässigbar macht.
Die Faustregel: Greife zu 302, es sei denn, du hast einen spezifischen Grund, den Link einzufrieren und für immer zu cachen. Der Beitrag 301 vs 302 Redirects arbeitet den Kompromiss im Detail durch, und Typen von Weiterleitungen behandelt den Rest der 3xx-Familie, einschließlich wann 307 und 308 wichtig sind.
Speicherung und Caching: Für ein 1000:1 Lese/Schreib-Verhältnis entwerfen#
Da Lesevorgänge Schreibvorgänge überfluten, ist die Datenbank nicht dein Engpass, sondern deine Cache-Strategie. Das Muster ist ein Read-Through-Cache: Bei einem Klick zuerst einen In-Memory-Cache prüfen und nur bei einem Miss auf die Datenbank zurückfallen, das Ergebnis für das nächste Mal in den Cache schreiben.
func resolve(ctx context.Context, code string) (string, error) {
if url, ok := cache.Get(code); ok {
return url, nil // hot path: served from memory
}
url, err := db.LookupLongURL(ctx, code)
if err != nil {
return "", err
}
cache.Set(code, url) // populate for the next click
return url, nil
}
In der Produktion wird daraus meist zwei Stufen: ein kleiner In-Process-Cache für die heißesten Links, gestützt durch einen gemeinsam genutzten In-Memory-Store wie Redis, sodass jede Server-Instanz von einer Suche profitiert, die eine von ihnen bereits durchgeführt hat. Die Datenbank, die Quelle der Wahrheit, wird nur bei einem echten Cold-Miss berührt. Lass diese Schicht richtig funktionieren, und ein einziger bescheidener Server bewältigt enormes Klickvolumen. Der Beitrag Cache-Strategie für URL-Weiterleitungen geht tief auf die Verdrängungsund Größenentscheidungen ein, und das Eckpfeiler-Stück über p95 unter 15ms erreichen zeigt, wie ein optimierter Weiterleitungspfad unter Last aussieht.
Wenn du all das lieber nicht betreiben möchtest, gibt dir Elidos API die Weiterleitungsebene, den Cache und die regionale EU-Zustellung mit sub-15ms p95 bei einem Cache-Treffer, hinter einem einzigen Aufruf. Starte kostenlos und spare den Betriebsaufwand.
Klicks zählen ohne die Weiterleitung zu verlangsamen#
Der Fehler, der die Weiterleitungslatenz zerstört, ist das Schreiben des Klicks in die Datenbank innerhalb des Weiterleitungs-Handlers. So muss jeder Besucher auf deinen Analytics-Schreibvorgang warten, bevor er seine Weiterleitung erhält.
Entkopple sie. Der Handler gibt die Weiterleitung sofort zurück und sendet dann das Klickereignis als Fire-and-Forget-Arbeit in ein dauerhaftes Log oder eine Nachrichtenwarteschlange. Ein separater Consumer liest diesen Stream und schreibt Ereignisse nach seinem eigenen Zeitplan in einen Analytics-Store. Der Besucher wartet nie, und eine Reporting-Anfrage, die Millionen von Klick-Zeilen scannt, konkurriert nie mit dem Weiterleitungspfad um Ressourcen. Eine spaltenbasierte Analytics-Datenbank verarbeitet diese Aggregationsanfragen viel besser als ein Zeilenspeicher, weshalb Klickereignisse normalerweise an einem anderen Ort als die Links-Tabelle landen. Der Beitrag Fire-and-Forget-Klickerfassung beschreibt die Warteschlangenseite im Detail, und warum ein spaltenbasierter Store Postgres für Klick-Analysen schlägt erklärt die Speicherwahl. Elidos Analytics folgen dieser Form, sodass Klicks in Sekunden abfragbar sind, ohne der Weiterleitung Millisekunden hinzuzufügen.
Was noch zu bauen ist: Die schwierigen 80 Prozent#
Hier ist der Teil, den die System-Design-Anleitungen auslassen. Eine funktionierende Weiterleitung ist vielleicht ein Fünftel eines echten URL-Kürzungsdienstes. Der Rest ist alles, was eine Demo in etwas verwandelt, das man ins öffentliche Internet stellen kann.
- Missbrauch und Sicherheitsüberprüfung. Ein öffentlicher Kürzer ist innerhalb von Stunden nach dem Launch ein Phishing-Magnet. Du musst Ziele gegen einen Bedrohungs-Feed wie Google Safe Browsing prüfen und erneut scannen, weil eine saubere URL bei der Erstellung später bösartig werden kann. Die URL-Kürzer-Sicherheits-Checkliste ist die vollständige Liste.
- Rate-Limiting und Idempotenz. Ein offener Create-Endpunkt wird sofort per Skript angegriffen. Du brauchst Per-Key-Limits und Idempotenz, damit eine wiederholte Anfrage keine doppelten Links erstellt. Die Mechanik findet sich in API Rate Limits und Idempotenz.
- Benutzerdefinierte Domains mit TLS. Gebrannte Links bedeuten das Ausstellen von Zertifikaten für Domains, die dir nicht gehören, auf Anfrage, ohne manuelle Schritte.
- GDPR-sichere Klickdaten. In dem Moment, in dem du Klicks protokollierst, verarbeitest du personenbezogene Daten. Das Kürzen von IP-Adressen und das Dokumentieren der Aufbewahrung ist in der EU nicht optional, wie GDPR für URL-Kürzer darlegt.
- Hochverfügbarkeit. Deine Weiterleitung liegt jetzt auf dem kritischen Pfad jedes Links, den jemand jemals geteilt hat. Ausfallzeiten beschädigen die Inhalte anderer Leute, sodass die Verfügbarkeitsanforderung höher ist als bei den meisten Apps.
Nichts davon ist exotisch. Es ist einfach eine Menge nachhaltiger Arbeit, und sie hört nie auf, was der ehrliche Grund ist, warum die meisten Teams beim MVP aufhören und zu etwas Gewartetem greifen.
Selbst bauen, kaufen oder selbst hosten#
Selbst zu bauen ist der beste Weg, Weiterleitungen, Kodierung und Caching zu verstehen, und für ein geschlossenes internes Tool kann das MVP alles sein, was du je brauchst. Bau es. Du lernst an einem Wochenende mehr als bei jeder Interview-Vorbereitung.
Für alles Öffentliche oder Geschäftliche solltest du den Wartungsaufwand ehrlich abwägen. Die Weiterleitung ist kostenlos; die Missbrauchsbehandlung, das benutzerdefinierte Domain-TLS, die Analytics-Pipeline und die Rufbereitschaft sind es nicht. Wenn du die Kontrolle ohne das Schreiben von Grund auf möchtest, kannst du einen bestehenden Dienst selbst hosten, und Elido liefert einen Self-Hosting-Pfad genau dafür, wobei der Beitrag Open-Source-Optionen sie nebeneinander stellt. Wenn du es lieber vollständig auslagern möchtest, geben dir die Entwickler-Lösung und der API und SDK Schnellstart eine Produktions-Weiterleitungsebene ohne den obigen Backlog.
Verwandte Beiträge im Blog#
- Wie URL-Kürzer funktionieren - die konzeptionelle Einführung, kein Code erforderlich.
- p95 unter 15ms für Weiterleitungen erreichen - das Engineering-Eckstück über einen optimierten Weiterleitungspfad.
- Cache-Strategie für URL-Weiterleitungen - der zweistufige Cache im Detail.
- Fire-and-Forget-Klickerfassung - Analytics von der Weiterleitung entkoppeln.
- Selbst gehostete URL-Kürzer - die Open-Source-Optionen, wenn du nicht von Null aufbauen möchtest.
Elido testen
URL einfügen, kurzer Link in Sekunden
Kein Konto nötig. Link bleibt 30 Tage aktiv. Konto erstellen, um ihn dauerhaft zu behalten.
Kostenlos, keine Anmeldung erforderlich · 2 pro Tag