Nginx giving 403 code for django static files - django

I'm trying to deploy my django project. In my django settings, static file settings are like this:
STATIC_URL = '/static/'
STATIC_ROOT = '/root/www/static'
so my static files are in /root/www/static directory, i can see them in the server. My nginx server block is like this:
server{
listen 80;
location /static/ {
root /root/www;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
when i browse my application with browser, i'm getting
403 - Forbidden
error for static files.

You shouldn't put the static files under /root: that's for files accessible via the root user only.

The problem seems to be with user permissions. You need to change the path that is accessible by the user for which the app is deployed.
The location should look something like this:
location /static {
alias /home/ubuntu/srv/webapps/[app_name]/static;
}
Try this tutorial for more help.

Related

Nginx responds 404 not found on Django media URL in preprod, dev ok

I have a quite standard Django application with a Vuejs frontend.
I have different environments (preprod/dev) in which I have file upload/download features.
For files, everything works fine because they are returned through standard API views in attachment (Content-Disposition: attachment). When it comes to images though, like profile pictures, there is a problem.
In development (DEBUG=True), I have this :
from django.conf import settings
from django.conf.urls.static import static
from django.urls import include, path
from backend.applications.base.views.authentication_views import LoginAPIView, LogoutAPIView
urlpatterns = [
path("api/login", LoginAPIView.as_view()),
path("api/logout", LogoutAPIView.as_view()),
path("api/base/", include("backend.applications.base.urls")),
path("api/contact/", include("backend.applications.contact.urls")),
path("api/helpdesk/", include("backend.applications.helpdesk.urls")),
path("api/inventory/", include("backend.applications.inventory.urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # For serving media files when DEBUG=True
and images are correctly served (no nginx in dev mode, just frontend and backend dev servers django's runserver).
My preprod however, is made of a nginx container which serves my built vuejs frontend, and a backend container which contains my Django (DEBUG=False) application (which runs with gunicorn this time, like this : gunicorn backend.wsgi:application --bind 0.0.0.0:8000 --access-logfile="-").
Before trying to serve images, I had this nginx configuration :
http {
client_max_body_size 5M;
upstream backend_api {
server backend:8000;
# 'backend' is the name of the backend service in my docker-compose config
}
server {
listen 80;
include /etc/nginx/mime.types;
root /usr/share/nginx/html;
index index.html;
location = /favicon.ico {
access_log off;
log_not_found off;
}
location /api {
proxy_pass http://backend_api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
}
location / {
try_files $uri $uri/ /index.html;
}
}
}
Then I thought that /media requests should also be passed to the backend and I changed
location /api
into
location ~ ^/(api|media)/ {
My /api URLs are still handled correctly but /media URLs are answered by a 404 :
(trying to load profile pictures of my user(s) in a kanban view).
Also trying directly http://localhost/media/base/users/8/picture.jpg directly in my browser doesn't work :
From here I don't know what to do to solve the issue. If something is missing, mention it and I'll update the post.
Django does not serve static- and media files with runserver, you will need WhiteNoise for that. See http://whitenoise.evans.io/en/stable/
Whitenoise however is not suitable for serving user-uploaded media files. See http://whitenoise.evans.io/en/stable/django.html#serving-media-files
(Optionally, skip whitenoise, and host static/media files through NGINX.)
You really shouldn't be hosting your server with py manage.py runserver. This is not secure. See Why not use "runserver" for production at Django? and https://docs.djangoproject.com/en/dev/ref/django-admin/#runserver
Use something like Gunicorn instead.
See https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/gunicorn/
(Or waitress, the windows alternative)
https://pypi.org/project/django-waitress/
To host static/media files with nginx, paste this into your nginx conf:
location /media {
alias /PATH/TO/DIRECTORY; #Absolute path.
}
And in your settings.py, set the media root to that same directory.

Configure Nginx to serve Angular front end, Django Rest Framework backend on same server?

My site's frontend is built with Angular 7, and the back end is built with Django Rest Framework. It's all running via docker-compose, and I'm trying to serve the entire site through NGINX.
The basic premise of my configuration is this:
If the request includes a url with /api/ or /admin/ in it, allow gunicorn to handle the request.
If the url is anything else, send it to the root index.html so that Angular can handle the routing.
The root url / should serve my pre-compiled javascript via the index.html.
With the following Nginx config, I can visit all of the Angular routes. I can visit the admin page and my browsable API. However, when I go to /admin or the browsable API, all of the static files return a 404 error.
Do you see the issue? Thanks!
# urls.py
urlpatterns = [
re_path(r'^favicon\.ico$', favicon_view),
path('api/v1/', include('api.urls'), name='api'),
path('admin/', admin.site.urls),
]
# nginx.conf
upstream my_app {
server django:8000;
}
server {
listen 80;
location /staticfiles/ {
alias /usr/src/app/staticfiles/;
}
location ~ (api|admin) {
proxy_pass http://my_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location / {
root /usr/src/app/staticfiles/;
try_files $uri $uri/ /index.html;
}
}
Your configs are saying that everything available from /api/ or /admin/ are provided by the proxy to my_app. So, you may either ensure that the static files used by those endpoints are available through Django Rest Framework, OR tell NGINX to always try the static files first, then fall back to the my_app proxy.
The first option will involve setting STATIC_ROOT, STATIC_URL, STATICFILES_STORAGE, etc per the Django documentation on static files (link goes to current dev version).
The other option involves collecting the assets used by /api/ and /admin/ in the same location as your Angular assets, and altering your NGINX configuration to look something like this:
# nginx.conf
upstream my_app {
server django:8000;
}
server {
listen 80;
location /staticfiles/ {
alias /usr/src/app/staticfiles/;
}
location / {
root /usr/src/app/staticfiles/;
try_files $uri $uri/ index.html;
location /drf {
proxy_pass http://my_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
}
This basically says try to look up everything in your Angular staticfiles directory, if it's not found, try looking it up in your DRF application.

Django + Nginx - serve private files using X-Accel-Redirect

I'm new to this, but my understanding of how this works is as follows:
Request private file from nginx
nginx forwards this to django
django does its thing, and returns a response containing the X-Accel-Redirect header
nginx sees this header, and serves the file.
I've set up my nginx conf like so:
upstream foo_app_server {
server unix:/home/project_dir/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name foo.com;
client_max_body_size 4G;
location /static/ {
alias /home/projec_dir/static/;
}
location /media/private/ {
internal;
alias /home/project_dir/media/private/;
}
location /media/ {
alias /home/project_dir/media/public/;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://foo_app_server;
}
}
Now, django is successfully receiving the request for files in media/private, and it is successfully attaching a X-Accel-Redirect header to the response.
I've tried the following, for the content of the header, when the requested path is of the form media/private/some/path/to/file.pdf
X-Accel-Redirect:
media/private/some/path/to/file.pdf
private/some/path/to/file.pdf
path/to/file.pdf
All of them gave me 404 errors. The file definitely does exist.
I think this is just path issue, of a mismatch between the path in the header, and the location and the alias in the nginx conf, but I've tried for awhile and I can't get the right combination.
Could somebody who has done this before suggest what exactly nginx is looking for?

Proper way to serve updated files without user refreshing on nginx

This is a pretty core question. I have a django app that I run in an nginx/gunicorn configuration. Nginx is responsible for proxying to gunicorn, but it also serves the static files of the project directly.
The issue is, when I update the static files, the browser doesn't load the latest version. Sometimes a refresh fixes it, but sometimes it requires clearing the cache. (I should mention I'm using require.js, which does not help).
To alleviate this issue I'm doing this trick:
VERSION = '2.03'
STATIC_URL = '/static/' + VERSION + '/'
STATIC_ROOT = BASE_DIR + '/static/' + VERSION + '/'
This way, when I change static files, I simply bump the version. But for various reasons, I need to stop doing this. Is there a way to configure nginx to better serve updated static files when they are available?
I'm just not sure if the browser or nginx is to blame.
Update: Here's my nginx configuration with the comments removed:
upstream mycoolsite_server {
server unix:/webapps/mycoolsite/run/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name www.mycoolsite.com;
return 301 http://mycoolsite.com$request_uri;
}
server {
listen 80;
server_name mycoolsite.com 42.42.42.42;
client_max_body_size 4G;
access_log /webapps/mycoolsite/logs/nginx-access.log;
error_log /webapps/mycoolsite/logs/nginx-error.log;
location /static/ {
alias /webapps/mycoolsite/site/static/;
}
location /media/ {
alias /webapps/mycoolsite/site/media/;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://mycoolsite_server;
break;
}
}
# Error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /webapps/mycoolsite/site/static/;
}
}
You can set expire for the static files in nginx configuration. For example if you don't want to cache css, js and png files. you can define sth. like:
location ~* \.(css|js|png)$ {
expires 0;
break;
}
so they won't been cached. But i assume you are making ./manage.py collectstatic after you change your static files on your repository or dev. environment.

Django static files in live server using nginx

I have a live server running django, the address is http://179.188.3.54/ . As you can see, the app works but looks like the static files arent working. Also if I click in any other link, doesnt work.
This website is running without any problems in development version. Im not sure what I should do to fix this problem.
Here is my nginx config file and my settings.py
STATIC_URL = '/static/'
STATIC_ROOT = '/cinegloria/cinegloria/cinegloria/static/'
PS: I tried to run collectstatic ;)
server {
root /usr/share/nginx/www;
index index.html index.htm;
access_log /var/log/nginx/domain-access.log;
server_name 0.0.0.0;
location / {
try_files $uri $uri/ /index.html;
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_connect_timeout 10;
proxy_read_timeout 10;
proxy_pass http://0.0.0.0:8000/;
}
}
Any ideas or sample code will be appreciated!
Add the static serving to the nginx conf before the / pattern:
location /static {
alias /cinegloria/cinegloria/cinegloria/static/;
}
location / {
...
}
Or set the STATIC_ROOT to the directory under the www root:
STATIC_ROOT = '/usr/share/nginx/www/static'
Or add the symlink from www root to you static dir:
$ ln -s /cinegloria/cinegloria/cinegloria/static /usr/share/nginx/www/static
Add another nginx directive for the static files. Static files should be served by nginx, not the Django server.
location /static/ {
alias /cinegloria/cinegloria/cinegloria/static/;
}
If that still doesn't work, you may need to add the mime type directive. I had to do that yesterday, because for some reason nginx wasn't serving the correct mime type when I used an alias.
As a helpful pointer, whenever you run into problems like this, take a look at your nginx error log and paste the last few lines for debugging. It is located at /var/log/nginx/error.log or a similar path.