Tools beginner ⏱ 25 minutes

YOURLS: Your Own URL Shortener

Simple, powerful URL shortener. Custom domains, plugins, and statistics.

By Homelab Team • 2025-01-15
#yourls#url-shortener#plugins#statistics#docker

Introduction

Since 2009, YOURLS (Your Own URL Shortener) has been the go-to open-source solution for self-hosted URL shortening. While newer alternatives have emerged with flashy interfaces and modern architectures, YOURLS remains beloved by the homelab community for its simplicity, stability, extensive plugin ecosystem, and battle-tested reliability. If you want a “set it and forget it” URL shortener that just works, YOURLS is your answer.

YOURLS is written in PHP and uses MySQL/MariaDB for storage, making it lightweight and easy to deploy on virtually any hosting environment. Its plugin architecture allows extensive customization, from QR code generation to advanced analytics, password-protected links, and API integrations. Whether you’re shortening personal links, managing a marketing campaign, or building a URL shortener into your application, YOURLS provides a proven foundation.

What you’ll learn:

  • Deploy YOURLS with Docker Compose in 15 minutes
  • Configure custom domains and branded short links
  • Use the admin interface and bookmarklets for quick shortening
  • Leverage the API for programmatic URL management
  • Install essential plugins for enhanced functionality
  • Set up private vs. public URL shortening modes
  • Implement analytics and tracking
  • Configure browser bookmarklets for one-click shortening

Prerequisites:

  • Docker and Docker Compose installed
  • Basic understanding of Docker containers
  • A domain name (optional, recommended for custom short URLs)
  • 256MB+ RAM available
  • Basic familiarity with PHP applications

Why Choose YOURLS?

The Classic URL Shortener

YOURLS occupies a unique position in the self-hosted ecosystem:

Advantages:

  1. Mature and stable: 15+ years of development and bug fixes
  2. Huge plugin ecosystem: 100+ plugins for every need
  3. Simple architecture: PHP + MySQL = runs anywhere
  4. Low resource usage: Runs on minimal hardware
  5. Active community: Large user base and extensive documentation
  6. Custom keywords: Create memorable short URLs like /docs or /blog

Trade-offs:

  1. Older codebase: Traditional PHP architecture vs. modern frameworks
  2. Basic UI: Functional but not as polished as newer alternatives
  3. Manual plugin management: No built-in plugin installer
  4. Limited built-in analytics: Requires plugins for advanced tracking
FeatureYOURLSShlink
First Release20092016
LanguagePHPPHP
DatabaseMySQL/MariaDBMultiple (PostgreSQL, MySQL, SQLite)
UITraditional admin panelModern REST API + web client
Plugins100+ community pluginsLimited plugin ecosystem
Built-in AnalyticsBasicAdvanced with geolocation
APIXML-RPC/JSONRESTful API
Best ForPlugin customization, custom keywordsModern integrations, detailed analytics

Choose YOURLS if:

  • You want extensive customization via plugins
  • You need custom memorable keywords
  • You prefer traditional PHP applications
  • You have existing YOURLS integrations

Choose Shlink if:

  • You want built-in advanced analytics
  • You need modern REST API
  • You prefer containerized microservices
  • You want real-time visit tracking

Installation: Docker Compose Method

Step 1: Create Project Directory

mkdir -p ~/yourls
cd ~/yourls

Step 2: Create docker-compose.yml

This configuration deploys YOURLS with MySQL database:

version: '3.8'

services:
  yourls:
    image: yourls:latest
    container_name: yourls
    restart: always
    ports:
      - "8080:80"
    environment:
      YOURLS_DB_HOST: mysql
      YOURLS_DB_USER: yourls
      YOURLS_DB_PASS: secure_password_here
      YOURLS_DB_NAME: yourls
      YOURLS_SITE: https://go.yourdomain.com
      YOURLS_USER: admin
      YOURLS_PASS: strong_admin_password_here
      # Generate unique random strings for these
      YOURLS_UNIQUE_URLS: "true"
      YOURLS_COOKIEKEY: "change_this_to_random_string_32_chars"
      # Optional: Hours to keep login cookie valid
      YOURLS_HOURS: "24"
    volumes:
      - ./plugins:/var/www/html/user/plugins
      - ./pages:/var/www/html/user/pages
    depends_on:
      - mysql
    networks:
      - yourls-net

  mysql:
    image: mysql:8
    container_name: yourls-db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root_password_here
      MYSQL_DATABASE: yourls
      MYSQL_USER: yourls
      MYSQL_PASSWORD: secure_password_here
    volumes:
      - ./data/mysql:/var/lib/mysql
    networks:
      - yourls-net
    command: --default-authentication-plugin=mysql_native_password

networks:
  yourls-net:
    driver: bridge

Configuration notes:

  • YOURLS_SITE: Your short URL domain (include https://)
  • YOURLS_USER/PASS: Admin credentials for login
  • YOURLS_COOKIEKEY: Random string for session security
  • YOURLS_UNIQUE_URLS: Prevents duplicate short URLs for same long URL
  • Volumes mount plugin and page directories for persistence

Step 3: Generate Secure Credentials

# Generate random cookiekey (32 characters)
openssl rand -base64 32

# Update docker-compose.yml with secure values
sed -i 's/secure_password_here/YOUR_DB_PASSWORD/g' docker-compose.yml
sed -i 's/root_password_here/YOUR_ROOT_PASSWORD/g' docker-compose.yml
sed -i 's/strong_admin_password_here/YOUR_ADMIN_PASSWORD/g' docker-compose.yml
sed -i "s/change_this_to_random_string_32_chars/$(openssl rand -base64 32)/g" docker-compose.yml

# Set file permissions
chmod 600 docker-compose.yml

Step 4: Create Plugin and Pages Directories

mkdir -p plugins pages

Step 5: Start Services

docker compose up -d

Step 6: Verify Installation

# Check containers are running
docker ps | grep yourls

# View logs
docker logs yourls
docker logs yourls-db

# Test web interface
curl -I http://localhost:8080

Step 7: Access Admin Interface

Navigate to: http://YOUR_SERVER_IP:8080/admin/

Login credentials:

  • Username: admin (or whatever you set in YOURLS_USER)
  • Password: Your YOURLS_PASS value

You should see the YOURLS admin dashboard with statistics and quick URL shortening form.


Initial Configuration

Admin Dashboard Overview

The YOURLS admin interface provides:

  1. Main Tools (top tabs):

    • Add: Quick URL shortening form
    • List URLs: View all shortened URLs
    • Plugins: Manage installed plugins
    • Tools: Statistics, bookmarklets, API info
  2. Statistics dashboard:

    • Total clicks
    • Total shortened URLs
    • Recent short URLs
    • Most popular links

Create Your First Short URL

Method 1: Admin Interface

  1. Navigate to admin panel
  2. Click “Add new URL” or use quick form at top
  3. Enter details:
    • Long URL: https://www.example.com/very/long/path/to/page
    • Custom keyword (optional): example-page
    • Title: Example Page (for reference)
  4. Click “Shorten”

Result: Short URL created instantly. If no custom keyword provided, random short code generated.

Method 2: Bookmarklet (setup below)

Drag bookmarklet to browser toolbar, click while on any page to instantly shorten current URL.


Custom Domain Setup

Use your own domain for branded short links.

DNS Configuration

Add DNS record for your short domain:

Type: A
Name: go.yourdomain.com
Value: YOUR_SERVER_IP
TTL: 3600

Or for Cloudflare users:

Type: A
Name: go
Value: YOUR_SERVER_IP
TTL: Auto
Proxy status: Proxied (orange cloud)

Reverse Proxy Configuration

Caddy (recommended):

# /etc/caddy/Caddyfile
go.yourdomain.com {
    reverse_proxy localhost:8080
    encode gzip
}
sudo systemctl reload caddy

Nginx:

# /etc/nginx/sites-available/yourls
server {
    server_name go.yourdomain.com;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Optional: Restrict admin access to local network
    location /admin/ {
        allow 192.168.1.0/24;
        deny all;
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    listen 443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/go.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/go.yourdomain.com/privkey.pem;
}

server {
    listen 80;
    server_name go.yourdomain.com;
    return 301 https://$server_name$request_uri;
}
sudo ln -s /etc/nginx/sites-available/yourls /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Update YOURLS Configuration

After setting up custom domain, update docker-compose.yml:

environment:
  YOURLS_SITE: https://go.yourdomain.com

Restart container:

docker compose restart yourls

API Usage

YOURLS provides a JSON API for programmatic access.

Generate API Signature

Option 1: Use username/password (less secure)

Every API request includes &username=admin&password=yourpass

Option 2: Use secret signature (recommended)

  1. Navigate to: http://go.yourdomain.com/admin/tools.php
  2. Find “Passwordless API” section
  3. Copy your secret signature (looks like: a1b2c3d4e5)

API Examples

Shorten URL:

# With signature
curl "http://go.yourdomain.com/yourls-api.php?signature=YOUR_SIGNATURE&action=shorturl&url=https://example.com&format=json"

# With username/password
curl "http://go.yourdomain.com/yourls-api.php?username=admin&password=yourpass&action=shorturl&url=https://example.com&format=json"

Response:

{
  "url": {
    "keyword": "abc12",
    "url": "https://example.com",
    "title": "Example Domain",
    "date": "2025-01-15 10:30:45",
    "ip": "192.168.1.100"
  },
  "status": "success",
  "message": "https://example.com added to database",
  "title": "Example Domain",
  "shorturl": "https://go.yourdomain.com/abc12",
  "statusCode": 200
}

Shorten with Custom Keyword:

curl "http://go.yourdomain.com/yourls-api.php?signature=YOUR_SIGNATURE&action=shorturl&url=https://github.com/YOURLS/YOURLS&keyword=gh-yourls&format=json"

Get URL Stats:

curl "http://go.yourdomain.com/yourls-api.php?signature=YOUR_SIGNATURE&action=url-stats&shorturl=abc12&format=json"

Response:

{
  "statusCode": 200,
  "message": "success",
  "link": {
    "shorturl": "https://go.yourdomain.com/abc12",
    "url": "https://example.com",
    "title": "Example Domain",
    "timestamp": "2025-01-15 10:30:45",
    "ip": "192.168.1.100",
    "clicks": 42
  }
}

Get Database Stats:

curl "http://go.yourdomain.com/yourls-api.php?signature=YOUR_SIGNATURE&action=stats&format=json"

Expand Short URL:

curl "http://go.yourdomain.com/yourls-api.php?signature=YOUR_SIGNATURE&action=expand&shorturl=abc12&format=json"

Python API Wrapper

import requests
from urllib.parse import urljoin

class YourlsAPI:
    def __init__(self, base_url, signature):
        self.base_url = base_url.rstrip('/')
        self.api_endpoint = urljoin(self.base_url, '/yourls-api.php')
        self.signature = signature

    def _request(self, action, **params):
        """Make API request"""
        params['signature'] = self.signature
        params['action'] = action
        params['format'] = 'json'

        response = requests.get(self.api_endpoint, params=params)
        return response.json()

    def shorten(self, url, keyword=None, title=None):
        """Shorten a URL"""
        params = {'url': url}
        if keyword:
            params['keyword'] = keyword
        if title:
            params['title'] = title

        return self._request('shorturl', **params)

    def stats(self, shorturl):
        """Get statistics for short URL"""
        return self._request('url-stats', shorturl=shorturl)

    def expand(self, shorturl):
        """Get original URL from short URL"""
        return self._request('expand', shorturl=shorturl)

    def db_stats(self):
        """Get database statistics"""
        return self._request('stats')

# Usage example
yourls = YourlsAPI('https://go.yourdomain.com', 'YOUR_SIGNATURE')

# Create short URL
result = yourls.shorten('https://example.com/long-url', keyword='my-link')
print(f"Short URL: {result['shorturl']}")
print(f"Status: {result['status']}")

# Get statistics
stats = yourls.stats('my-link')
print(f"Clicks: {stats['link']['clicks']}")

# Get database stats
db_stats = yourls.db_stats()
print(f"Total URLs: {db_stats['stats']['total_links']}")
print(f"Total clicks: {db_stats['stats']['total_clicks']}")

Essential Plugins

YOURLS’ plugin ecosystem is its superpower. Here are must-have plugins:

Installing Plugins

cd ~/yourls/plugins

# Download plugin (example: QR Code plugin)
git clone https://github.com/plugin-author/yourls-plugin-name.git

# Or download ZIP and extract
wget https://github.com/author/plugin/archive/main.zip
unzip main.zip
rm main.zip

After adding plugins, activate them:

  1. Navigate to admin panel
  2. Click “Plugins” tab
  3. Click “Activate” next to plugin name

Top 10 Essential Plugins

1. QR Code Generator

Install:

cd ~/yourls/plugins
git clone https://github.com/seandrickson/YOURLS-QRCode-Plugin.git qrcode

Usage: Access QR code at https://go.yourdomain.com/abc12+

2. Advanced Analytics

3. Password Protected URLs

Usage:

# Create password-protected short URL via API
curl "http://go.yourdomain.com/yourls-api.php?signature=YOUR_SIGNATURE&action=shorturl&url=https://secret-content.com&keyword=secret&password=mypass123&format=json"

4. Link Expiry

5. API Action Plugins

6. Preview URL

Usage: Access preview at https://go.yourdomain.com/abc12~

7. Google Analytics

8. Bulk Import

9. Public API

10. Random Keywords


Bookmarklets

Bookmarklets enable one-click URL shortening from any webpage.

Setup Bookmarklets

  1. Navigate to: http://go.yourdomain.com/admin/tools.php
  2. Find “Bookmarklets” section
  3. Drag one of these bookmarklets to browser bookmark bar:

Standard Bookmarklet:

  • Shortens current page URL
  • Opens popup with short URL

Shorten and Copy:

  • Shortens current page
  • Copies short URL to clipboard
  • No popup (silent operation)

Shorten and Tweet:

  • Shortens current page
  • Opens Twitter with pre-filled tweet containing short URL

Custom Bookmarklet

Create custom bookmarklet with auto-copy functionality:

javascript:(function(){var url='https://go.yourdomain.com/yourls-api.php?signature=YOUR_SIGNATURE&action=shorturl&format=json&url='+encodeURIComponent(location.href);fetch(url).then(r=>r.json()).then(d=>{navigator.clipboard.writeText(d.shorturl);alert('Short URL copied: '+d.shorturl)})})();

To use:

  1. Create new bookmark in browser
  2. Name: “Shorten & Copy”
  3. Paste JavaScript code as URL
  4. Save to bookmark bar

Usage: Click bookmarklet on any page to instantly shorten and copy URL.


Public vs. Private Mode

YOURLS can operate in public or private mode.

Private Mode (Default)

Only logged-in users can create short URLs.

Configuration: Already set by default with admin credentials.

Best for:

  • Personal use
  • Internal team links
  • Controlled URL creation

Public Mode

Anyone can create short URLs (with optional restrictions).

Enable public mode:

Install “Public Prefix & Shorten” plugin:

cd ~/yourls/plugins
git clone https://github.com/YOURLS/public-prefix-n-shorten.git

Activate in admin panel → Plugins.

Configure prefix restriction:

Edit plugin settings to require prefix for public URLs:

  • Public URLs must start with specific prefix (e.g., pub-)
  • Prevents abuse while allowing public use

Example: Public users create pub-example, admins create example.

Rate Limiting

Protect public instances from abuse:

Install rate limiting plugin:

cd ~/yourls/plugins
git clone https://github.com/YOURLS/yourls-api-rate-limit.git

Configuration:

  • Limit: 10 URLs per hour per IP
  • Customizable per plugin settings

Analytics and Tracking

Built-in Analytics

YOURLS includes basic analytics:

  1. Navigate to: “List URLs” in admin panel
  2. Click number in “Clicks” column for any URL
  3. View click details:
    • Total clicks
    • Click timeline
    • Individual click timestamps
    • Referrer information

Enhanced Analytics with Plugin

Install advanced analytics plugin:

cd ~/yourls/plugins
git clone https://github.com/miconda/yourls-advanced-analytics.git advanced-analytics

Activate in admin panel.

Features:

  • Geographic distribution maps
  • Browser/OS breakdown
  • Device type analysis
  • Referrer statistics
  • Time-based charts
  • Export to CSV

Google Analytics Integration

For enterprise-grade analytics:

cd ~/yourls/plugins
git clone https://github.com/powerthazan/YOURLS-Google-Analytics-Plugin.git google-analytics

Setup:

  1. Activate plugin in admin panel
  2. Navigate to plugin settings
  3. Enter Google Analytics tracking ID
  4. Configure tracking options

Result: Every short URL click tracked in Google Analytics with custom events.


Security Hardening

Restrict Admin Access

Method 1: Firewall rules

# Allow admin access only from local network
sudo ufw allow from 192.168.1.0/24 to any port 8080

Method 2: Nginx IP restriction

See reverse proxy configuration above for location-based IP restrictions.

Method 3: VPN-only access

Require Tailscale/WireGuard VPN for admin panel access:

# Only allow admin access via Tailscale IPs
location /admin/ {
    allow 100.64.0.0/10;  # Tailscale CGNAT range
    deny all;
    proxy_pass http://localhost:8080;
}

Enable HTTPS Only

Force HTTPS redirects in reverse proxy (see Nginx/Caddy configs above).

Update YOURLS configuration:

environment:
  YOURLS_SITE: https://go.yourdomain.com

Change Default Admin Path

Obfuscate admin panel location:

# Copy admin files to custom location
docker exec -it yourls bash
cp -r /var/www/html/admin /var/www/html/secret-admin-path

Access admin at: https://go.yourdomain.com/secret-admin-path/

Regular Password Updates

# Generate new strong password
openssl rand -base64 20

# Update docker-compose.yml
# Restart container
docker compose restart yourls

Troubleshooting

Issue 1: Cannot Access Admin Panel

Symptoms: 404 error on /admin/ path.

Solutions:

  1. Verify container is running: docker ps | grep yourls
  2. Check logs: docker logs yourls
  3. Ensure reverse proxy forwards /admin/ path correctly
  4. Test direct access: curl -I http://localhost:8080/admin/

Issue 2: Database Connection Errors

Symptoms: “Error connecting to database” message.

Diagnosis:

# Check database container
docker ps | grep yourls-db

# View database logs
docker logs yourls-db

# Test connection from YOURLS container
docker exec -it yourls mysql -h mysql -u yourls -p

Solutions:

  1. Verify database password matches in both containers
  2. Ensure database has fully initialized (wait 30 seconds after first start)
  3. Check network connectivity: docker network inspect yourls-net

Issue 3: Short URLs Not Redirecting

Symptoms: Short URL shows YOURLS error page instead of redirecting.

Solutions:

  1. Check URL exists: View “List URLs” in admin panel
  2. Verify keyword spelling (case-sensitive)
  3. Check for expired/deactivated plugins
  4. Review YOURLS error logs: docker logs yourls | grep -i error

Issue 4: Plugins Not Activating

Symptoms: Plugin activation fails or shows error.

Solutions:

  1. Verify plugin files in correct directory:
    ls -la ~/yourls/plugins/
  2. Check file permissions:
    chmod -R 755 ~/yourls/plugins/
  3. Review plugin requirements (some require PHP extensions)
  4. Check container logs during activation:
    docker logs -f yourls

Issue 5: API Returns Invalid Signature

Symptoms: API calls fail with “Invalid signature” error.

Solutions:

  1. Verify signature from: http://go.yourdomain.com/admin/tools.php
  2. Check signature hasn’t changed (regenerates if cookiekey changes)
  3. Ensure no extra spaces in signature parameter
  4. Test with username/password instead: &username=admin&password=yourpass

Backup and Maintenance

Automated Backup Script

#!/bin/bash
# backup-yourls.sh

BACKUP_DIR="/backups/yourls"
DATE=$(date +%Y%m%d-%H%M%S)

# Create backup directory
mkdir -p "$BACKUP_DIR"

# Backup MySQL database
docker exec yourls-db mysqldump -u yourls -psecure_password_here yourls | gzip > "$BACKUP_DIR/yourls-db-$DATE.sql.gz"

# Backup plugins and custom pages
tar czf "$BACKUP_DIR/yourls-plugins-$DATE.tar.gz" -C ~/yourls plugins pages

# Backup configuration
cp ~/yourls/docker-compose.yml "$BACKUP_DIR/docker-compose-$DATE.yml"

# Remove backups older than 30 days
find "$BACKUP_DIR" -name "yourls-db-*.sql.gz" -mtime +30 -delete
find "$BACKUP_DIR" -name "yourls-plugins-*.tar.gz" -mtime +30 -delete

echo "Backup completed: $DATE"

Make executable and schedule:

chmod +x backup-yourls.sh

# Add to crontab (daily at 2 AM)
crontab -e
0 2 * * * /root/backup-yourls.sh >> /var/log/yourls-backup.log 2>&1

Restore from Backup

#!/bin/bash
# restore-yourls.sh

BACKUP_FILE="$1"  # Path to .sql.gz backup file

if [ -z "$BACKUP_FILE" ]; then
    echo "Usage: $0 /path/to/backup.sql.gz"
    exit 1
fi

# Stop YOURLS container
docker compose stop yourls

# Restore database
zcat "$BACKUP_FILE" | docker exec -i yourls-db mysql -u yourls -psecure_password_here yourls

# Restart containers
docker compose start yourls

echo "Restore completed from: $BACKUP_FILE"

Update YOURLS

cd ~/yourls

# Backup before updating
./backup-yourls.sh

# Pull latest image
docker compose pull

# Restart with new image
docker compose up -d

# Check version
docker logs yourls | head -20

Database Maintenance

# Optimize database (quarterly)
docker exec -it yourls-db mysql -u yourls -psecure_password_here yourls -e "OPTIMIZE TABLE yourls_url, yourls_options, yourls_log;"

# Check database size
docker exec -it yourls-db mysql -u yourls -psecure_password_here yourls -e "SELECT table_name AS 'Table', ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Size (MB)' FROM information_schema.TABLES WHERE table_schema = 'yourls';"

Migration and Import

Import from Bitly

Export Bitly links and import to YOURLS:

  1. Export from Bitly (CSV format)
  2. Install import plugin:
    cd ~/yourls/plugins
    git clone https://github.com/YOURLS/YOURLS-Import-Export.git import-export
  3. Activate plugin in admin panel
  4. Navigate to: Tools → Import
  5. Upload CSV file
  6. Map columns appropriately
  7. Click “Import”

Import from Other Services

Most URL shorteners support CSV export. Format CSV:

keyword,url,title,timestamp
example1,https://example.com/page1,Example Page 1,2025-01-15 10:30:00
example2,https://example.com/page2,Example Page 2,2025-01-15 10:31:00

Use import plugin to bulk upload.


Advanced Customization

Custom Homepage

Create custom landing page at root URL:

# Create custom index page
cat > ~/yourls/pages/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>My URL Shortener</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 600px;
            margin: 100px auto;
            text-align: center;
        }
        h1 { color: #333; }
        .shortener {
            margin-top: 40px;
        }
        input[type="text"] {
            width: 70%;
            padding: 10px;
            font-size: 16px;
        }
        button {
            padding: 10px 20px;
            font-size: 16px;
            background: #007bff;
            color: white;
            border: none;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h1>URL Shortener</h1>
    <p>Shorten your long URLs quickly</p>
    <div class="shortener">
        <input type="text" id="longUrl" placeholder="Enter long URL here">
        <button onclick="shortenUrl()">Shorten</button>
        <p id="result"></p>
    </div>
    <script>
        function shortenUrl() {
            const longUrl = document.getElementById('longUrl').value;
            const apiUrl = '/yourls-api.php?signature=YOUR_SIGNATURE&action=shorturl&format=json&url=' + encodeURIComponent(longUrl);
            fetch(apiUrl)
                .then(r => r.json())
                .then(d => {
                    document.getElementById('result').innerHTML =
                        '<strong>Short URL:</strong> <a href="' + d.shorturl + '">' + d.shorturl + '</a>';
                });
        }
    </script>
</body>
</html>
EOF

Update docker-compose.yml to map custom homepage:

volumes:
  - ./pages/index.html:/var/www/html/index.html

Custom Error Pages

Create custom 404 page:

cat > ~/yourls/pages/404.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>404 - Short URL Not Found</title>
    <style>
        body { font-family: Arial; text-align: center; margin-top: 100px; }
        h1 { color: #d9534f; }
    </style>
</head>
<body>
    <h1>404 - Short URL Not Found</h1>
    <p>The short URL you requested does not exist.</p>
    <p><a href="/">Return to homepage</a></p>
</body>
</html>
EOF

Map in docker-compose.yml:

volumes:
  - ./pages/404.html:/var/www/html/404.html

Conclusion

You now have a fully functional YOURLS URL shortener with:

  • Custom branded short domain
  • Admin interface for URL management
  • REST API for automation
  • Plugin ecosystem for extended functionality
  • Bookmarklets for one-click shortening
  • Analytics and tracking capabilities
  • Secure, self-hosted infrastructure

Next steps:

  1. Install essential plugins (QR codes, analytics, password protection)
  2. Set up bookmarklets for daily use
  3. Configure API for application integrations
  4. Customize homepage and branding
  5. Implement automated backups
  6. Explore plugin ecosystem for additional features

YOURLS proves that sometimes the classics are classics for a reason. With 15+ years of development, extensive plugin support, and rock-solid reliability, YOURLS remains the gold standard for self-hosted URL shortening. Whether you’re managing personal links or running a full-scale URL shortening service, YOURLS provides the flexibility and control you need.


Additional Resources

Official:

Plugins:

Community:

Tools:


Last Updated: January 2025 Version Covered: YOURLS 1.9.2+