Package: django-nginx-secure-links
Django Nginx Secure LinksThis module is a Django extension for usingngx_http_secure_link_module.
It provides private urls with expiration lifetime by implementing described logic of ngx_http_secure_link_module.
The major advantage of the extension is that Django delegates file serving on Nginx layer and does only pre-signed urls generation.RequirementsDjango Nginx Secure Links requires Django 3.2 or later.InstallationInstalling from PyPI is as easy as doing:pipinstalldjango-nginx-secure-linksIf you want to install it from source, grab the git repository from GitHub and run setup.py:gitclonegit://github.com/lighTechLLC/django-nginx-secure-links.gitcddjango-nginx-secure-linkspythonsetup.pyinstallNginx module set upOption 1Install using apt (Ubuntu example):sudoaptinstallnginx-extrasOption 2Build from sources:./configure....--with-http_secure_link_moduleQuick exampleDjango settings set upsettings.py:INSTALLED_APPS=(...'nginx_secure_links',...)MEDIA_ROOT='/var/www/media/'MEDIA_URL='/media/'SECURE_LINK_SECRET_KEY='KfM6aA6M7H'Django < 4.2DEFAULT_FILE_STORAGE='nginx_secure_links.storages.FileStorage'Django >= 4.2STORAGES={"default":{"BACKEND":"nginx_secure_links.storages.FileStorage",},"staticfiles":{"BACKEND":"django.contrib.staticfiles.storage.StaticFilesStorage",},}Create a private file inside yoursettings.MEDIA_ROOT:echo"I'm private text file">/var/www/media/sample.txtLet’s startrunserverand access the file outside of Django file storage. It works and the file is available. There is no access denied, because ofrunservermode:curlhttp://127.0.0.1:8000/media/sample.txtSet up Nginx virtual host filesite.conf:server127.0.0.1;listen80;...location/media/{secure_link$arg_token,$arg_expires;secure_link_md5"$secure_link_expires$uriKfM6aA6M7H";if($secure_link=""){return403;}if($secure_link="0"){return410;}alias/var/www/media/;}...Let’s access the file through Nginx host/port.curlhttp://127.0.0.1/media/sample.txtBecause of Nginx secure link module protection, the file won’t be served
without?token=...&expires=...parameters. Only django users will be able
to access files which urls generated by django storage.Usagemodels.pyclassReport(models.Model):pdf_file=models.FileField(upload_to='reports')views.pydefreport_details(request,report_id)instance=Report.objects.get(id=report_id)returnJsonResponse({'url':instance.pdf_file.url})json response{"url":"/media/reports/29974.pdf?expires=1599214310&token=ErLcMm96-4h2qsuj2Avo-w"}That’s it, all uploaded media files through Django will be pre-signed.
If you work locally and do not want to install Nginx, let’s skip it for
local development- django will generate pre-signed urls, but all files will be
available because ofrunservercommand serves files and does not provide
3rd-partynginx-secure-linkmodule functionality.SettingsSECURE_LINK_SECRET_KEYYour specific secret string which Nginx is going to use insecure_link_md5directive.SECURE_LINK_TOKEN_FIELD(optional, default:token)Your custom name of the hash GET-parameter (?token=xyz)SECURE_LINK_EXPIRES_FIELD(optional, default:expires)Your custom name of expiration timestamp GET-parameter  (?expires=1599215210)SECURE_LINK_EXPIRATION_SECONDS(optional, default:86400- 1 day)Your custom value of expiration seconds. Any pre-signed link will be expired afterSECURE_LINK_EXPIRATION_SECONDS.SECURE_LINK_PRIVATE_PREFIXES(optional, default:[])List of private paths withoutMEDIA_URLprefix. Just leave it empty for making all media urls private. Example:MEDIA_URL='/media/'SECURE_LINK_PRIVATE_PREFIXES=['documents/','reports/',]In such case all/media/documents/and/media/reports/urls will be private and pre-signed by using token and expiration time. If any of existing prefixes on the project are not listed inSECURE_LINK_PRIVATE_PREFIXES, so the url will be public.SECURE_LINK_PUBLIC_PREFIXES(optional, default:[])List of private paths withoutMEDIA_URLprefix. Example:MEDIA_URL='/media/'SECURE_LINK_PUBLIC_PREFIXES=['avatars/','shared/',]In such case only/media/avatars/and/media/shared/urls will be public and generated without pre-signed urls. All other urls, will be private and pre-signed by using token and expiration time.ImportantIf you want to keep all media files privately,SECURE_LINK_PRIVATE_PREFIXESandSECURE_LINK_PUBLIC_PREFIXESshould be[].Custom storage for non-media filesExample 1:We are going to use our own server directory and url prefix instead
ofsettings.MEDIA_ROOT/settings.MEDIA_URL.
The example is going to use all defaultsettings.SECURE_LINK_*fromnginx_secure_links.storagesimportFileStoragestorage=FileStorage(location='/var/www/personal_data/',base_url='/personal/')storage.url('profile.pdf')Example 2: We are going to use custom storage with all overridden settings.fromnginx_secure_links.storagesimportFileStoragestorage=FileStorage(location='/var/www/personal_data/',base_url='/personal/'nginx_secret_key='91rdywY7d4494X',expires_field_name='expires_timestamp',token_field_name='hash',private_prefixes=[],public_prefixes=[],expires_seconds=60*60,# 60min)# all privatestorage.url('profile.pdf')# /personal/profile.pdf?hash=mlkiuhbhu83d&expires_timestamp=2147483647Using ItGenerate pre-signed url by passing public url:python manage.py secure_links_gen_signed /media/reports/sample.pdfGenerates a sample of Nginx location basing on the settings:python manage.py secure_links_nginx_locationFound a Bug?Issues are tracked via GitHub issues at theproject issue page.