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.
Related
We use an Tomcat application which has been migrated to another system. Also attached is a MSSQL report server. The application needs to be read-only eventually. At this moment we still need to enter some data for certain customers. I use nginx so that only one server is needed for both Tomcat service and Report service. Now I'd like to use nginx location method to block certain requests.
With the help of regex101.com I created a regex to use in nginx. I already tried to change the order of the location blocks. However, I can't get it to work. Here's the link to the regex where you can see that only the blocked URL's are colored: https://regex101.com/r/w3oy0M/1/
Here's the code:
server {
listen 443 ssl;
server_name subdomain.domainname.com;
ssl_certificate c:\cert\public.crt;
ssl_certificate_key c:\cert\private.rsa;
location / {
proxy_pass https://subdomain.domainname.com:8443;
}
location ~ "https:\/\/subdomain\.domainname\.com:8443\/(?!.*customer\/510|.*customer\/10638)(.*\/edit|.*\/new|.*\/archive)" {
# TEST URL FOR NOT ALLOWED REQUESTS
return 301 https://www.google.com;
}
location /Reports {
return 301 https://subdomain.domainname.com:7443/Reports;
}
}
Try
location ~ /.+?(?=customer\/)(?!customer\/510|customer\/10638)(.*\/edit|.*\/new|.*\/archive) {
# TEST URL FOR NOT ALLOWED REQUESTS
return 301 https://www.google.com;
}
However please note anything after # will be dropped as it's an anchor element so a request like
https://subdomain.domainname.com/#/customer/521/installations/new will be sent to nginx as GET / and gets routed to your first location block
But, a request like https://subdomain.domainname.com/hello/customer/521/installations/new will be routed to https://google.com
I'm trying to figure out if there's an easy way to convert a tracking pixel request that gets to Nginx into a POST that will go to an upstream with some added body.
for exmaple, if I get a GET request for http://domain.com/track/mail-id.gif, I'd like to configure Nginx to convert it to a POST that goes to http://upstream/mail-id with some body (let's say status:opened).
how can it be done?
Just wanted to add a more detailed example:
location /track/mail-id.gif {
proxy_pass http://upstream/mail-id;
proxy_method POST;
proxy_set_body "status:opened";
# if needed
# proxy_set_header Some-Header value;
}
Provided a url for proxy_pass here, so to ensure the exact behaviour requested.
You should be able to use Nginx proxy functionality to achieve this, specifically with the proxy_method directive.
Something like:
location /track/mail-id.gif {
proxy_pass http://upstream
proxy_method POST
}
See http://nginx.org/en/docs/http/ngx_http_proxy_module.html for more information about Nginx proxy directives.
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'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'm following Ken Cochrane's answer to the question Seamless deployment of Django to single server. I'm essentially running two production servers, one primary and one fallback, and my nginx is configured as follows (omitting unnecessary details):
upstream app-primary {
server localhost:12345;
server localhost:12346 backup;
}
server {
root /home/fraxtil/app/primary;
location /static/ {
alias /home/fraxtil/app/primary/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-primary;
}
}
When pushing an update, I upgrade fallback, then turn off primary, upgrade it, and turn it back on. The solution is almost perfect, but there's one problem: I'm using django-compressor to collect and compress my CSS and JS files, and the two instances aren't guaranteed to have the same static filenames. So while primary is down, the fallback server handles app requests just fine, but nginx is looking for static files in /.../primary/static/ instead of /.../fallback/static/.
Is there a way to solve this problem without routing static file requests through Django? Alternatively, is there a better way to seamlessly deploy Django updates? (I'm interested in having zero downtime, which is why the two-server model enticed me.)
I found a way to make it work using the try_files directive. I had actually tried this before posting the question, but for some reason my first attempt made every request 404. This seems to work, though:
server {
# Moved the root up one level
root /home/fraxtil/app;
location /static/ {
# Try the primary instance's static folder, then the fallback's
try_files /primary/$uri /fallback/$uri =404;
}
...
The only drawback here is that all requests for nonexistent static files will hit the disk twice, but that shouldn't be too big of an issue.