I have deployed Django with Gunicorn and NGINX, and it works fine, if the Django app is served on the root url, with this configuration:
server {
listen 80;
location = /favicon.ico { access_log off; log_not_found off; }
location / {
include proxy_params;
proxy_pass http://unix:my_app.sock;
}
}
However when I try to serve the Django app on another URL, it doesn't work.
If I try to access http://domain/my_app/admin/ then Django tells me it cannot find the view.
This is the NGINX config:
server {
listen 80;
location = /favicon.ico { access_log off; log_not_found off; }
location /my_app {
include proxy_params;
proxy_pass http://unix:/var/my_app/app.sock;
}
}
How could I make this work? I was not able to find any solution to specify something like a "BASE_URL" so far.
My comment doesn't show the whole picture. When I've run Django sites on subfolders, I like to use a dynamic config so that you can still access the machine directly (without the proxy) and have a working web-app. This can help a LOT for debugging tricky stuff like this that is hard to reproduce in dev.
If you do not have the ability to pass the header or modify wsgi.py, you can still set FORCE_SCRIPT_NAME in your Django settings.
3 steps:
set up a proxy in front of the webserver that strips the subfolder out of the URL
set the X-Script-Name header so that your Django site generates its urls with /myapp/ infront of them -- make sure you're using the {% url %} tag and reverse, vs. hard-coding!
modify myapp/wsgi.py to read a new header X-Script-Name into the wsgi environment variable SCRIPT_NAME (based on this flask snippet)
Here is an example Nginx config for a proxy that points to a Django site on a subdirectory and also sets X-Script-Name (steps 1 and 2), note that this doesn't use a unix socket, so it's a little different from the OP's question. Would welcome an edit:
nginx.conf
location /my_app {
proxy_pass https://mywebapp.com/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Script-Name /my_app;
proxy_cookie_path / /my_app;
}
And to read X-Script-Name:
myapp/wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings")
_application = get_wsgi_application()
def application(environ, start_response):
# http://flask.pocoo.org/snippets/35/
script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
if script_name:
environ['SCRIPT_NAME'] = script_name
path_info = environ['PATH_INFO']
if path_info.startswith(script_name):
environ['PATH_INFO'] = path_info[len(script_name):]
scheme = environ.get('HTTP_X_SCHEME', '')
if scheme:
environ['wsgi.url_scheme'] = scheme
return _application(environ, start_response)
#andyC thank you for your answer.
With up-to-date Django (4.1.4) it worked with the following settings. I made the following setting in nginx for django, which is behind the api gateway.
settings.py
USE_X_FORWARDED_HOST = True
FORCE_SCRIPT_NAME = '/content/'
SESSION_COOKIE_PATH = '/content/'
LOGIN_URL = "login/"
LOGIN_REDIRECT_URL = '/content/'
LOGOUT_REDIRECT_URL = '/content/'
STATIC_URL = '/content/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
# Media files
MEDIA_URL = '/content/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles/")
apiGateway -> nginx.conf
server_name api.mydomain.com;
location /auth/ {
proxy_pass http://django_app:8002/;
proxy_set_header Host $http_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;
}
if you run django with gunicorn and use uvicorn as asgi worker, like
gunicorn example:asgi.app -w 4 -k uvicorn.workers.UvicornWorker
you need to add root_path='/api/' to django asgi scope, unfortunately, gunicorn can't pass this thought arguments but you can write a custom worker and run gunicorn with it
class MyUvicornWorker(UvicornWorker):
CONFIG_KWARGS = {"loop": "auto", "http": "auto", "root_path": "/api/"}
and then run gunicorn with custom worker
gunicorn example:asgi.app -w 4 -k example.workers.MyUvicornWorker
Here is what I solve this problem. I can run multiple django running on same server/domain with subdirectory.
project/settings.py
MY_PROJECT = env('MY_PROJECT', '') # example; '/dj'
if MY_PROJECT:
USE_X_FORWARDED_HOST = True
FORCE_SCRIPT_NAME = MY_PROJECT + "/"
SESSION_COOKIE_PATH = MY_PROJECT + "/"
LOGIN_URL = "login/"
LOGIN_REDIRECT_URL = MY_PROJECT + "/"
LOGOUT_REDIRECT_URL = MY_PROJECT + "/"
NGINX configuration
location /dj/ {
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8000/;
}
Be aware that location url should end with "/"
Related
I've been stuck for days just to find a way to fix this problem.
Why my Nginx can't connect to WebSocket? and always get these errors on the console:
WebSocket connection to '<URL>' failed: WebSocket is closed before the connection is established.
or
WebSocket connection to 'wss://domain.com/virtualexpo/' failed: Error during WebSocket handshake: Unexpected response code: 404
Here's my Nginx setup looks like:
upstream projectname {
server localhost:9001;
}
upstream uvsock {
server 127.0.0.1:6379;
# server unix://var/run/supervisor.sock;
}
server {
server_name domain.com www.domain.com;
sendfile on;
charset utf-8;
client_max_body_size 20M;
client_body_buffer_size 80M;
client_body_timeout 50;
location /ws/ {
proxy_pass http://uvsock;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
location / {
proxy_pass http://projectname;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
...
}
location /static {
alias /home/username/server/vier/staticfiles;
}
location /media {
alias /home/username/server/vier/media;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.domain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = domain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name domain.com www.domain.com;
return 404; # managed by Certbot
}
How can I connect this Nginx with my WebSocket for use with Django Channel?
Here's my routing.py looks like:
application = ProtocolTypeRouter({
# "http": get_asgi_application(),
'websocket': AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter(
[
path('stream/<pk>/', LiveStreamConsumer),
path('discussion/global/', GlobalLiveChatConsumer),
path('somepath/', VirtualExpoConsumer)
]
)
)
)
})
The ASGI setup:
...
from django.core.asgi import get_asgi_application
from channels.layers import get_channel_layer
from channels.routing import get_default_application
os.environ['DJANGO_SETTINGS_MODULE'] = 'core.settings'
django.setup()
channel_layer = get_channel_layer()
application = get_default_application()
# application = get_asgi_application()
And my settings.py looks like this:
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
},
},
}
Been around looking for a way to fix them but can't find any.
The celery and Redis themselves have already run with no issue.
But it seems that the WebSocket can't connect with my Nginx config.
This is might due to your daphne server wasn't able to access the cert to redirect to wss. You may refer article here.
After adding the SSL certificate, my static files do not work only in django-admin. Other files on the entire website work very well.
How can I fix this situation (preferably without brutal copying of django-admin static files to my application)?
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('app.urls', namespace='app')),
path('media/<path>/', serve,{'document_root': settings.MEDIA_ROOT}),
path('static/<path>/', serve,{'document_root': settings.STATIC_ROOT}),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
settings.py
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, '../static/')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'app/media')
app (/etc/nginx/sites-available/app) After making changes to this file, they django-admin static file stopped working.
upstream app_server {
server unix:/home/app/run/gunicorn.sock fail_timeout=0;
}
server {
#listen 80;
# add here the ip address of your server
# or a domain pointing to that ip (like example.com or www.example.com)
listen 443 ssl;
server_name ebluedesign.online;
ssl_certificate /etc/letsencrypt/live/ebluedesign.online/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ebluedesign.online/privkey.pem;
server_name 101.170.18.112;
keepalive_timeout 5;
client_max_body_size 4G;
access_log /home/app/logs/nginx-access.log;
error_log /home/app/logs/nginx-error.log;
location /static/ {
alias /home/app/app/app/static/;
}
# checks for static file, if not found proxy to app
location / {
try_files $uri #proxy_to_app;
}
location #proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
}
server {
listen 80;
server_name ebluedesign.online;
return 301 https://$host$request_uri;
}
How can I serve media files during production?
Within nginx, I have:
upstream app_server {
server unix:/home/django/gunicorn.socket fail_timeout=0;
}
server {
# listen 80 default_server;
# listen [::]:80 default_server ipv6only=on;
listen 443 ssl;
server_name tweetz.co;
ssl_certificate /etc/letsencrypt/live/mywebsite.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mywebsite.com/privkey.pem;
root /usr/share/nginx/html;
index index.html index.htm;
client_max_body_size 4G;
server_name _;
keepalive_timeout 5;
# Your Django project's media files - amend as required
location /media {
alias /home/django/django_project/django_project/media;
}
# your Django project's static files - amend as required
location /static {
alias /home/django/django_project/django_project/static;
}
client_max_body_size 4G;
server_name _;
keepalive_timeout 5;
# Your Django project's media files - amend as required
location /media {
alias /home/django/django_project/django_project/media;
}
# your Django project's static files - amend as required
location /static {
alias /home/django/django_project/django_project/static;
}
# Proxy the static assests for the Django Admin panel
location /static/admin {
alias /usr/lib/python2.7/dist-packages/django/contrib/admin/static/admin/;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://app_server;
}
}
` `# Proxy the static assests for the Django Admin panel
location /static/admin {
alias /usr/lib/python2.7/dist-packages/django/contrib/admin/static/admin/;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://app_server;
}
}
server{
listen 80;
server_name mywebsite.com;
return 301 https://$host$request_uri;
}
Not working. Do I need to change something else?
Below my files:
models.py
image = models.ImageField(upload_to='media',blank=True)
urls.py
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
settings.py
MEDIA_ROOT = '/home/django/django_project/static-serve/media/'
MEDIA_URL = '/media/'
server {
'/home/django/django_project/static-serve/media/image.jpg'
location /media {
autoindex on;
alias /home/django/django_project/static-serve/media/;
}
}
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
template
<img src="{{ object.profile.image.url }}" class="w3-circle" style="height:106px;width:106px" alt="Avatar">
The image source in the browser is: mysite/media/media/myimage.jpg
The image is saved: home/django/django_project/media/media/myimage.jpg
Thank you for any help
You should not prepend the path with a slash, since that means you want to upload your file to a directory named media in the root of your filesystem.
You can thus for example upload it to:
image = models.ImageField(upload_to='image/',blank=True)
If you however omit the upload_to=... parameter [Django-doc], Django will simply upload this to the MEDIA_ROOT [Django-doc].
I have Django 2.2 and am trying to serve it to http://myserver.com/application using Nginx proxy pass.
If I try and go to myserver.com/application/admin I get redirect to myserver.com/admin immediately.
Is this a setting I should be specifying in Nginx or in Django to avoid this? Django is running in gunicorn, see Nginx.conf:
location /static {
alias /home/simernes/workspace/django_server/env/static;
}
location /application {
proxy_pass http://localhost:8000/;
proxy_redirect off;
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 http;
proxy_buffer_size 128k;
proxy_buffers 8 128k;
proxy_busy_buffers_size 256k;
}
Finally, this is what my urls.py looks like in a project folder "backend":
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
FORCE_SCRIPT_NAME="/application"
STATIC_ROOT="/home/simernes/workspace/django_server/env/static/"
app_name='backend'
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^', include('api.urls', namespace='api')),
]
Furthermore, when I go to the root of myserver.com/application I get an error of 404 not found and:
Using the URLconf defined in backend.urls, Django tried these URL patterns, in this order:
admin/
^ auth$ [name='auth']
The current path, /, didn't match any of these.
Which is not what I expected, as I am configuring to show the urls available in my api app (see urls.py below), but it's not my main concern with this question so I'm just including it for broader context.
I have another app folder called api with urls also, which is the one being included in backend:
from django.conf.urls import url
from rest_framework.authtoken import views as drf_views
app_name="api"
urlpatterns = [
url(r'auth$', drf_views.obtain_auth_token, name='auth'),
]
Thanks in advance for any responses, they will be greatly appreciated.
You need to also override LOGIN_URL, STATIC_URL, LOGIN_REDIRECT_URL and any other settings that require a path
I'm fairly new to django and nginx, and I started using DigitalOcean's one-click-image install Django 1.6 environment which uses nginx and gunicorn. The default specifications and details can be found here https://www.digitalocean.com/community/tutorials/how-to-use-the-django-one-click-install-image.
Recently, I have been trying to figure out how to secure the admin page for deployment with SSL/HTTPS to prevent login capture. And I realize there is information on this on stackoverflow already. However, it seems like most information has base settings for nginx that differ from Digital Oceans. So, I'm a little thrown for what I should keep, edit and delete. At this moment I'm satisfied with proxy over two instances in nginx: flat pages being under HTTP and the admin under HTTPS as this is simply going to be a launch page. How would you secure the entire site behind https? What would be the necessary modifications to the following nginx and Django settings?
Nginx Settings
upstream app_server {
server 127.0.0.1:9000 fail_timeout=0;
}
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html;
index index.html index.htm;
client_max_body_size 4G;
server_name _;
keepalive_timeout 5;
# Your Django project's media files - amend as required
location /media {
alias /home/django/django_project/media;
}
# your Django project's static files - amend as required
location /static {
alias /home/django/django_project/static;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
}
Django Settings
"""Production settings and globals."""
from __future__ import absolute_import
from os import environ
from .base import *
SESSION_COOKIE_SECURE = True
CRSF_COOKIE_SECURE = True
# Honor the 'X-Fowarded-Proto' header for request.is_secure()
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
########## HOST CONFIGURATION
# See: https://docs.djangoproject.com/en/1.5/releases/1.5/#allowed-hosts-required-in- production
ALLOWED_HOSTS = [
'localhost',
'{{website url}}',
]
########## END HOST CONFIGURATION
########## EMAIL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host
EMAIL_HOST = environ.get('EMAIL_HOST', 'smtp.gmail.com')
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host-password
EMAIL_HOST_PASSWORD = environ.get('EMAIL_HOST_PASSWORD', '')
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host-user
EMAIL_HOST_USER = environ.get('EMAIL_HOST_USER', 'your_email#example.com')
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-port
EMAIL_PORT = environ.get('EMAIL_PORT', 587)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix
EMAIL_SUBJECT_PREFIX = '[%s] ' % SITE_NAME
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-use-tls
EMAIL_USE_TLS = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#server-email
SERVER_EMAIL = EMAIL_HOST_USER
########## END EMAIL CONFIGURATION
########## DATABASE CONFIGURATION
DATABASES = {}
########## END DATABASE CONFIGURATION
########## CACHE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
CACHES = {}
########## END CACHE CONFIGURATION
########## SECRET CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
SECRET_KEY = get_secret("SECRET_KEY")
########## END SECRET CONFIGURATION