Skip to main content

LinkShorter v2 – Ausführliche Anleitung

LinkShorter is a self-hosted URL shortening tool that lets you create short, shareable links, protect them with passwords, set expiration rules, generate QR codes, and track click statistics. Below is a step-by-step guide covering every feature.


1. Logging InInhaltsverzeichnis

  1. OpenÜberblick your& browser and go to your LinkShorter URL (e.g. https://link.fabula.vision/).Architektur
  2. YouInstallation will& see a login screen with Username and Password fields.Konfiguration
  3. EnterDas the credentials your administrator set up, then click Login.Admin-Dashboard
  4. AfterLinks aerstellen successful& loginverwalten
youPasswortgeschützte areLinks takenQR-Codes toOpenGraph-Proxying theStatistiken Dashboard.& Click-Tracking Die REST-API Die Chrome-Extension Automatische Link-Expiration (Cron) Sicherheitshinweise Technische Architektur im Detail

2.1. TheÜberblick Dashboard& Architektur

TheLinkShorter dashboardist isein theselbstgehosteter mainURL-Shortener, screen.der Itvollständig hasin threePHP areas:geschrieben ist und eine SQLite-Datenbank verwendet – es wird also kein MySQL/MariaDB-Server benötigt. Die gesamte Anwendung besteht aus einer einzigen Einstiegsdatei (index.php), die als Front-Controller fungiert: Alle HTTP-Requests werden über die .htaccess per mod_rewrite an diese Datei weitergeleitet.

    Technische

    Create a new short link (the form at the top) Search bar (to find existing links) Links table (a list of all your shortened links)

    3. Creating a Short Link

    In the "Shorten a Link" card at the top of the dashboard:

    Kernkomponenten
    FieldKomponente Required?Datei What to enterVerantwortung
    URLRouting & Controller Yesindex.php TheEinziger full,Entry-Point, long URL you want to shorten (e.g.wertet https://example.com/some/very/long/page$_GET['route']) aus
    Custom SlugDatenbank Noincludes/db.php ASQLite-Verbindung shortvia keywordPDO, for the link (e.g. my-link). If left empty, one is generated automatically. Only letters, numbers, hyphens and underscores are allowed. A live preview below the field shows you what the final short URL will look like.Schema-Migration
    PasswordAuthentifizierung Noincludes/auth.php IfSession-basiertes you type a password here, anyone who clicks the short link will be asked to enter that password before being redirected.Login
    Expires AtSlug-Generierung Noincludes/slug.php PickZufällige aKurzlinks datevia and time. After that moment the link will stop working automatically.SHA-256
    Max ClicksLink-Logik Noincludes/links.php SetCRUD, aClick-Recording, number.Batch-Import
    Once thelinkOpenGraph hasincludes/opengraph.php beenMetadaten-Extraktion clickedvon thatZiel-URLs many timesitQR-Code willincludes/qrcode.php stopEigene working.QR-Code-Implementierung (kein externer Service!)

    Datenfluss bei einem Kurzlink-Aufruf

    Browser → Apache (.htaccess Rewrite)
           → index.php?route=mein-slug
           → getLinkBySlug("mein-slug")
           → Prüfung: aktiv? abgelaufen? Passwort? Crawler?
           → recordClick() → HTTP 302 Redirect → Ziel-URL
    

    2. Installation & Konfiguration

    Voraussetzungen

      PHP ≥ 7.4 mit aktivierten Extensions: pdo_sqlite, gd (für PNG-QR-Codes), mbstring Apache mit mod_rewrite aktiviert Schreibrechte auf das Verzeichnis data/

      Schritt 1: Dateien hochladen

      ClickLaden Sie alle Dateien auf Ihren Webserver hoch. Die Verzeichnisstruktur sollte so aussehen:

      /
      ├── index.php
      ├── config.php
      ├── .htaccess
      ├── assets/
      │   ├── style.css
      │   └── app.js
      ├── includes/
      │   ├── db.php
      │   ├── auth.php
      │   ├── slug.php
      │   ├── links.php
      │   ├── opengraph.php
      │   └── qrcode.php
      ├── templates/
      │   ├── dashboard.php
      │   ├── edit.php
      │   ├── login.php
      │   ├── stats.php
      │   ├── settings.php
      │   ├── password.php
      │   ├── og_proxy.php
      │   ├── unavailable.php
      │   └── 404.php
      └── data/           ← wird automatisch erstellt
          ├── linkshorter.db
          ├── qr_cache/
          └── qr_icon.svg
      

      Schritt 2: Konfiguration anpassen

      Öffnen Sie config.php und passen Sie die Werte an:

      <?php
      define('ADMIN_USERNAME', 'admin');          // Ihr gewünschter Benutzername
      define('ADMIN_PASSWORD', 'IhrSicheresPasswort');  // UNBEDINGT ÄNDERN!
      define('BASE_URL', 'https://kurz.example.com/');  // Ihre Domain mit abschließendem /
      define('DB_PATH', __DIR__ . '/data/linkshorter.db');
      define('SITE_TITLE', 'LinkShorter');
      define('QR_ICON_PATH', __DIR__ . '/data/qr_icon.svg');
      

      Technischer Hintergrund: Die Konstanten werden mit define() festgelegt und sind damit Create Short Linkglobal whenin youallen areinkludierten done.Dateien Averfügbar. greenADMIN_PASSWORD successwird messageim willKlartext appeargespeichert containing youres newwird shortnicht URLgehasht, da es bei jedem Login direkt verglichen wird (e.g.in attemptLogin). Dies ist ein bewusster Trade-off für Einfachheit bei einer Single-User-Anwendung.

      Schritt 3: Erster Aufruf

      Beim ersten Aufruf von https://link.fabula.vision/my-linkkurz.example.com/ erstellt getDB automatisch:

        Das data/-Verzeichnis Die SQLite-Datenbank mit den Tabellen links, clicks und settings

        Die Funktion nutzt WAL-Modus (PRAGMA journal_mode=WAL)., Youwas canparallele nowLese- shareund thatSchreibzugriffe URL.ermöglicht und die Performance bei gleichzeitigen Zugriffen deutlich verbessert.


        3. Das Admin-Dashboard

        Login

        Rufen Sie https://kurz.example.com/ auf. Sie werden zum Login weitergeleitet, da die Route ?page=login aktiv wird. Die Authentifizierung funktioniert session-basiert:

          attemptLogin vergleicht die Eingaben mit den Konstanten aus config.php Bei Erfolg wird $_SESSION['logged_in'] = true gesetzt Alle geschützten Seiten prüfen via requireLogin, ob die Session gültig ist

          Dashboard-Oberfläche

          Nach dem Login sehen Sie das Dashboard (dashboard.php) mit:

          Link-Erstellungsformular (oben) Suchleiste mit Live-Filterung Link-Tabelle mit Sortierung nach Slug, URL, Klicks oder Erstellungsdatum

          Technischer Hintergrund zur Sortierung: Die Funktion getAllLinks baut die SQL-Query dynamisch zusammen. Erlaubte Spalten sind in einem Whitelist-Array definiert, um SQL-Injection zu verhindern:

          $allowed = ['slug', 'url', 'clicks', 'created_at', 'active'];
          if (!in_array($sort, $allowed)) $sort = 'created_at';
          

          Die Sortierrichtung wird ebenfalls validiert (ASC oder DESC), bevor sie in die Query eingebaut wird.


          1. Geben

            OnSie the dashboard, click thedie Batch ImportZiel-URL buttonein next to "Create Short Link".

            (Pflichtfeld)
          2. Optional:

            ACustom pop-upSlug window appearswird withautomatisch abereinigt large(nur texta-zA-Z0-9_- box.

            erlaubt)
          3. Optional: Passwort – wird mit password_hash() als bcrypt-Hash gespeichert
          Optional: Ablaufdatum – als datetime-local Optional: Max Clicks – nach Erreichen wird der Link deaktiviert

          PasteTechnischer yourHintergrund URLszur Slug-Generierung: oneWenn URLkein perCustom line,Slug forangegeben example:wird, generiert generateSlug einen 6-Zeichen-Code. Der Algorithmus ist bewusst kryptographisch robust:

          https://example.com/page1Eingabe https://example.com/page2= https://example.com/page3microtime() + random_bytes(8) + Versuchszähler
                   ↓
          SHA-256 Hash
                   ↓
          6 Zeichen aus dem Zeichensatz [a-zA-Z0-9] extrahiert
                   ↓
          Kollisionsprüfung gegen Datenbank (max 100 Versuche)
          

          Die Kombination aus microtime() (Zeitstempel mit Mikrosekunden) und random_bytes() (kryptographisch sichere Zufallsbytes) macht Kollisionen extrem unwahrscheinlich. Bei 62 möglichen Zeichen pro Position ergibt ein 6-Zeichen-Slug 62⁶ ≈ 56,8 Milliarden mögliche Kombinationen.

          Batch-Import

          Klicken Sie auf „Batch Import" im Dashboard. Es öffnet sich ein Modal-Dialog, in dem Sie eine URL pro Zeile eingeben können. batchCreateLinks verarbeitet jede Zeile einzeln:

            Trennung nach Zeilenumbrüchen und Trimming URL-Validierung

            Clickvia Createfilter_var($url, All.

            FILTER_VALIDATE_URL) Automatische

            ASlug-Generierung resultsfür tablejede willURL

            appearErgebnisanzeige showingmit each URL, its new short link, and whether it succeeded or failed. Erfolgs-/Fehlerstatus

            Auf der Edit-Seite (edit.php) können Sie alle Eigenschaften eines Links ändern. Besonders interessant ist die Note:Passwort-Verwaltung Batch-importedmit linksdrei do not get custom slugs, passwords, or expiration — those can be set afterwards by editing each link.


            5. Searching and Sorting Links

              Search: Type a keyword into the search bar above the links table and click Search. It matches against both slugs and destination URLs. Click Clear to remove the filter. Sort: Click any column header in the table (Slug, Destination, Clicks, Created) to sort by that column. Clicking again toggles between ascending and descending order.
              ButtonAuswahl Whatpassword_action
              it doesVerhalten CopyBehalten / Kein Passwort Copieskeep thepassword shortwird URLaus to$data your clipboard. The button briefly changes to "Copied!" to confirm.entfernt QRNeues setzen Openschange aNeues pop-upPasswort showingwird the QR code for this link, with options to download it as PNG or SVG (see Section 8).bcrypt-gehasht EditEntfernen Opens the edit page where you can change every setting of the link (see Section 7). Statsremove Openspassword awird detailedauf statistics'' pagegesetzt for thisin linkupdateLink (seewird Sectiones 9).zu Delete Deletes the link permanently. A confirmation dialog appears first — click OK to confirm or Cancel to abort.null

              Technischer Hintergrund: In updateLink wird die OpenGraph-Daten-Aktualisierung nur ausgelöst, wenn sich die URL geändert hat. Das verhindert unnötige HTTP-Requests an die Ziel-URL:

              $og = ($url !== $link['url']) ? fetchOpenGraph($url) : [
                  'og_title' => $link['og_title'],
                  'og_description' => $link['og_description'],
                  'og_image' => $link['og_image'],
              ];
              

              Das Löschen erfolgt via POST-Request mit einer JavaScript-Bestätigung. Dank ON DELETE CASCADE in der Datenbank-Schema-Definition werden zugehörige Click-Datensätze automatisch mitgelöscht.


              7.5. EditingPasswortgeschützte a LinkLinks

              Besucher → /mein-slug
                       → getLinkBySlug() findet Link mit password ≠ null
                       → GET-Request: Zeige Passwort-Formular
                       → POST-Request mit link_password:
                          → password_verify(eingabe, hash) → true:  recordClick + Redirect
                                                            → false: Fehlermeldung
              

              Wichtig: Das Passwort wird immer als bcrypt-Hash gespeichert (password_hash($password, PASSWORD_DEFAULT)). Beim Vergleich wird password_verify() verwendet, was automatisch den Salt aus dem Hash extrahiert. Das bedeutet: Selbst wenn die Datenbank kompromittiert wird, sind die Link-Passwörter nicht im Klartext einsehbar.


              6. QR-Codes

              LinkShorter enthält eine vollständig eigene QR-Code-Implementierung – es werden keine externen APIs oder Libraries benötigt.

              Technischer Tiefgang: QR-Code-Generierung

              Die Funktion qrEncode implementiert den kompletten QR-Code-Standard:

                Versionswahl: Basierend auf der Datenlänge wird die minimale QR-Version (1–40) bestimmt. Version 1 hat 21×21 Module, Version 40 hat 177×177 Module. Die Kapazitätstabelle $capacityL enthält die maximale Byte-Kapazität für Error Correction Level L (Low, ~7% Fehlerkorrektur).

                Matrix-Aufbau:

                • URLplaceFinderPattern: Drei the7×7-Erkennungsmuster destinationin theden shortEcken link(oben-links, pointsoben-rechts, to.unten-links)
                • SlugTiming-Patterns: Abwechselnde theModule shortin code.Zeile Changing6 itund changesSpalte your short URL.6
                • PasswordplaceAlignmentPattern: Ab chooseVersion from:2
                    werden
                  • KeepAusrichtungsmuster currentplatziert (don'tPositionen changeaus anything)
                  Set new (type a new password in the field that appears) Remove (remove password protection entirely)getAlignmentPatternPositions)

                  Expires AtDatenkodierung (encodeData):

                  set
                  orMode clearIndicator: 0100 (Byte-Modus) Zeichenzähler: 8 Bit (Version 1–9) oder 16 Bit (ab Version 10) Daten als 8-Bit-Bytes Padding mit 0xEC 0x11 (abwechselnd) Reed-Solomon-Fehlerkorrektur via generateECCodewords

                  Galois-Feld-Arithmetik: Für die Fehlerkorrektur werden Berechnungen im GF(2⁸) durchgeführt. Die Funktionen gfMultiply, gfExp und gfLog implementieren die Multiplikation über das irreduzible Polynom 0x11D (x⁸ + x⁴ + x³ + x² + 1).

                  Data-Interleaving: Bei mehreren Blöcken werden die Daten- und EC-Codewords interleaved (verschachtelt), um Burst-Fehler besser zu korrigieren.

                  Maskierung: Alle 8 Maskmuster werden getestet. calculatePenalty berechnet die Strafpunkte nach vier Regeln:

                    Fünf oder mehr gleiche Module in einer Reihe 2×2-Blöcke gleicher Module Spezielle Muster (1:1:3:1:1) Verhältnis dunkler zu heller Module nahe 50%

                    Das Mask-Pattern mit der niedrigsten Penalty wird gewählt.

                    Ausgabeformate

                      PNG (getQRCodePNG): Erzeugt via GD-Library (imagecreatetruecolor), mit optionalem Center-Icon via ImageMagick (convert-Befehl) SVG (getQRCodeSVG): Reine XML-Generierung, Icon wird als eingebettetes SVG eingefügt

                      Caching

                      Beide Formate werden im Verzeichnis data/qr_cache/ gecacht (24 Stunden TTL). Der Cache-Key ist ein MD5-Hash aus Daten + Größe + Format.

                      Custom Icon

                      Unter Settings können Sie ein SVG-Icon hochladen (admin/action mit action=upload_icon), das im Zentrum aller QR-Codes angezeigt wird. Beim Upload wird der QR-Cache geleert, damit die neuen QR-Codes das Icon enthalten.


                      7. OpenGraph-Proxying

                      $isCrawler = preg_match(
                        '/facebookexternalhit|Twitterbot|LinkedInBot|WhatsApp|Slackbot|TelegramBot|Discordbot|bot|crawler|spider/i',
                        $ua
                      );
                      

                      Statt den Crawler weiterzuleiten, wird og_proxy.php ausgeliefert – eine minimale HTML-Seite mit den OpenGraph-Meta-Tags der Ziel-URL. Das bewirkt, dass in der Vorschau (z.B. auf Facebook, Twitter, Slack) das Bild, der Titel und die Beschreibung der Original-Seite angezeigt werden, obwohl der geteilte Link eine kurze URL ist.

                      Technischer Hintergrund: fetchOpenGraph extrahiert beim Erstellen eines Links die Meta-Daten:

                        HTTP-Request an expirationdie date.Ziel-URL mit 10-Sekunden-Timeout HTML-Parsing via DOMDocument Extraktion von og:title, og:description, og:image Fallback auf <title>-Tag, wenn kein og:title vorhanden

                        Die SSL-Verifikation ist bewusst deaktiviert (verify_peer => false), um Probleme mit selbstsignierten Zertifikaten zu vermeiden. In Produktionsumgebungen sollte das ggf. angepasst werden.

                        Wichtig: Das OpenGraph-Proxying funktioniert auch für passwortgeschützte Links – Crawler erhalten die Vorschau, ohne ein Passwort eingeben zu müssen. Normale Benutzer werden weiterhin nach dem Passwort gefragt.


                        8. Statistiken & Click-Tracking

                        Was wird erfasst?

                        Bei jedem erfolgreichen Redirect (auch nach Passworteingabe) ruft recordClick zwei Datenbankoperationen aus:

                          Inkrement des Klickzählers in der links-Tabelle Detaillierter Click-Eintrag in der clicks-Tabelle:
                            ip: IP-Adresse des Besuchers ($_SERVER['REMOTE_ADDR']) user_agent: Browser-Kennung referer: Woher der Besucher kam clicked_at: Zeitstempel (automatisch via SQLite datetime('now'))

                            Stats-Seite

                            Die Stats-Seite (stats.php) zeigt:

                              Gesamtklicks als große Zahl Max Clicks (Limit set or clear a click limit. Active checkbox — uncheck to deactivate the link (visitors will see a "No longer available" page). Check it again to reactivate.

                              Click Save Changes when finished.

                              The edit page also shows:

                                A QR Code preview with download buttons. An OpenGraph Preview showing the title, description, and image that social media platforms (Facebook, Twitter, Slack, etc.) will display when someone shares your short link.

                                8. QR Codes

                                  From the dashboard: Click the QR button on any link's row. A pop-up shows the code with download options. From the edit or stats page: A QR code preview is shown at the bottom.

                                  Download formats:

                                    PNG — a standard image file, good for printing or embedding in documents. SVG — a scalable vector file, ideal for high-quality printing at any size.

                                    If your administrator has uploaded a center icon (see Settings), it will appear in the middle of every QR code.


                                    9. Click Statistics

                                    Click the Stats button on any link to see:

                                      Total Clicks — how many times the link has been used. Max Clicks — the limit you set (oroder for unlimited).) Status — ✓ for active, ✗ for inactive.(aktiv/inaktiv) Recent Clicks table — the lastLetzte 100 clicks, showing:
                                        TimeKlicks als whenTabelle themit clickIP, happened.Referer, IPUser-Agent und the visitor's IP address. Referer — where the visitor came from (e.g. a social media site), or - if unknown. User Agent — the visitor's browser/device information. Zeitstempel

                                        IfDie theAbfrage linkin isgetClickStats currentlyist inactive,auf a100 ReactivateEinträge buttonlimitiert appears(LIMIT to100), turnum itbei backviel-geklickten on.Links die Performance zu gewährleisten.

                                          active = 0 (manuell deaktiviert) max_clicks erreicht wurde (clicks >= max_clicks) expires_at in der Vergangenheit liegt

                                          In allen drei Fällen wird unavailable.php angezeigt.


                                          10.9. Settings

                                          Die

                                          Click Settings in the top navigation bar.

                                          QR Code Center Icon

                                          You can upload a small logo (in SVG format only) that will be placed in the center of all generated QR codes:

                                            Click Choose File and select an .svg file from your computer. Click Upload Icon. All previously cached QR codes are regenerated automatically.

                                            If an icon is already uploaded, a small preview is shown.

                                            Configuration Info

                                            The settings page also displays read-only information:

                                              Base URL — the root address of your LinkShorter installation. Database — the file path of the database. Cron URL — the URL to call periodically to auto-deactivate expired links (see Section 12).

                                              11. What Visitors See

                                              Scenario What happens Normal link The visitor is instantly redirected to the destination URL. Password-protected link The visitor sees a "This link is password protected" page. They must enter the correct password and click Continue to be redirected. Expired / inactive / max-clicks-reached link The visitor sees a "No longer available" page. Non-existent slug The visitor sees a "404 — This link does not exist" page. Social media crawler (Facebook, Twitter, Slack, Discord, etc.) Instead of a redirect, the crawler receives OpenGraph metadata (title, description, image) so the link shows a rich preview in chat and social feeds.

                                              12. Automatic Expiration (Cron)

                                              https://link.fabula.vision/cron
                                              

                                              To make this run automatically, ask your hosting provider or administrator to set up a cron job (a scheduled task) that visits this URL periodically, for example every 5 minutes. This ensures expired links are deactivated promptly.

                                              If you don't set up a cron job, expired links will still show the "No longer available" page to visitors (the expiration is checked at click time too), but they will remain listed as "Active" in your dashboard until the cron URL is called.


                                              13. REST-API (for Advanced Users or Integrations)

                                              LinkShorter providesbietet twozwei APIAPI-Endpunkte, endpointsdie thatmit externalHTTP toolsBasic orAuthentication browsergeschützt extensions can use:sind.

                                              CheckPOST Connection/api/shorten

                                              Erstellt einen neuen Kurzlink.

                                              Request:

                                              GETPOST https://link.fabula.vision/api/checkshorten HTTP/1.1
                                              Authorization: Basic (your credentials)
                                              

                                              Returns {"status":"ok","title":"LinkShorter"} if the credentials are correct.

                                              POST https://link.fabula.vision/api/shorten
                                              Authorization: Basic (your credentials)base64(username:password)
                                              Content-Type: application/json
                                              
                                              {
                                                "url": "https://example.com/long-sehr-lange-url",
                                                "slug": "optional-custom-slug"custom",           // optional
                                                "password": "optional-password"geheim",       // optional
                                                "expires_at": "2025-12-31T23:59", // optional
                                                "max_clicks": 100           // optional
                                              }
                                              

                                              ReturnsResponse (Erfolg):

                                              {
                                                "short_url": "https://link.fabula.vision/abc123"kurz.example.com/custom",
                                                "slug": "abc123"custom"
                                              }
                                               on success.
                                              

                                              BasicResponse authentication(Fehler):

                                              {
                                                "error": "Slug already exists"
                                              }
                                              

                                              GET /api/check

                                              Prüft die Erreichbarkeit und Authentifizierung einer Instanz. Wird von der Chrome-Extension beim Hinzufügen einer neuen Instanz verwendet.

                                              Response:

                                              {
                                                "status": "ok",
                                                "title": "LinkShorter"
                                              }
                                              

                                              Technischer Hintergrund

                                              Die Authentifizierung wird im index.php inline geprüft:

                                              $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
                                              if (preg_match('/^Basic\s+(.+)$/i', $authHeader, $m)) {
                                                  $decoded = base64_decode($m[1]);
                                                  list($user, $pass) = explode(':', $decoded, 2);
                                                  // Vergleich mit ADMIN_USERNAME und ADMIN_PASSWORD
                                              }
                                              

                                              Hinweis: meansApache yourkann usernameden andAuthorization-Header passwordmanchmal arenicht sentan encodedPHP withweiterleiten. everyIn request.diesem MostFall HTTPmuss tools (Postman, cURL, browser extensions) have a built-in "Basicder Auth".htaccess optionfolgende Zeile justergänzt enter your LinkShorter username and password.werden:

                                              SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
                                              

                                              14.10. QuickDie ReferenceChrome-Extension

                                              Überblick

                                              Die Chrome-Extension ermöglicht es, die aktuelle Tab-URL mit einem Klick zu kürzen, ohne das Dashboard öffnen zu müssen. Sie unterstützt mehrere LinkShorter-Instanzen, was nützlich ist, wenn man verschiedene Domains für verschiedene Zwecke nutzt.

                                              Installation

                                                Navigieren Sie in Chrome zu chrome://extensions/ Aktivieren Sie den Entwicklermodus (oben rechts) Klicken Sie „Entpackte Erweiterung laden" Wählen Sie den Ordner chrome-extension/

                                                Dateien der Extension

                                                I want to…Datei Do thisFunktion
                                                Create a linkmanifest.json DashboardExtension-Konfiguration (Manifest fill in URL → click Create Short LinkV3)
                                                Create many links at oncepopup.html Dashboard → Batch Import → paste URLs → Create AllUI-Struktur
                                                Copy a short URLpopup.css Dashboard → click Copy on the link's rowStyling
                                                See a QR codepopup.js DashboardGesamte → click QR on the link's row
                                                Edit a link Dashboard → click Edit See who clicked Dashboard → click Stats Delete a link Dashboard → click Delete → confirm Deactivate without deleting Edit page → uncheck Active → Save Changes Add password protection Edit page → Password → Set new → type password → Save Changes Set an expiration Create or Edit page → fill in Expires At Upload a QR logo Settings → upload an SVG file Log out Click Logout in the top navigationLogik

                                                Instanz hinzufügen

                                                  Klicken Sie auf das ⚙-Symbol (Settings) Geben Sie ein:
                                                    Name: Anzeigename (z.B. „Mein Server") URL: Die BASE_URL Ihrer LinkShorter-Installation Username/Password: Wie in config.php konfiguriert Klicken Sie „Add & Verify"

                                                    Technischer Hintergrund: Beim Hinzufügen wird zunächst ein GET /api/check ausgeführt, um die Verbindung und Authentifizierung zu testen. Erst wenn {"status": "ok"} zurückkommt, wird die Instanz gespeichert. Die Instanzen werden in chrome.storage.sync gespeichert, was bedeutet, dass sie über mehrere Chrome-Installationen hinweg synchronisiert werden (wenn der Nutzer in Chrome eingeloggt ist).

                                                    URL kürzen

                                                      Navigieren Sie zur gewünschten Webseite Klicken Sie auf das LinkShorter-Icon in der Toolbar Die aktuelle Tab-URL wird automatisch eingetragen (via chrome.tabs.query) Optional: Wählen Sie eine andere Instanz oder geben Sie einen Custom Slug ein Klicken Sie „Shorten" Die verkürzte URL erscheint mit Copy-Button

                                                      Berechtigungen

                                                      Die Extension benötigt nur zwei Berechtigungen (manifest.json):

                                                        activeTab: Zugriff auf die URL des aktiven Tabs storage: Speichern der Instanz-Konfigurationen

                                                        Es werden keine Host-Permissions benötigt, da fetch() in Manifest V3 standardmäßig CORS-Requests durchführen darf (die API-Endpunkte müssen allerdings CORS erlauben oder die Extension muss die Requests direkt an die URL senden).


                                                        11. Automatische Link-Expiration (Cron)

                                                        Einrichtung

                                                        Der Endpunkt /cron deaktiviert alle abgelaufenen Links. Die Funktion expireLinks führt folgendes SQL aus:

                                                        Cron-Job einrichten

                                                        Fügen Sie in Ihrer Crontab folgenden Eintrag hinzu (z.B. alle 5 Minuten):

                                                        */5 * * * * curl -s https://kurz.example.com/cron > /dev/null 2>&1
                                                        

                                                        Alternativ via PHP-CLI:

                                                        */5 * * * * php /var/www/html/index.php route=cron > /dev/null 2>&1
                                                        

                                                        Die Antwort ist ein JSON-Objekt: {"expired": 3} – die Anzahl der gerade deaktivierten Links.

                                                        Hinweis: Der Cron-Endpunkt ist nicht authentifiziert. Er führt jedoch nur eine Statusänderung durch (aktiv → inaktiv) und gibt keine sensiblen Daten zurück. Wenn Sie das absichern möchten, können Sie einen API-Key prüfen oder den Zugriff per .htaccess einschränken.

                                                        Wichtig: Auch ohne Cron werden abgelaufene Links beim Aufrufen als „nicht verfügbar" angezeigt, da die Prüfung strtotime($link['expires_at']) <= time() direkt in der Routing-Logik stattfindet. Der Cron-Job sorgt lediglich dafür, dass der active-Status in der Datenbank korrekt gesetzt wird (relevant für die Dashboard-Anzeige).


                                                        12. Sicherheitshinweise

                                                        Passwort in config.php

                                                        Das Admin-Passwort in config.php steht im Klartext. Stellen Sie sicher, dass:

                                                          Die Datei nicht über den Webserver direkt abrufbar ist Ein starkes Passwort verwendet wird (nicht das Standard-hackme123!)

                                                          Datenbankschutz

                                                          Die .htaccess blockiert den direkten Zugriff auf .db- und .sqlite-Dateien:

                                                          <FilesMatch "\.db$">
                                                              Require all denied
                                                          </FilesMatch>
                                                          

                                                          XSS-Schutz

                                                          Alle Benutzereingaben werden in den Templates mit htmlspecialchars() escaped, z.B.:

                                                          <?= htmlspecialchars($link['slug']) ?>
                                                          

                                                          SQL-Injection-Schutz

                                                          Alle Datenbankabfragen verwenden Prepared Statements mit Paramter-Binding:

                                                          $stmt = $db->prepare('SELECT * FROM links WHERE slug = ?');
                                                          $stmt->execute([$slug]);
                                                          

                                                          Die einzige Ausnahme ist die dynamische ORDER BY-Klausel in getAllLinks, die aber über ein Whitelist-Array abgesichert ist.

                                                          CSRF-Schutz

                                                          Aktuell gibt es keinen CSRF-Token-Schutz. Da die Anwendung nur einen einzigen Admin-User hat, ist das Risiko begrenzt, aber bei einer Erweiterung sollte ein Token-System implementiert werden.


                                                          13. Technische Architektur im Detail

                                                          Routing-System

                                                          Das Routing in index.php funktioniert als Kaskade von if-Statements:

                                                          Eingang: $_GET['route'] (via .htaccess Rewrite)
                                                                   ↓
                                                          1. Exakte Routen: 'cron', 'api/shorten', 'api/check', 'admin/action'
                                                                   ↓
                                                          2. QR-Routen: 'qr/png', 'qr/svg', 'qr/png/download', 'qr/svg/download'
                                                                   ↓
                                                          3. Slug-Auflösung: Beliebiger Pfad → getLinkBySlug()
                                                                   ↓
                                                          4. Seiten-Routing: $_GET['page'] → login, dashboard, edit, stats, settings
                                                          

                                                          Datenbank-Schema

                                                          Frontend-JavaScript

                                                          app.js ist bewusst minimal und framework-frei:

                                                            Batch-Modal: Toggle der CSS-Klasse active auf dem Overlay QR-Modal: Dynamisches Setzen der src- und href-Attribute basierend auf dem Slug Slug-Preview: Live-Aktualisierung beim Tippen mit Regex-Filterung Copy-Button: Via navigator.clipboard.writeText() mit visueller Bestätigung Delete-Confirmation: Nativer confirm()-Dialog

                                                            Performance-Überlegungen

                                                              SQLite WAL-Modus: Ermöglicht gleichzeitige Leser während ein Schreibvorgang läuft QR-Code-Cache: Verhindert wiederholte Berechnung (24h TTL) OpenGraph-Cache in DB: Meta-Daten werden nur beim Erstellen/Ändern der URL abgerufen Statische Assets: CSS und JS werden direkt ausgeliefert (kein Build-Prozess nötig)

                                                              Zusammenfassung

                                                              LinkShorter v2 ist eine schlanke, selbstgehostete Lösung ohne externe Abhängigkeiten. Die bemerkenswerteste technische Leistung ist die vollständige QR-Code-Implementierung in reinem PHP, inklusive Reed-Solomon-Fehlerkorrektur und Galois-Feld-Arithmetik. Die Chrome-Extension ergänzt das System um einen komfortablen Workflow direkt aus dem Browser heraus, wobei die Multi-Instanz-Unterstützung besonders für Nutzer mit mehreren Domains nützlich ist.