Develop a URL Shortener with Flask

By Anurag Singh

Updated on Nov 29, 2024

Develop a URL Shortener with Flask

In this tutorial, we'll develop a URL shortener with Flask Python framework.

A URL shortener is a web application that converts long URLs into shorter, more manageable links while allowing redirection to the original address. In this tutorial, we will develop a URL shortener using Flask, Python, and SQLAlchemy. The focus will be on production-grade code with scalability, performance, and security in mind.

Develop a URL Shortener with Flask

Step 1: Setting Up the Project

Start by setting up your Python environment. Create a directory for the project and initialize a virtual environment:

mkdir flask-url-shortener
cd flask-url-shortener
python3 -m venv venv
source venv/bin/activate

Install Flask, SQLAlchemy, and other required dependencies:

pip install flask flask-sqlalchemy flask-migrate validators gunicorn flask-limiter

Step 2: Structuring the Flask Application

Organize the application into a modular structure:

flask-url-shortener/
├── app/
│   ├── __init__.py
│   ├── models.py
│   ├── routes.py
│   ├── utils.py
│   └── config.py
├── migrations/
├── venv/
├── run.py
└── requirements.txt

Create a app directory

mkdir app

Step 3: Writing the Configuration

Create a config.py file to store configurations:

nano config.py

Add following code

import os

class Config:
    SECRET_KEY = os.environ.get("SECRET_KEY", "your_secret_key")
    SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL", "sqlite:///url_shortener.db")
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    FLASK_ENV = 'production'  # Set to production

Note: We have used SQLite for the demonstration purpose. You should use PostgresSQL or MongoDB.

Step 4: Initializing the Flask Application

In __init__.py, initialize the Flask app, SQLAlchemy, and Flask-Migrate:

nano __init__.py

Add following code

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from .config import Config

db = SQLAlchemy()
migrate = Migrate()

def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)
    db.init_app(app)
    migrate.init_app(app, db)
    
    from .routes import main
    app.register_blueprint(main)

    return app

Step 5: Designing the Database Model

In models.py, define the database schema for storing original and shortened URLs:

nano models.py

Add following code

from . import db
import string
import random

class URL(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    original_url = db.Column(db.String(2048), nullable=False)
    short_url = db.Column(db.String(6), unique=True, nullable=False)

    def generate_short_url(self):
        characters = string.ascii_letters + string.digits
        return ''.join(random.choices(characters, k=6))

Step 6: Writing Helper Functions

In utils.py, write utility functions for validation and generating short URLs:

nano utils.py

Add following code

import validators

def validate_url(url):
    return validators.url(url)

Step 7: Developing Routes

In routes.py, define routes for shortening URLs and handling redirections:

nano routes.py

Add following code

from flask import Blueprint, request, jsonify, redirect
from .models import URL, db
from .utils import validate_url
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from flask import current_app

main = Blueprint('main', __name__)

@main.route('/shorten', methods=['POST'])
def shorten_url():
    data = request.get_json()
    original_url = data.get("original_url")

    if not validate_url(original_url):
        return jsonify({"error": "Invalid URL"}), 400

    existing_url = URL.query.filter_by(original_url=original_url).first()
    if existing_url:
        return jsonify({"short_url": existing_url.short_url}), 200

    url = URL(original_url=original_url)
    url.short_url = url.generate_short_url()

    db.session.add(url)
    db.session.commit()

    return jsonify({"short_url": url.short_url}), 201

@main.route('/<short_url>', methods=['GET'])
def redirect_to_original(short_url):
    url = URL.query.filter_by(short_url=short_url).first_or_404()
    return redirect(url.original_url)

limiter = Limiter(
    get_remote_address,
    app=current_app,
    default_limits=["200 per day", "50 per hour"]
)

Step 8: Running the Application

Create run.py to run the application:

nano run.py

Add following code

from app import create_app

app = create_app()

if __name__ == "__main__":
    app.run(debug=True)

Run database migrations:

flask db init
flask db migrate -m "Initial migration"
flask db upgrade

Start the application:

gunicorn -w 4 -b 127.0.0.1:5000 run:app
  • -w 4: Specifies 4 worker processes.
  • -b 127.0.0.1:5000: Binds the application to localhost on port 5000.

Step 9: Testing the Application

  • Use a tool like curl or Postman to send a POST request to /shorten with a JSON payload containing original_url.
  • Access the generated short URL in a browser or via a GET request to test redirection.

Example cURL commands:

# Shorten URL
curl -X POST -H "Content-Type: application/json" \
-d '{"original_url": "https://example.com"}' \
http://127.0.0.1:5000/shorten

output: 

{
  "short_url": "FxFIRp"
}

Redirect using the short URL

curl -v http://127.0.0.1:5000/<short_url>

Step 10: Configure Nginx

Install Nginx:

sudo apt update
sudo apt install nginx

Create an Nginx configuration file for your Flask app. For example:

sudo nano /etc/nginx/sites-available/url_shortener

Add the following content:

server {
    listen 80;
    server_name your-domain.com;

    location / {
        proxy_pass http://127.0.0.1:5000;
        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;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

Note: Replace your-domain.com with your domain name.

Enable the site by creating a symlink:

sudo ln -s /etc/nginx/sites-available/url_shortener /etc/nginx/sites-enabled/

Test the Nginx configuration and reload:

sudo nginx -t
sudo systemctl reload nginx

Step 11: Configure Systemd for Gunicorn

Create a Gunicorn systemd service file:

sudo nano /etc/systemd/system/url_shortener.service

Add the following content:

[Unit]
Description=Gunicorn instance to serve Flask URL Shortener
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/flask-url-shortener
Environment="PATH=/path/to/flask-url-shortener/venv/bin"
ExecStart=/path/to/flask-url-shortener/venv/bin/gunicorn -w 4 -b 127.0.0.1:5000 run:app

[Install]
WantedBy=multi-user.target
Replace /path/to/flask-url-shortener with the absolute path to your project.

Note: Replace /path/to/flask-url-shortener with your path

Start and enable the service:

sudo systemctl start url_shortener
sudo systemctl enable url_shortener

Check the status:

sudo systemctl status url_shortener

Step 12: Secure with HTTPS

Install Certbot for Nginx:

sudo apt install certbot python3-certbot-nginx

Obtain and configure a Let's Encrypt SSL certificate:

sudo certbot --nginx -d your-domain.com

Automatically renew certificates:

sudo systemctl enable certbot.timer

Step 13: Testing the Setup

Access your application using https://your-domain.com.

Test API endpoints with tools like Postman or curl to verify functionality.

Conclusion

We've developed a URL shortener with Flask Python framework. This tutorial covered the development of a production-grade URL shortener using Flask and Python. The application is modular, secure, This production-grade URL shortener is secured with Gunicorn and Nginx, and it’s designed for easy scalability.

With SQLite as the database, it is best suited for small to medium traffic but can be upgraded to a more robust DBMS when needed, and extensible for future features such as analytics or user authentication. With SQLite as the database, it is best suited for small to medium traffic but can be upgraded to a more robust DBMS when needed.

Checkout our dedicated servers India, Instant KVM VPS, and Web Hosting India