Python project 13
Python URL Shortener Project
Build a local URL-shortening web application with Flask and SQLite. Users can submit a complete web address, receive a short code, and follow that code through a database-backed redirect.
Learn Python with mentor-guided projectsView all project ideas
What you will build
The app runs on your computer, creates its database automatically, validates HTTP and HTTPS URLs, generates hard-to-guess short codes, stores parameterized SQL values, and returns a normal 302 redirect.
Skills practised
- Flask routes and forms
- SQLite persistence
- URL parsing and validation
- Secure random tokens
Requirements
- Python 3.10 or later
- Flask:
pip install flask - A terminal and browser
- About 45 minutes to build
Full Python code
Save this code as url_shortener.py, then follow the run instructions below.
"""A local Flask and SQLite URL shortener for learning purposes."""
from __future__ import annotations
import secrets
import sqlite3
import string
from contextlib import closing
from pathlib import Path
from urllib.parse import urlsplit
from flask import Flask, abort, redirect, render_template_string, request, url_for
DATABASE = Path(__file__).with_name("short_urls.db")
ALPHABET = string.ascii_letters + string.digits
app = Flask(__name__)
PAGE = """
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width">
<title>Local URL Shortener</title></head>
<body>
<main>
<h1>Local URL Shortener</h1>
<form method="post">
<label>Long URL <input name="long_url" type="url" required placeholder="https://example.com/page"></label>
<button type="submit">Shorten</button>
</form>
{% if error %}<p role="alert">{{ error }}</p>{% endif %}
{% if short_url %}<p>Short URL: <a href="{{ short_url }}">{{ short_url }}</a></p>{% endif %}
</main>
</body>
</html>
"""
def connect() -> sqlite3.Connection:
connection = sqlite3.connect(DATABASE)
connection.row_factory = sqlite3.Row
return connection
def setup_database() -> None:
with closing(connect()) as connection:
connection.execute(
"CREATE TABLE IF NOT EXISTS links "
"(code TEXT PRIMARY KEY, long_url TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP)"
)
connection.commit()
def valid_public_url(value: str) -> bool:
"""Apply basic syntax checks; this is not a reputation or safety check."""
try:
parsed = urlsplit(value)
return parsed.scheme in {"http", "https"} and bool(parsed.netloc) and not parsed.username
except ValueError:
return False
def new_code(length: int = 7) -> str:
return "".join(secrets.choice(ALPHABET) for _ in range(length))
def store_url(long_url: str) -> str:
with closing(connect()) as connection:
for _ in range(10):
code = new_code()
try:
connection.execute("INSERT INTO links (code, long_url) VALUES (?, ?)", (code, long_url))
connection.commit()
return code
except sqlite3.IntegrityError:
continue
raise RuntimeError("Could not create a unique short code. Try again.")
@app.route("/", methods=["GET", "POST"])
def home():
error = None
short_url = None
if request.method == "POST":
long_url = request.form.get("long_url", "").strip()
if not valid_public_url(long_url):
error = "Enter a complete http:// or https:// URL without embedded credentials."
else:
short_url = url_for("follow", code=store_url(long_url), _external=True)
return render_template_string(PAGE, error=error, short_url=short_url)
@app.get("/<code>")
def follow(code: str):
with closing(connect()) as connection:
row = connection.execute("SELECT long_url FROM links WHERE code = ?", (code,)).fetchone()
if row is None:
abort(404)
return redirect(row["long_url"], code=302)
def main() -> None:
setup_database()
app.run(host="127.0.0.1", port=5000, debug=False)
if __name__ == "__main__":
main()
How the project works
store_url() tries a randomly generated seven-character code and relies on the database primary key to reject the extremely unlikely duplicate. SQL parameters keep submitted values separate from the query text.
The dynamic route looks up the code and returns an HTTP 302 redirect. This learning app intentionally runs only on 127.0.0.1. A public shortener would also need abuse prevention, unsafe-domain controls, rate limiting, moderation, analytics privacy, HTTPS, and production hosting.
Run the project
- Install Flask with
python -m pip install flask. - Run
python url_shortener.py. - Open
http://127.0.0.1:5000/. - Submit a complete URL and test the generated short link.
Accuracy and safety notes
- Local database: links are stored in
short_urls.db. - Validation: syntax checks do not prove that a destination is safe.
- Development server: do not expose Flask’s built-in server directly to the internet.
Ways to extend it
Add click counts, expiry dates, custom aliases, duplicate-URL reuse, an admin view, and production-grade abuse controls.
Continue learning Python
Build the next project, return to the Softenant project library, or explore the Python programming course in Vizag for structured lessons, mentor feedback, and portfolio practice.