A clean, production-ready URL shortener built with Django.
Users authenticate via phone + OTP, create and manage short links, and track clicks with safe redirects.
- OTP authentication (phone-based) with:
- Per-phone rate limiting (default: 5 requests / 5 min)
- Cooldown between resends (default: 120s)
- OTP expiration (default: 300s)
- Short link creation with auto-generated
code- Codes avoid reserved words (e.g.
admin,login,api, …) - Links expire automatically after 1 year (default)
- Codes avoid reserved words (e.g.
- Safe redirect with atomic click counter
- URL validation:
- Only
http://andhttps:// - Rejects
javascript:/data:** schemes - Rejects links to your own domain
- Only
- User dashboard to manage your links
- Bootstrap 5 UI
- Django apps:
accounts,shortener - Auth: phone + OTP (via Kavenegar, with dev fallback to console)
- Short link model: unique
code(A–Z, a–z, 0–9), reserved list protected - Click tracking:
F()-based atomic increment - Templates: Bootstrap 5
git clone https://github.com/amir-pashayi/django-url-shortener.git
cd django-url-shortener/urlshortnerpython -m venv .venv
# Windows:
.venv\Scripts\activate
# macOS/Linux:
source .venv/bin/activatepip install -r ../requirements.txtCopy the example and adjust:
cp .env.example .env| Key | Description | Default |
|---|---|---|
SECRET_KEY |
Django secret | required |
ALLOWED_HOSTS |
Allowed hosts | localhost,127.0.0.1 |
KAVENEGAR_API_KEY |
SMS provider key (optional in dev) | empty |
OTP_RESEND_COOLDOWN |
Seconds between resends | 120 |
OTP_ATTEMPT_WINDOW |
Rate-limit window (seconds) | 300 |
OTP_REQUESTS_PER_WINDOW |
Max requests per window | 5 |
OTP_EXPIRATION_TIME |
Seconds before OTP expires | 300 |
python manage.py migrate
python manage.py runserverOpen: http://127.0.0.1:8000
- User enters phone → server generates a 6-digit OTP and stores it.
- Rate-limiting: max
OTP_REQUESTS_PER_WINDOWper phone duringOTP_ATTEMPT_WINDOW. - Resend cooldown:
OTP_RESEND_COOLDOWNseconds. - OTP is sent via Kavenegar (if API key present), or logged to console in dev mode.
- On verify: OTP must be valid and not older than
OTP_EXPIRATION_TIME.
- Accepts http/https only.
- Rejects
javascript:anddata:schemes. - Rejects links pointing to your own domain (from
ALLOWED_HOSTS). - On redirect: increments
click_countatomically and checks expiry (returns 404 if expired).
django-url-shortener/
├─ urlshortner/
│ ├─ accounts/ # phone auth, otp
│ ├─ shortener/ # short link logic
│ ├─ templates/ # bootstrap ui
│ ├─ manage.py
├─ requirements.txt
└─ README.md
On development, with DEBUG=True or no KAVENEGAR_API_KEY, OTPs are printed to console instead of calling the provider:
[OTP][DEV] to=09123456789 code=123456
- Always set
DEBUG=Falsein production. - Configure
ALLOWED_HOSTS. - Consider enabling HSTS, SSL redirect, and secure cookies.
- Keep your
SECRET_KEYsecret; never commit.env.
| Path | View | Notes |
|---|---|---|
/ |
Home | Landing page |
/create/ |
CreateShortLinkView | Make a short link |
/link/<code>/ |
LinkDetailView | Details page |
/<code>/ |
GoView | Redirect to target URL |
/accounts/login/ |
LoginView | Phone login (OTP) |
/accounts/verify/ |
LoginVerifyView | Submit OTP |
/accounts/dashboard/ |
DashboardView | Manage your links |
Developed with ❤️ and ☕ by Amir Pashayi