Just spent ages trying to get this working, main thing is that when serving static elements in production (DEBUG = False) django will not let you serve them without running --insecure flag. Hence, why we need white noise.
Note this solution works with using function (instead of build) which allows you to increase max_timeout options.
This is how I got it running on vercel, main thing was outputting the static files (after collectstatic) to the correct directory inside the vercel.json and changing STATIC_URL accordingly if DEBUG was True or False (Handled using .env file)
Full working example: https://github.com/leele2/timesheet-xlsx-to-ics
Settings.py
from pathlib import Path
from os import getenv
from dotenv import load_dotenv
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Load environment variables from .env
load_dotenv(BASE_DIR / '.env')
DEBUG = getenv('DEBUG', 'False') == 'True'
INSTALLED_APPS = [
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
"whitenoise.middleware.WhiteNoiseMiddleware",
]
# Static files (CSS, JavaScript, images)
if DEBUG:
STATIC_URL = '/dev_static/' # Development URL for static files
else:
STATIC_URL = '/static/' # Production URL for static files
# Add your local static file directories here
STATICFILES_DIRS = [
BASE_DIR / "dev_static", # This allows Django to look for static files in the 'dev_static' directory
]
# Directory where static files will be stored after running collectstatic
STATIC_ROOT = BASE_DIR / 'static'
# Optional: Use manifest storage for cache busting (adding hash to filenames)
STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage"
urls.py
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('', include('some_views.urls'))
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
vercel.json
{
"outputDirectory": "static/",
"buildCommand": "python3 manage.py collectstatic --noinput",
"functions": {
"api/wsgi.py": {
"maxDuration": 15
}
},
"routes": [
{
"src": "/(.*)",
"dest": "api/wsgi.py"
}
]
}