Skip to main content

Overview

The Short-URL API is configured entirely through environment variables. This approach ensures secure credential management and flexible deployment across different environments.
All environment variables should be defined in a .env file for local development or configured in your hosting platform’s environment settings for production.

Required Environment Variables

These variables must be set for the API to function correctly.
MONGO_URI
string
required
MongoDB connection string for database accessSource: main.py:16Format:
  • Local: mongodb://localhost:27017
  • Atlas: mongodb+srv://username:[email protected]/?retryWrites=true&w=majority
  • Replica Set: mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=myReplicaSet
Example:
MONGO_URI=mongodb://localhost:27017
Never commit connection strings with credentials to version control. Use environment-specific configuration.
SECRET_KEY
string
required
Secret key for JWT token signing and verificationSource: auth_utils.py:6Requirements:
  • Minimum 32 characters recommended
  • Use cryptographically secure random string
  • Keep secret and rotate periodically
Generate a secure key:
# Using Python
python -c "import secrets; print(secrets.token_urlsafe(32))"

# Using OpenSSL
openssl rand -hex 32
Example:
SECRET_KEY=your-super-secret-key-min-32-characters-long-abc123xyz
Changing the SECRET_KEY will invalidate all existing JWT tokens. Users will need to re-authenticate.
SURL_BASE
string
required
Base URL for generated short linksSource: main.py:18Usage: Used in the /create endpoint response to construct the full short URLFormat: https://your-domain.com (no trailing slash)Example:
SURL_BASE=https://short.example.com
Response format:
{
  "message": "URL created",
  "short_url": "https://short.example.com/abc123"
}
Ensure SURL_BASE matches your actual deployment domain. Mismatched URLs will confuse users.

Optional Environment Variables

These variables have default values but can be customized for your deployment.
ALLOWED_ORIGINS
string
default:"*"
Comma-separated list of allowed CORS originsSource: main.py:37Default: * (allow all origins)Format: Comma-separated URLs without spacesDevelopment:
ALLOWED_ORIGINS=*
Production:
ALLOWED_ORIGINS=https://example.com,https://app.example.com,https://admin.example.com
Never use * in production. Always specify exact origins to prevent unauthorized cross-origin requests.
TOKEN_EXPIRE
integer
default:"5"
JWT token expiration time in minutesSource: auth_utils.py:8Default: 5 minutesType: Integer (minutes)Example:
# 30 minute expiration
TOKEN_EXPIRE=30

# 2 hour expiration
TOKEN_EXPIRE=120

# 24 hour expiration
TOKEN_EXPIRE=1440
Security considerations:
  • Shorter expiration = more secure but requires frequent re-authentication
  • Longer expiration = better UX but higher security risk if token is compromised
  • Use /refresh_token endpoint to obtain new tokens without re-entering password
For production APIs, 15-60 minutes is a good balance between security and user experience.
URL_BLACKLIST
string
default:""
Comma-separated list of blacklisted URL patterns to prevent malicious short linksSource: main.py:17Default: Empty (no blacklist)Format: Comma-separated domain fragmentsExample:
URL_BLACKLIST=malware.com,phishing.net,spam.org,example.scam
Behavior: If any blacklist pattern is found in the submitted URL, creation fails with:
{
  "detail": "URL Blacklisted"
}
Implementation: See main.py:78 and helper.py:23-27
The blacklist uses substring matching. Adding “badsite.com” will block “https://badsite.com” and “https://subdomain.badsite.com”.

Environment Variables by Component

Authentication (auth_utils.py)

VariableTypeDefaultRequiredDescription
SECRET_KEYstring-YesJWT signing secret
TOKEN_EXPIREinteger5NoToken expiration (minutes)
Algorithm: Hardcoded to HS256 (see auth_utils.py:7) Password Hashing: Uses pbkdf2_sha256 scheme (see auth_utils.py:10)

Application (main.py)

VariableTypeDefaultRequiredDescription
MONGO_URIstring-YesMongoDB connection string
SURL_BASEstring-YesBase URL for short links
ALLOWED_ORIGINSstring*NoCORS allowed origins
URL_BLACKLISTstringNoBlacklisted URL patterns

Example .env Files

Development Environment

.env.development
# Database
MONGO_URI=mongodb://localhost:27017

# Authentication
SECRET_KEY=dev-secret-key-change-in-production-min-32-chars
TOKEN_EXPIRE=60

# Application
SURL_BASE=http://localhost:8000
ALLOWED_ORIGINS=*
URL_BLACKLIST=
Development keys should never be used in production. Always generate new secure keys for each environment.

Production Environment

.env.production
# Database
MONGO_URI=mongodb+srv://prod_user:[email protected]/?retryWrites=true&w=majority

# Authentication
SECRET_KEY=CRYPTOGRAPHICALLY_SECURE_RANDOM_STRING_MIN_32_CHARACTERS_LONG
TOKEN_EXPIRE=15

# Application
SURL_BASE=https://short.example.com
ALLOWED_ORIGINS=https://example.com,https://app.example.com
URL_BLACKLIST=malware.com,phishing.net,spam.org,scam.xyz
Use a secret management service like AWS Secrets Manager, Azure Key Vault, or HashiCorp Vault for production secrets.

Testing Environment

.env.test
# Database
MONGO_URI=mongodb://localhost:27017

# Authentication
SECRET_KEY=test-secret-key-for-automated-testing-only
TOKEN_EXPIRE=5

# Application
SURL_BASE=http://localhost:8000
ALLOWED_ORIGINS=*
URL_BLACKLIST=test-blocked.com

Loading Environment Variables

Local Development

Use python-dotenv to load variables from .env file:
pip install python-dotenv
Add to your startup script:
startup.py
from dotenv import load_dotenv
import os

load_dotenv()  # Load .env file

# Now environment variables are available
print(os.getenv("MONGO_URI"))

Docker

Pass environment variables in docker-compose.yml:
docker-compose.yml
version: '3.8'
services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - MONGO_URI=mongodb://mongo:27017
      - SECRET_KEY=${SECRET_KEY}
      - SURL_BASE=http://localhost:8000
      - ALLOWED_ORIGINS=*
      - TOKEN_EXPIRE=30
    env_file:
      - .env

Cloud Platforms

Set environment variables in the EB console or using .ebextensions:
eb setenv MONGO_URI="mongodb+srv://..." SECRET_KEY="..." SURL_BASE="https://api.example.com"
Use Heroku CLI or dashboard:
heroku config:set MONGO_URI="mongodb+srv://..."
heroku config:set SECRET_KEY="..."
heroku config:set SURL_BASE="https://yourapp.herokuapp.com"
Configure in gcloud command or Cloud Console:
gcloud run deploy shorturl-api \
  --set-env-vars MONGO_URI="mongodb+srv://...",SECRET_KEY="...",SURL_BASE="https://..."
Set in Application Settings:
az webapp config appsettings set --resource-group myRG --name myApp \
  --settings MONGO_URI="mongodb+srv://..." SECRET_KEY="..." SURL_BASE="https://..."
Use ConfigMap and Secrets:
apiVersion: v1
kind: Secret
metadata:
  name: shorturl-secrets
type: Opaque
stringData:
  MONGO_URI: "mongodb+srv://..."
  SECRET_KEY: "..."
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: shorturl-config
data:
  SURL_BASE: "https://short.example.com"
  ALLOWED_ORIGINS: "https://example.com"
  TOKEN_EXPIRE: "15"

Security Best Practices

1. Never Commit Secrets

Add .env to .gitignore:
.gitignore
.env
.env.local
.env.*.local
.env.production

2. Use Different Keys Per Environment

  • Development: Short, easy-to-remember keys
  • Staging: Production-like secure keys
  • Production: Cryptographically secure random keys

3. Rotate Secrets Regularly

  • Rotate SECRET_KEY every 90 days
  • Update MONGO_URI credentials quarterly
  • Monitor for unauthorized access

4. Validate Environment Variables

Add startup validation:
import os
import sys

required_vars = ["MONGO_URI", "SECRET_KEY", "SURL_BASE"]
missing = [var for var in required_vars if not os.getenv(var)]

if missing:
    print(f"Error: Missing required environment variables: {', '.join(missing)}")
    sys.exit(1)

5. Use Secret Management Services

For production, use:
  • AWS Secrets Manager: Automatic rotation, audit logs
  • Azure Key Vault: Integration with Azure services
  • Google Secret Manager: Cloud-native secret storage
  • HashiCorp Vault: Self-hosted, enterprise-grade
Secret management services provide encryption at rest, access logs, automatic rotation, and fine-grained access control.

Troubleshooting

Variable Not Loading

import os
print("MONGO_URI:", os.getenv("MONGO_URI"))  # Debug output
If None, check:
  1. .env file exists in correct directory
  2. Variable name spelling (case-sensitive)
  3. python-dotenv is installed and loaded
  4. No spaces around = in .env file

MongoDB Connection Fails

  • Verify MONGO_URI format
  • Check network connectivity
  • Verify credentials
  • Check IP whitelist (MongoDB Atlas)
  • Test connection: python -c "from pymongo import MongoClient; MongoClient(os.getenv('MONGO_URI')).admin.command('ping')"

CORS Errors

  • Ensure ALLOWED_ORIGINS includes the requesting domain
  • Use exact protocol (https:// vs http://)
  • No trailing slashes in origin URLs
  • Check browser console for specific CORS error

Invalid Tokens

  • Verify SECRET_KEY hasn’t changed
  • Check TOKEN_EXPIRE setting
  • Ensure server time is synchronized (JWT uses timestamps)
  • Use /refresh_token to get new token

Build docs developers (and LLMs) love