Open-source URL shortener with QR code generation, user system, and admin panel.
- URL shortening with random Base62 codes
- Custom aliases and optional expiration
- Automatic QR code generation (PNG)
- User system (sign up, sign in, link management, password change)
- Protected admin panel (unified user system with
is_adminflag) - Admin: full user and link management, search by owner
- Internationalization (EN, PT-BR) — extensible
- Dark / Light mode
- Full REST API
- Rate limiting by IP
| Layer | Technology |
|---|---|
| Backend | PHP 8+ pure (no frameworks) |
| Database | MariaDB 11 |
| Frontend | HTML + CSS + JS (vanilla) |
| QR Code | endroid/qr-code |
| Infra | Docker + Docker Compose |
openshortener/
├── backend/
│ ├── .env.example # Environment variables template
│ ├── composer.json
│ ├── config/
│ │ └── app.php
│ ├── public/
│ │ ├── .htaccess
│ │ └── index.php # Entry point
│ ├── routes/
│ │ └── api.php
│ └── src/
│ ├── Controllers/
│ ├── Core/ # Database, Env, Router, Session, Request, Response
│ ├── Middleware/ # Auth, RateLimit
│ ├── Models/ # User, Url
│ └── Services/ # Auth, Url, QrCode, Base62
├── frontend/
│ ├── index.html
│ ├── admin.html
│ ├── termos.html
│ ├── styles.css
│ ├── app.js
│ ├── admin.js
│ └── i18n/
│ ├── en.json
│ └── pt-BR.json
├── database/
│ └── schema.sql
├── docker/
│ ├── backend.Dockerfile
│ ├── frontend.Dockerfile
│ └── nginx.conf
├── docker-compose.yml
├── LICENSE
└── README.md
- Docker and Docker Compose
git clone https://github.com/thiagotraue/openshortener.git
cd openshortener
# Start all services (database, backend, frontend)
docker compose up --build -dAllow ~15 seconds for MariaDB to initialize. Services will be available at:
| Service | URL |
|---|---|
| Frontend | http://localhost:8080 |
| API | http://localhost:8080/api/v1 |
| Backend | http://localhost:8000 |
| MariaDB | localhost:3306 |
docker compose downTo also remove database data:
docker compose down -vInstall MariaDB 11+ and run:
mysql -u root -p < database/schema.sqlThis creates the openshortener database, all tables, and the default admin.
cd backend
# Copy and edit the environment file
cp .env.example .env
# Adjust DB_HOST, DB_USER, DB_PASS for your environment
# Install PHP dependencies
composer install
# Start PHP built-in server (development)
php -S localhost:8000 -t publicServe the frontend/ folder with any HTTP server. Example with Python:
cd frontend
python3 -m http.server 8080Or with PHP's built-in server:
php -S localhost:8080 -t frontendNote: In production, configure a reverse proxy (nginx/Apache) to route
/api/*and/{code}to the PHP backend.
| Variable | Description | Default |
|---|---|---|
DB_HOST |
MariaDB host | 127.0.0.1 |
DB_PORT |
MariaDB port | 3306 |
DB_NAME |
Database name | openshortener |
DB_USER |
Database user | root |
DB_PASS |
Database password | (empty) |
APP_BASE_URL |
Base URL for short links | https://short.opensource.dev.br |
CORS_ORIGIN |
Allowed CORS origin | * |
Base: /api/v1
| Method | Endpoint | Description |
|---|---|---|
| POST | /register |
Sign up |
| POST | /login |
Sign in |
| POST | /logout |
Sign out |
| PUT | /password |
Change password |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /shorten |
Optional | Shorten a URL |
| GET | /my-urls |
Yes | List own links |
| PUT | /urls/{id} |
Yes | Edit a link |
| DELETE | /urls/{id} |
Yes | Delete a link |
| Method | Endpoint | Description |
|---|---|---|
| GET | /{code} |
302 redirect |
| GET | /qr/{code} |
QR Code (PNG image) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /admin/login |
Admin sign in (email) |
| GET | /admin/me |
Admin session check |
| GET | /admin/users |
List all users |
| DELETE | /admin/users/{id} |
Delete user |
| PUT | /admin/users/{id} |
Block/activate user |
| GET | /admin/users/{id}/urls |
List user's URLs |
| GET | /admin/urls |
List all URLs |
| DELETE | /admin/urls/{id} |
Delete URL |
The schema.sql automatically creates an admin user:
| Field | Value |
|---|---|
admin@admin.com |
|
| Password | admin123 |
Admin users have is_admin = 1 in the users table. There is no separate admin table — admin is a flag on the unified user system.
IMPORTANT: Change the admin password immediately in production.
Access the admin panel at /admin.
The system automatically detects the browser language. Users can manually switch via the flag button in the header.
- 🇺🇸 English (default)
- 🇧🇷 Português (Brasil)
- Copy
frontend/i18n/en.jsontofrontend/i18n/{code}.json(e.g.es.json) - Translate all keys
- Add the code to the
SUPPORTED_LANGSarray infrontend/app.js:
const SUPPORTED_LANGS = ['en', 'pt-BR', 'es'];- Passwords hashed with
PASSWORD_ARGON2ID - SQL injection protection (PDO prepared statements)
- XSS protection (
escapeHtmlon frontend,htmlspecialcharson backend) - Sessions:
HTTPOnly,SameSite: Lax, configurableSecureflag - Session regeneration on login
- Rate limiting by IP/endpoint
- Unified admin system:
is_adminflag verified from DB on every admin request (real-time revocation) - Admin self-protection: cannot delete or block own account
- Admin panel at
/admin(clean URL)
This project uses a proprietary license that requires:
- Attribution to the original author (Thiago Traue) in all derivatives
- Notification to the author about public forks within 30 days
See the LICENSE file for full details.