How to host a Django project in a subpath? - django

I am building an API with Django REST framework which is served via Gunicorn and Nginx. The project "exampleproject" has to run at a subpath such as: https://100.100.100.100/exampleproject (example IP address). I do not have a domain name registered for the IP.
Currently, the start page renders as expected at https://100.100.100.100/exampleproject. However a the resource path for "products" does not work. Instead of https://100.100.100.100/exampleproject/products the start page displays https://100.100.100.100/products - which does not work.
I configured the subpath for exampleproject in /etc/nginx/sites-enabled/default as follows:
server {
# ...
location /exampleproject/ {
proxy_pass http://localhost:8007/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
When I manually visit https://100.100.100.100/exampleproject/products Nginx records the following in /var/log/nginx/access.log:
"GET /products/ HTTP/1.1" 404 151 "-"

I found here that one needs to add the following setting to Django's configuration in settings.py:
FORCE_SCRIPT_NAME = '/exampleproject'
This seems to rewrite all paths for nested resources.

Just remove the trailing slash from the proxy pass URL, like this:
proxy_pass http://localhost:8007;
If proxy_pass is specified without a URI, the request URI is passed to the server in the same form as sent by a client when the original request is processed, then keeping the /exampleproject/ part.
See: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
Then you will need to configure Django to understand the URLs under /exampleproject/.
First, you can prepend the subpath to all URLs by updating the urls.py file like this:
urlpatterns = [url(r'^exampleproject/', include(urlpatterns))]
And, in case you are serving statics and media just update in the settings:
MEDIA_URL = '/exampleproject/media/'
STATIC_URL = '/exampleproject/static/'
Of course you can create a custom setting to replicate this or to make the app work without a subpath as well, I believe this is a better solution than FORCE_SCRIPT_NAME because in that case I found we need to update the Django URLs anyways.

Related

How to allow django view in nginx with PWA at /

I'm trying to setting up nginx to my PWA app with django backend.
My django backend has some views to pdf reports on /export route.
My PWA app (Vue) runs on /. I'm getting a blank page when access to /export. The blank page is trying to response from PWA, but when I reload with Shift (⇧) it loads the pdf report.
Here is my nginx server config:
server {
server_name awesome.app;
root /home/awesomeapp/pwa;
location /api/ {
include proxy_params;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://unix:/home/awesomeapp/awesome-backend/awesome.sock;
}
location /export/ {
include proxy_params;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://unix:/home/awesomeapp/awesome-backend/awesome.sock;
}
location / {
index index.html;
}
}
Any idea how to setting up nginx to allow the access to the pdf report generated by django?
Secondly, django backend also has a dashboard app (no pwa) but nginx does not allows to access it (only with shift(⇧)).
OP here.
I'm using workbox (https://developers.google.com/web/tools/workbox) to simplify service workers management.
Workbox allows exclude url paths in pwa routing (https://developers.google.com/web/tools/workbox/reference-docs/v4/workbox.routing). Black lists solve my problem.

flask redirect command goes to http rather than https

I have a flask app behind an nginx server. Nginx handles SSL termination and redirect to https. All http requests are redirected.
In flask, which I don't think should have to know about any of this, I have this bit of code:
#bp.route('/', methods=['GET', 'POST'])
def index_root():
"""Assign a session tag.
"""
return redirect(url_for('main.index', tag=make_new_tag()))
#bp.route('/D/<tag>/accueil', methods=['GET', 'POST'])
def index(tag):
return render_template('index.html', title='', tag=tag)
Now I have a problem: When someone requests http://example.com/, they are 301'd by nginx to https://www.example.com/, which is correct. Then flask 302's them to http://www.example.com/D/123/accueil, which is less good, because nginx will just 301 them to https://www.example.com/D/123/accueil (which is good, but I'd have preferred to skip that extra redirect).
Is this a configuration issue somewhere?
Note that in dev it's important that flask really not think about https. Indeed, flask shouldn't need to know anythint at all about https, and this is why I'm finding this a bit mystifying.
Thanks for any pointers.
The current way to achieve this appears to be passing x_proto=1 as an argument to Werkzeug's ProxyFix middleware:
from werkzeug.middleware.proxy_fix import ProxyFix
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1 ,x_proto=1)
Be sure to set x_for correctly, as per the docs. This is set to the number of proxies, and must be correct for security reasons, otherwise Flask may trust some headers set by an internet user as if they'd come from the reverse proxy. The danger is setting this to 1 when there's no proxy.
Then in the nginx ensure you're setting the X-Forwarded-Proto header in the location block, before your proxy_pass:
location / {
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;
proxy_pass http://localhost:9999/;
}
I haven't tested this with nginx but was able to test directly against the dev server and gunicorn by setting that header specifically in curl:
curl -is http://localhost:5000/ -H 'X-Forwarded-Proto: https'
This returns:
HTTP/1.0 302 FOUND
Location: https://localhost:5000/test_str/accueil`
So it appears to effect url_for which doesn't require modifying individual url_for() calls in your code, which is the case with accepted answer linked by #PGHE.
I've solved this problem on reverse proxy side (Nginx) by using X-Forwarded-HTTPS instead of X-Forwarded-Proto
location / {
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-HTTPS on;
proxy_pass http://localhost:9999/;
}

CSRF verification Failed - Referer is insecure while host is secure

I upgraded Django from 1.8 to 1.9. Afterwards, I get this error on my localhost after the Django admin login:
Referer checking failed - Referer is insecure while host is secure.
Everything works fine in production.
Below is a snippet of my settings.py file:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
I had this error when I was switching from ssl setup to no ssl and forgot to remove last line from upstream configuration in nginx config:
location / {
proxy_pass http://127.0.0.1:8085;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host; #:8080;
#proxy_set_header X-FORWARDED-PROTO https;
}
Those lines in your settings.py file are fine on production because you're using an SSL certificate attached to your domain. However, on local you're probably using http://localhost:8000 or something similar. If you try to connect via https://localhost:{{YOUR_PORT_NUMBER}} you'll most likely get an error like ERR_SSL_PROTOCOL_ERROR.
The issue is in lines 167-168 of django/django/middleware/csrf.py. When you're using https on production, request.is_secure() is returning True...which requires that the HTTP_REFERER also be true or you'll get the error you referenced.
One solution would be to adjust your settings.py file depending on whether you're in your local or production environment. That way you can add those three lines to a settings_production.py file that imports other settings that are common to both localhost and your production server. Your localhost would use a different set of settings that don't include those lines.

resolving domain name on Django-rest-framework HyperlinkedModelSerializer

I would like DRF to use for serialized hyperlinks:
http://<mydomain.com>/api/v1/endpoint
rather than
http://127.0.0.1/api/v1/endpoint
Can this be configured within Django or is it related to my http server configuration (gunicorn+nginx)?
Just set host header for django.
Example for nginx:
location /api/ {
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8000;
}
if you are using https, also add :
proxy_set_header X-Forwarded-Proto https;
and everything works fine :)

django over https form redirection issues

I am using Nginx + supervisord to host a django site behind SSL. The site index loads fine. Everything locally works fine without HTTPS using local server. I am using Django 1.4.2
For some reason I get weird redirections.
When using admin if I edit any item I get redirected to home page.
When submitting new item for save I get 404 (but data is saved).
Non admin:
Again form submit returns me to homepage instead of "success".
The reason for going to homepage I can explain. My nginx redirects all not http traffic to https://localhost with a 301 redirect. So I am guessing django does not think I need secure URLs in places.
The problem is django is not assuming secure url or rather
request.is_secure is False.
I have noted this SO
Accessing Django Admin over HTTPS behind Nginx
Made the changes for proxy pass, i dont think it does anything to handle this. But here it is as is.
settings.py
SESSION_COOKIE_SECURE = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
CSRF_COOKIE_SECURE = True
warning: I am fairly new to django.
I removed the Nginx redirection. Django already handles the redirection correctly.
This was the settings.py values I used together with my nginx proxy pass
SESSION_COOKIE_SECURE = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
CSRF_COOKIE_SECURE = True
Nginx proxy
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_connect_timeout 10;
proxy_read_timeout 10;
proxy_pass http://localhost:8000/;
}
where localhost:8000 is where gunicorn is running.
I still have other issues but redirection problem is solved.