nginx - multiple django apps same domain different Urls - django

I want to serve multiple django projects (actually django rest API apps) On one domain but serve each of them on seperated url. like this:
http://test.com/app1/...
http://test.com/app2/...
and so on. I will be using nginx to config it. But i'm facing some problems that wants your helps:
These apps should have different cookie for each other. cause they have different auth system. so token and cookie in one is not valid for another. How to handle this?
What nginx configs you recommend.
Note:
I don't want full detail cause i know concepts. just some hints and usefull commands will do.
Update:
For example i have a django app which has a url test. and i want this path to be served on server with /app1/test. The problem is that when is send request to /app1/test, Django doesn't recognize it as /test, instead as /app1/test and because /app1 is not registered in urls.py will give 404 error.
here is a sample of my nginx config:
server {
listen 80;
server_name test.com;
location /qpp1/ {
include uwsgi_params;
proxy_pass http://unix://home//app1.sock;
}
location /qpp2/ {
include uwsgi_params;
proxy_pass http://unix://home//app2.sock;
}
}

You can try to play with proxy_cookie_path directive:
server {
...
location /app1/ {
proxy_cookie_path / /app1/;
proxy_pass http://backend1/;
}
location /app2/ {
proxy_cookie_path / /app2/;
proxy_pass http://backend2/;
}
}
Update
Here is another variant of configuraion to test.
upstream qpp1 {
server unix:/home/.../app1.sock;
}
upstream qpp2 {
server unix:/home/.../app2.sock;
}
server {
listen 80;
server_name test.com;
location /qpp1/ {
include uwsgi_params;
proxy_cookie_path / /qpp1/;
proxy_pass http://qpp1/;
}
location /qpp2/ {
include uwsgi_params;
proxy_cookie_path / /qpp2/;
proxy_pass http://qpp2/;
}
}

Because I am not using nginx, django's SESSION_COOKIE_PATH-Variable was my solution.
https://docs.djangoproject.com/en/3.1/ref/settings/#session-cookie-path
In your example, you could set it to:
app1
SESSION_COOKIE_PATH = "/app1/"
app2
SESSION_COOKIE_PATH = "/app2/"
Afterwards clear the cookie cache for the domain in your browser, if you've logged in before.

Related

Nginx internal location ignores django completely and allows free access instead

I want to have a private media folder on my django website, accessible only to logged in users, so I got to know that I should handle authentication part on the django side, and file serving on the nginx side. However following internal location config examples I find it impossible to make it work. Nginx ignores django completely (only for the internal location case). Even if I don't have the url allowed in my urls.py and I have it listed as internal location in nginx, it will still be freely accessible to everybody.
I am posting my nginx configuration in hope that someone can find a mistake in it.
My expectation is that everything in /internal/ folder will not be accessible to anonymous users and it will only be accessible by the django application through X-Accel-Redirect header. Right now if I go to /internal/test.png in an incognito window it will show me the picture.
I am not posting my django code for now, since it is ignored anyway by nginx, so it must be the nginx config problem.
server {
server_name XXX.XX.XX.XXX example.com www.example.com;
location = /favicon.ico {
access_log off;
log_not_found off;
alias /home/user/myproject/static/favicon4.ico;
}
location /static/ {
root /home/user/myproject;
}
location /media/ {
root /home/user/myproject;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
location /internal/ {
internal;
root /home/user/myproject;
}
root /home/user/myproject;
location ~* \.(jpg|jpeg|png|webp|ico|gif)$ {
expires 30d;
}
location ~* \.(css|js|pdf)$ {
expires 1d;
}
client_max_body_size 10M;
# below in this server block is only my Certbot stuff
}
P.S. I swapped identifiable data to X characters and basic names.
I had 2 more problems in this config and I will show everything I did to make it work. The original problem why nginx was ignoring django was in how nginx chooses which location block to use, as suggested by Richard Smith.
From nginx.org we can read:
To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered. Then regular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.
And also:
If the longest matching prefix location has the “^~” modifier then regular expressions are not checked.
So regular expressions, if available, will be chosen first. ^~ modifier before prefix makes it chosen instead of regular expressions.
I changed location /internal/ { line to location ^~ /internal/ { and then I got 404 errors every time and no matter how I tried to access the files, but at least I knew nginx was going to this location.
The 2nd mistake was thinking that I can get away with using the same url as the folder name, or in other words, that I can put in my urls.py
path('internal/<path>', views.internal_media, name='internal_media')
together with
location ^~ /internal/ {
internal;
root /home/user/myproject;
}
in my nginx config.
I can't. The url must be different, because otherwise the url doesn't lead to django urls.py - it still leads to /internal/ location through nginx (again, due to how nginx chooses locations).
I changed my urls.py line to point to private url instead:
path('private/<path>', views.internal_media, name='internal_media')
and in the views.py file I redirect to /internal/:
def internal_media(request, path):
if request.user.groups.filter(name='team-special').exists():
response = HttpResponse()
response['X-Accel-Redirect'] = '/internal/' + path
del response['Content-Type'] # without this your images will open as text
return response
else:
raise PermissionDenied()
Aaaand this still didn't work. 404 errors every time. The 3rd mistake was forgetting about the combo of those two:
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
location ~* \.(jpg|jpeg|png|webp|ico|gif)$ {
expires 30d;
}
Now if I went to the url /private/test.jpg nginx didn't let me go to django, because location / is lower in priority than regular expressions, so location ~* took precedence and I never got to django. I noticed it by accident after a lot of time being frustrated, when I put the url incorrectly in incognito mode. When I went to /private/test.jp now I got a 403 forbidden error instead of 404.
It started working immediately when I commented out this.
location ~* \.(jpg|jpeg|png|webp|ico|gif)$ {
expires 30d;
}
location ~* \.(css|js|pdf)$ {
expires 1d;
}
So now internal files worked nicely, but I didn't have caching...
To fix that, I modified my /static/ and /media/ locations, but maybe I won't go into that here, since it is a different topic. I'll just post my full nginx config that works :)
Well, what you might want to also know is that:
~* tells nginx that we are writing a regular expression that is case insensitive
~ would tell nginx that we were writing a regular expression that is case sensitive
server {
server_name XXX.XX.XX.XXX example.com www.example.com;
location = /favicon.ico {
access_log off;
log_not_found off;
alias /home/user/myproject/static/favicon4.ico;
expires 30d;
}
location /static/ {
root /home/user/myproject;
expires 30d;
}
location /media/ {
root /home/user/myproject;
expires 30d;
}
location ~* \/(static|media)\/\S*\.(css|js|pdf) {
root /home/user/myproject;
expires 1d;
}
location ^~ /internal/ {
root /home/user/myproject;
internal;
expires 1d;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
client_max_body_size 10M;
# certbot stuff
}

How to forward all paths that start with a specific location in nginx?

I want to forward all paths that start with /api/ (/api/* ??) to port 1000 but the actual configuration either forwards only the paths that contain "/api/" and nothing else after (/api/login is not forwarded)
location /api/ {
proxy_pass http://localhost:1000/;
}
or it doesn't work at all
location ~ ^/api/(.*)$ {
proxy_pass http://localhost:1000/;
}
. The server is cinfigured as fallows:
server {
listen 80;
keepalive_timeout 70;
server_name server_name;
location / {
root /var/www/html;
index index.html;
}
location /api/ {
proxy_pass http://localhost:1000/;
}
}
I would appreciate any help, Thank you!
Note that with:
location /api/ {
proxy_pass http://localhost:1000/;
}
If there is request /api/foo, then your API server will see /foo.
If, on the other hand (note there is no trailing slash in proxy_pass) you use:
location /api/ {
proxy_pass http://localhost:1000;
}
Then for the same request, your API server will receive request "as is": /api/foo.
So make sure you use the right approach (slash / no slash) which depends on how your API server handles URLs (if it is configured to handle /api/foo URLs then you should not use trailing slash in the proxy_pass.

nginx invalid number of arguments in "proxy_pass" directive

nginx: [emerg] invalid number of arguments in "proxy_pass" directive in /etc/nginx/sites-enabled/django_direct:12
My nginx conf file:
server {
listen 80;
server_name 94.154.13.214;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /root/django_direct/main_app/;
}
location / {
include proxy_params;
proxy_pass unix: /root/django_direct/django_direct.sock;
}
}
What do I do?
UPD:
I have changed file like this:
proxy_pass http://unix:/root/django_direct/django_direct.sock;
But didn't help, I've restarted nginx and now
I am getting now a 502 Bad Gateway error.
If someone else finds this and has this error. Check that you have a semi-colon at the end of the parameters line. goes for all of the lines, not just proxy_pass.
Well the nginx sees two parameters: unix, and /root/django_redirect/.... I have the idea however that you want to specify a UNIX domain socket path. You can do this with:
proxy_pass http://unix:/root/django_direct/django_direct.sock;
As is described in the documentation.
Your argument is wrong.
It needs an URL:
Sets the protocol and address of a proxied server and an optional URI to which a location should be mapped. As a protocol, “http” or “https” can be specified. The address can be specified as a domain name or IP address, and an optional port:
proxy_pass http://localhost:8000/uri/;
or as a UNIX-domain socket path specified after the word “unix” and enclosed in colons:
proxy_pass http://unix:/tmp/backend.socket:/uri/;
See the documentation: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
I can also happen when you mistakenly put a = character in, trying to assign a value that way.
proxy_pass = http://other_node;
The same thing might happen to you with for example auth_basic = "restricted site".
Just remove the =.

Trying to set up domain and subdomain in the same port. (NGINX)

I am trying to run a domain and sub-domain in the same port using Nginx, and do not have success yet.
I have a domain named www.just4bettors.mobi which is for a mobile page, and the subdomain must be named www.desktop.just4bettors.mobi which is clearly for the desktop site.
If you enter www.just4bettors.mobi everything works, you reach the page, but if you enter www.desktop.just4bettors.mobi you get This web page is not available. This is the server block I have so far
server {
large_client_header_buffers 1 1K;
listen 80;
server_name ~^(?<subdomain>[^.]*)\.?just4bettors.mobi$ just4bettors.mobi;
root /home/c0pt/capilleira/capilleiraclickandgamblemobile/www;
location / {
if ($subdomain) {
root /home/c0pt/capilleira/capilleiraclickandgambleweb/dist;
}
if ($host = "just4bettors.mobi") {
root /home/c0pt/capilleira/capilleiraclickandgamblemobile/www;
}
index index.html index.htm;
...
}
}
once I try to access to desktop.just4bettors.mobi, the console returns this GET http://desktop.just4bettors.mobi/ net::ERR_NAME_NOT_RESOLVED
you have that the roots are different here, mobile and web lives in separate places.
so, what am I missing here ?
I've managed Nginx configurations where millions of different domains were in use. It's very complicated trying to work inside one server{} block directive. Here is what I would do:
server {
large_client_header_buffers 1 1K;
listen 80;
server_name www.just4bettors.mobi;
root /home/c0pt/capilleira/capilleiraclickandgamblemobile/www;
location / {
...
}
}
server {
large_client_header_buffers 1 1K;
listen 80;
server_name www.desktop.just4bettors.mob;
root /home/c0pt/capilleira/capilleiraclickandgambleweb/dist;
location / {
...
}
}
You can read more about this here: http://wiki.nginx.org/Pitfalls#Server_Name

Nginx: How to rewrite all URLs except of images?

I'm new to nginx and I want to migrate my website from Apache to nginx. My site has URLs like:
www.mywebsite.com/category/product/balloon
www.mywebsite.com/category/product/shoe
www.mywebsite.com/information/help
etc.
Since I'm using PHP I need to rewrite all URLs to index.php except if it's an image OR if it's a "fake-request". My nginx.config so far:
#block fake requests
location ~* \.(aspx|jsp|cgi)$ {
return 410;
}
#rewrite all requests if it's not a image
location / {
root html;
index index.php 500.html;
if (!-f $request_filename) {
rewrite ^(.*)$ /index.php?q=$1 last;
break;
}
}
error_page 404 /index.php;
# serve static files directly
location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico)$ {
access_log off;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME E:/test2/html/$fastcgi_script_name;
include fastcgi_params;
}
This configuration does not work because:
1. It doesn't block fake request to .php files and I can't add .php to (aspx|jsp|cgi)$
2. It doesn't rewrite the URL if the file exists which is wrong: It should only serve static files directly if it's a defined file-type in(jpg|jpeg|gif|css|png|js|ico)$
How can I solve these problems? I really appreciate every answer, clarification or feedback you can give me.
Thanks
Mike
You need to configure the HttpRewriteModule. This module makes it possible to change URI using regular expressions (PCRE), and to redirect and select configuration depending on variables.
If the directives of this module are given at the server level, then they are carried out before the location of the request is determined. If in that selected location there are further rewrite directives, then they also are carried out. If the URI changed as a result of the execution of directives inside location, then location is again determined for the new URI.