Skip to content

MSK-Scripts/msk-shortener

Repository files navigation

πŸ”— MSK Shortener

Self-hosted URL shortener with statistics, QR codes, and password protection.

Part of the MSK ecosystem – open source, privacy-friendly, no tracking, no third parties.

License: AGPL v3 Next.js TypeScript Made with ❀️

Live demo β†’ Β Β·Β  Report a bug Β Β·Β  Request a feature


✨ Features

πŸ”— Custom short codes Define your own short URLs or let the system generate one
πŸ“Š Click statistics Anonymous tracking with interactive charts and aggregations
πŸ”’ Password protection Lock links behind a password (bcrypt, cost 12)
⏱️ Link expiration Auto-expire links at a date/time of your choice
πŸ“± QR code generator Download as PNG or SVG
πŸ”Œ REST API Fully documented endpoints for automation
🌍 Bilingual UI German and English, switchable per user (cookie-based)
πŸ›‘οΈ Privacy-friendly IP hashing, no GeoIP, no third-party trackers, no analytics cookies
⚑ Rate limiting 20 links/hour per IP to prevent abuse
πŸ—‘οΈ Delete tokens Every short link comes with a one-time delete token

πŸš€ Live Instance

The reference deployment runs at s.msk-scripts.de.

You're welcome to use it, or self-host your own (see Installation below).


πŸ› οΈ Tech Stack

Category Technology
Framework Next.js 15 (App Router)
Language TypeScript (strict mode)
Database MariaDB via mysql2
Styling Tailwind CSS
Validation Zod 4
i18n next-intl 4
Hashing bcryptjs 3
Short codes nanoid
QR codes qrcode
Charts Recharts
UA parsing ua-parser-js 2
Deployment Debian/Ubuntu + Apache2 + systemd
CI/CD GitHub Actions

Deliberately not used: Prisma (too much overhead), Redis (single-server setup), third-party auth providers.


πŸ“‹ Installation

Prerequisites

  • Node.js 20 or later
  • MariaDB 10.6 or later
  • A modern terminal (Linux, macOS, or WSL on Windows)

Local development

# Clone the repository
git clone https://github.com/MSK-Scripts/msk-shortener.git
cd msk-shortener

# Install dependencies
npm install

# Copy the environment template and adjust the values
cp .env.example .env

# Run database migrations
npm run migrate

# Start the dev server
npm run dev

The app will be available at http://localhost:3000.

Production server

For a full production setup on Debian/Ubuntu, see the dedicated Deployment Guide β†’.

TL;DR for a one-shot install:

curl -fsSL https://raw.githubusercontent.com/MSK-Scripts/msk-shortener/main/deployment/scripts/install.sh \
  | sudo bash

This walks you through domain setup, MariaDB, Apache, Let's Encrypt SSL, systemd, and prints out the GitHub secrets you'll need for CI/CD.


πŸ“‘ REST API

Every feature in the web UI is available through the REST API.

Full documentation: docu.msk-scripts.de/shortener

Create a link

curl -X POST https://s.msk-scripts.de/api/links \
  -H "Content-Type: application/json" \
  -d '{
    "url":        "https://msk-scripts.de",
    "customCode": "msk",
    "expiresAt":  "2026-12-31T23:59:59Z"
  }'

Response:

{
  "shortCode":   "msk",
  "shortUrl":    "https://s.msk-scripts.de/msk",
  "deleteToken": "dk_a7c4f2…",
  "expiresAt":   "2026-12-31T23:59:59.000Z",
  "hasPassword": false
}

Other endpoints

Method Path Description
POST /api/links Create a new short link
GET /api/links/:code Look up a link
DELETE /api/links/:code Delete a link (requires deleteToken)
GET /api/links/:code/stats Get click statistics for one link
GET /api/links/:code/qr QR code as PNG or SVG
GET /api/stats Global statistics across all links
POST /api/verify Verify a password-protected link

βš™οΈ Configuration

All configuration happens through environment variables. See .env.example for the full list.

Most important:

Variable Description
NEXT_PUBLIC_BASE_URL Public base URL of your instance (used for QR codes and share URLs)
DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME MariaDB connection
IP_HASH_SECRET Secret for HMAC-based IP hashing β€” generate with openssl rand -hex 32
RATE_LIMIT_CREATE_PER_HOUR Max links per IP per hour (default: 20)
SHORTCODE_LENGTH Length of auto-generated short codes (default: 7)

πŸ”’ Security & Privacy

MSK Shortener is built with GDPR compliance and personal privacy in mind.

  • No IP addresses stored in plain text β€” only salted HMAC hashes
  • No GeoIP lookups β€” country/region data is never collected
  • No third-party trackers β€” no Google Analytics, no Plausible, nothing
  • No tracking cookies β€” the only cookie sets the user's chosen language
  • Password hashing β€” bcrypt with cost factor 12
  • SSRF protection β€” private IP ranges (RFC 1918, loopback, link-local) are rejected as targets
  • Rate limiting β€” abuse protection per IP
  • Strict CSP headers β€” defense in depth against XSS

A full privacy policy in German and English is available at /privacy on every instance.

To report a security issue, see SECURITY.md.


🌍 Internationalization

The UI is available in German and English.

  • Language is detected from the user's browser on first visit (Accept-Language header)
  • Users can switch languages via the header dropdown β€” the choice is stored in a cookie
  • No URL prefix (no /de/… or /en/…) β€” short links stay as short as possible
  • All UI strings live in messages/de.json and messages/en.json

Want to add another language? PRs welcome!


🀝 Contributing

Contributions are welcome! This is a single-maintainer project, so please:

  1. Open an issue first to discuss larger changes
  2. Keep PRs focused β€” one concern per PR
  3. Run npm run lint and npm run type-check before submitting
  4. Follow the existing code style

For bug reports, please include:

  • Steps to reproduce
  • Expected vs. actual behavior
  • Your environment (Node version, browser, OS)

πŸ“„ License

GNU Affero General Public License v3.0 or later Β© MSK Scripts

What this means in plain English

  • βœ… You can use, copy, and modify this software
  • βœ… You can distribute your own versions
  • ⚠️ If you offer a modified version as a network service (e.g. a public web app), you must make your modifications publicly available under the same license
  • ⚠️ Derivative works must also be licensed under AGPL-3.0

If you need a commercial license without the copyleft obligations, please contact the author at info@msk-scripts.de.


Part of the MSK ecosystem Β Β·Β  msk-scripts.de Β Β·Β  musiker15.de

About

Self-hosted URL shortener with statistics, QR codes, and password protection.

Resources

License

Security policy

Stars

Watchers

Forks

Contributors