I'm currently developing a site where the functionality needs to be split into separate subdomains, dashboard.example.com, admin.example.com, and facebook.example.com. I would like everything to be served through a single Django project because everything will be using the same core models. I'm using Nginx as a front-facing proxy server handling static files and passing all other requests to Apache.
The solution I thought of was to map each of these subdomains to the appropriate app through nginx:
server {
listen 80;
server_name dashboard.example.com;
...
location / {
proxy_pass http://127.0.0.1/dashboard/;
...
}
}
server {
listen 80;
server_name admin.example.com;
...
location / {
proxy_pass http://127.0.0.1/admin/;
...
}
}
...doing that for each subdomain, effectively mapping the subdomains to their respective app url namespaces. The problem I encountered was that Django was unaware of the mapping, so when it reversed a URL, it would prepend /dashboard/, etc. to it, creating URLs like dashboard.example.com/dashboard/dashboard/. I figure I could write a custom reverse function to strip out the unnecessary subdirectory, but that seems like a band-aid.
Is there a better way to accomplish what I need, or should I restructure the project?
Thanks for your help.
Django's Sites framework (https://docs.djangoproject.com/en/1.7/ref/contrib/sites/) should be sufficient for this, if not, take a look at django-subdomains (http://django-subdomains.readthedocs.org/en/latest/) as seems to have a means of resolving your reverse URLs (based off a quick Google search, I've never used it myself!)
Related
First of all, google or SO search didn't help me: lots of tips regarding django's staticfiles, which I believe are not relevant here.
I have inherited a project consisting of:
Django backend in form of API returning JSON responses only;
standard Swampdragon deployment pushing realtime updates to frontend; very little configuration has been done here;
Frontend webapp built on Backbone and marionette.js, compiled and minified by Grunt.
My problem is: the frontend needs to know addresses for swampdragon and django servers; right now those values are hardcoded, so there is for example a Backbone model with lines like:
url: function() {
return App.BACKEND_URL+'settings/map';
}
Why hardcoded: backend can be served on any port or have a subdomain to itself; frontend is static and normally would be simply thrown into /var/www (for Apache) or would use some very simple nginx config. Both will be served from the same place, but there is no guarantee the port numbers or subdomains would match.
Idea number 1: try to guess what BACKEND_URL is from javascript, by taking window.location.host and appending standard port. That's hackish and error prone.
Idea number 2: move frontend to Django and make it ask for swampdragon credentials (they would be sent in the context of home view). Problem with that is, the frontend files are compiled by grunt. So where Django would kindly expect something like:
<script src="{% static 'scripts/vendor/modernizr.js' %}"></script>
I actually have
<script src="scripts/vendor/a8bcb0b6.modernizr.js"></script>
Where 'a8bcb0b6' is grunt's hash/version number and will be regenerated during next minification/build. Do I need to add additional logic to get rid of such stuff and copy grunt's output directory to django's static and template dirs?
Or is there another way to make this work, the right one, I am missing?
Your architecture is already clean, no need to make Django know about grunt or serve static files, and no need to use JS hacks to guess port numbers
Reverse Proxy
Use a reverse proxy like nginx or any other web server you like as a front end to both the static files and the REST API.
In computer networks, a reverse proxy is a type of proxy server that
retrieves resources on behalf of a client from one or more servers.
These resources are then returned to the client as though they
originated from the proxy server itself. (Wikipedia)
I will outline the important aspects without going into too much detail:
URL for the REST API
We make configs so that nginx will forward the API requests to Django
location /api {
proxy_pass http://127.0.0.1:8000; # assumes Django listens here
proxy_set_header Host $http_host; # preserve host info
}
So the above assumes your Django REST is mapped to /api and runs on port 8000 (e.g. you can run gunicorn on that port, or any other server you like)
http://nginx.org/en/docs/http/ngx_http_proxy_module.html
URL for our front end app
Next nginx will serve the static files that come out of grunt, by simply pointing it to the static folder
location / { alias /app/static/; }
The above assumes your static resources are in /app/static/ folder (like index.html, your CSS, JS etc). So this is primarily to load your BackboneJS app.
Django static files
Next step is not required, but if you have static files that you use with the Django app (static files that are generated with ./manage.py collectstatic, e.g. the django admin or the UI of Django REST Framework etc), simply map according to your Django settings.py STATIC_URL and STATIC_ROOT
location /static { alias /app/django_static_root/; }
/static and django_static_root being the STATIC_URL and STATIC_ROOT respectively
To sum up
So e.g. when you hit example.com/, nginx simply serves up the static files, then when a JS script makes REST call to /api, it gets trapped in the /api nginx location and gets forwarded to Django
End result is, example.com/ and example.com/api both hit the same front end web server, which proxies them to the right places
So there you have it, reserve proxying solves your ports and subdomain issues (and many others, like slow static files from Django and same-origin policies in web browsers and firewalls not liking anything besides default HTTP and HTTPS ports)
I'm currently developing a site where the functionality needs to be split into separate subdomains, dashboard.example.com, admin.example.com, and facebook.example.com. I would like everything to be served through a single Django project because everything will be using the same core models. I'm using Nginx as a front-facing proxy server handling static files and passing all other requests to Apache.
The solution I thought of was to map each of these subdomains to the appropriate app through nginx:
server {
listen 80;
server_name dashboard.example.com;
...
location / {
proxy_pass http://127.0.0.1/dashboard/;
...
}
}
server {
listen 80;
server_name admin.example.com;
...
location / {
proxy_pass http://127.0.0.1/admin/;
...
}
}
...doing that for each subdomain, effectively mapping the subdomains to their respective app url namespaces. The problem I encountered was that Django was unaware of the mapping, so when it reversed a URL, it would prepend /dashboard/, etc. to it, creating URLs like dashboard.example.com/dashboard/dashboard/. I figure I could write a custom reverse function to strip out the unnecessary subdirectory, but that seems like a band-aid.
Is there a better way to accomplish what I need, or should I restructure the project?
Thanks for your help.
Django's Sites framework (https://docs.djangoproject.com/en/1.7/ref/contrib/sites/) should be sufficient for this, if not, take a look at django-subdomains (http://django-subdomains.readthedocs.org/en/latest/) as seems to have a means of resolving your reverse URLs (based off a quick Google search, I've never used it myself!)
I have two separate Django projects running on a Amazon EC2, that have different databases and use different settings files.
To do this, I use Nginx, that for the project1 listens at port 80 (url www.domain.com) and for the project2 listens at port 81 (url www.domain.com:81).
Using my website at some places, I noticed that project2 was excessively slow, due to maybe some networks block requests to specific ports.
So, I just want to use both projects with port 80. Is it possible, maybe with a different URL after the ".com"?
Sure it's possible.
Take a look at Nginx 'Server Blocks' to accomplish this...
http://wiki.nginx.org/ServerBlockExample
The caveat is that I don't believe it's possible to create 'Server Blocks' and route based on "different URL after the ".com"?" To be technically correct, it would be the URL path after the .com.
However, if you have different 'server names' (http://nginx.org/en/docs/http/server_names.html) than you could set up different 'server blocks' to handler requests to different Django applications running on the same box.
Say for instance you have Application1 using domain 'foo.com' and Application2 using domain 'bar.com'. A typical setup would look like,
http {
index index.html;
server {
server_name foo.com;
access_log logs/application1.access.log main;
root /var/www/application1.com/htdocs;
}
server {
server_name bar.com;
access_log logs/application2.access.log main;
root /var/www/application2.com/htdocs;
}
}
Just make sure both foo.com and bar.com point to your server running the Django apps.
You could use subdomains too if you'd like... app1.foo.com, app2.foo.com... both are valid server_name values.
Specifically, I think I need Nginx to not consume (capture?) a piece of the URI I'm using to route a location. But I don't know if such a thing is possible.
Let me back up. I'm transitioning my app's setup. Before, I had an Nginx config file with a single location block matching everything:
server {
listen 80;
server_name ec2-54-234-175-21.compute-1.amazonaws.com;
location / {
...
proxy_pass http://localhost:8000/;
}
With this setup, up until now, I've just been running a Django app. After Nginx routes the user to the only endpoint, Django consumes the whole URI. What this means is that internally, Django chops off /api/ and then just has 1.0, which it also needs.
Now, I'm going to use Nginx to proxy for multiple servers, each hosting an app. Because of the aforementioned internal routing, the Django app needs to receive (at least) a URI of /1.0, while the other (on Flask) needs to get /api/2.0. However, in order for the Nginx location directives to make any sense, I have to differentiate the two applications. What I've worked out is that I'll just have the following two location directives:
server {
listen 80;
server_name ec2-54-234-175-21.compute-1.amazonaws.com;
location /api/[1.0] {
...
proxy_pass http://localhost:8000/;
}
location / {
...
proxy_pass http://localhost:8080/;
}
However, note the [1.0] in brackets. If that is there, what I've realized is that in order to actually access the intended resource, I have to enter a URI of /api/1.0/1.0. So, somehow I need a non-consuming location in my nginx conf. If I knew how to express this in simpler terms, I would. Is what I want possible?
About a week after asking this question, a coworker pointed out the answer I wanted, and it's very simple. The key is in the malleability that Nginx gives you with its routing. Because I wanted to access a resource that was at /api/1.0, but still needed to differentiate the two apps in my config, I do the following:
server {
listen 80;
server_name ec2-54-234-175-21.compute-1.amazonaws.com;
location /api/1.0 {
...
proxy_pass http://localhost:8000/api/1.0;
}
location /api/2.0 {
...
proxy_pass http://localhost:8080/api/2.0;
}
This effectively makes the URL "non-consuming", as I wrote above, because I use the desired resource URL to route with, and then duplicate it in referencing the actual location within the specific app.
Maybe this will help someone else.
it seems like google links to my page contain a "www." before the domain, this causes security errors since I'm reading data from the canvas and the images will be marked as cross-orign if they are from "www.x.com" rather than "x.com".
So i'm wondering what's the nicest way to redirect people from the url with the www. in it to one without?
(alternately, can I get google to link without the www?)
Thanks
The best way would be to let your webserver (apache/nginx) handle the redirect, instead of doing it in Django.
In nginx it could look something like this:
server {
listen 80;
server_name www.example.com;
rewrite ^(.*) http://example.com:80$1 permanent;
}
Of course you could do it in Django, simply check for the existence of the subdomain www and then redirect to the same URL without this subdomain. In this case you would need to add this logic to all of your views though (could be a decorator for example). Still, its hard to maintain and the better and simpler approach is the one I mentioned above.