Modifying a Location header with nginx proxy_pass - regex

I have an nginx proxy_pass setup to pass every request on /api through to a backend Tomcat REST service. This service in some cases returns a Location header which varies according to the type of request, e.g., Location: http://foo.bar/baz/api/search/1234567 -- the baz part is due to it being hosted on Tomcat.
My current configuration rewrites the foo.bar host name correctly, but leaves the baz part intact. I'd like to strip this, but the proxy_pass options seem to be limited to clearing or setting a new value for the header.
Is there a way to modify headers dynamically before being passed on to the client, using a regex substitute, for instance? This is my nginx configuration:
location /api {
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_max_temp_file_size 0;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffers 32 4k;
proxy_redirect off;
proxy_pass http://foo.bar:8080/baz/api;
}

You may be able to use regexp to modify it but a better way is to use a proxy redirect:
proxy_redirect http://foo.bar/baz/ /;
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect
Any Location headers for foo.bar/baz/ will go to /
If you just want to redirect /baz/api, that'll work too.
If any redirects are also adding the port, you'll need to add http://foo.bar:8080/baz/ as well (separate redirect).
Hope this helps!

Related

Django Rest Framework deployed under subdirectory proxy urlpattern missmatch

I'm trying to deploy a small API using a subdirectory on the server. Usually if I do the following config it works fine:
location /iframe/api/ {
proxy_pass http://127.0.0.1:8001;
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 https;
client_max_body_size 0;
}
The funny thing is that for static files it seems to work, because I get back proper styling for the rest framework, but for every single urlpattern it also transfers the iframe/api which isn't ideal and basically not a single API route is matched.
I tried to add proxy_redirect off but still no avail. Any idea why isn't this working as expected? How should I deploy a rest framework API under a sub-directory?
Also tried to use FORCE_SCRIPT_NAME='/iframe/api', however I still get the same issue that the URL isn't matched The current path, iframe/api/, didn’t match any of these.
After quite a few hours spend debugging I was able to solve it kinda, but it required multiple ways to do it. If you have better solution feel free to post it!
First you need the trailing / for proxy_pass so the /iframe/api isn't transfered.
location /iframe/api/ {
proxy_pass http://127.0.0.1:8001/;
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 https;
client_max_body_size 0;
}
This solved that part that the URLs wasn't being matched. For some strange reason I wasn't able to solve the static file sharing directly trough the reverse proxy, so what I ended up was to bind both media and static files to a volume on the main machine and serve them with alias
location /media {
alias /home/user/path_to_media/media;
try_files $uri $uri/ =404;
}
location /static {
alias /home/user/path_to_media/static;
try_files $uri $uri/ =404;
}
You also need to set the FORCE_SCRIPT_NAME in the settings file otherwise the urls generated by the rest framework won't work properly.

How to config nginx proxy pass using subfolder domain whith gunicorn Django

How can I configure ngnix to redirect a proxypass from a domain with subfolder to /?
Example:
https://example.com/yoursub/
to
localhost without /yoursub/ prefix
At the moment the direct access to the server ip http://xxx.xxx.xxx.xx/ from the intranet works without problems.
my nginx config file:
upstream app_server {
server unix:/home/myname/APP-Server/gunicorn/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)
server_name 123.456.789.1;
keepalive_timeout 5;
client_max_body_size 4G;
access_log /home/myname/APP-Server/logs/nginx-access.log;
error_log /home/myname/APP-Server/logs/nginx-error.log;
location /static/ {
alias /home/myname/APP-Server/static-root/;
}
# 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;
}
}
If it's relevant: The backend is a django app with a gunicorn server. Do I have to consider anything about the redirect from https to http? I have no control over the base domain.
If I understand correctly, you want to remove the first part of the URI. There are multiple ways you can do that, but the easiest is probably with the alias directive, which will remove the portion of the URI that matches the current location block:
location /foo/ {
alias /home/myname/APP-Server/static-root/; # It doesn't really matter what you put here, since you're proxying everything.
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;
}
If your Nginx server is running on foobar.example and you request http://foobar.example/foo/bar, the upstream server will see a request for http://foobar.example/bar.
The alias directive can be a bit buggy/unintuitive, so it's best to keep your location directive top-level (not nested within other location blocks) and as simple as possible.
If instead you want to add a prefix to the URI, you can do that within the proxy_pass directive itself:
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/foo$uri$is_args$args;
}
If your Nginx server is running on foobar.example and you request http://foobar.example/bar, the upstream server will see a request for http://foobar.example/foo/bar
Try this:
server {
...
location #proxy_to_app {
...
proxy_pass http://app_server/; # note the trailing slash
}
}
Explanation
As per the nginx docs:
If the proxy_pass directive is specified with a URI, then when a request is passed to the server, the part of a normalized request URI matching the location is replaced by a URI specified in the directive.
Since / location matches anything, then everything will be replaced by / (the trailing slash in proxy_pass) before being proxied to the upstream.
The way I handle this is with a rewrite rule:
location /yoursub {
rewrite /yoursub(.*) $1;
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;
}
The rewrite will convert your url from /yoursub/path/to/view to /path/to/view for your app server.

Nginx configuration for jenkins on ec2

Im learning nginx and jenkins by setting up a build server on ec2. Setting up jenkins was easy and I was able to even create a test job. I now want to move on to nginx config and mighty confused as to how to set it up. I have hosted zone with my domain, lets call it domain.com . I created an A record for jenkins.domain.com and in the value box put it the IP of the ec2 instance.
Then added this to the /etc/nginx/site-enabled/default
server {
listen 80;
server_name jenkins.domain.com;
return 301 https://$host$request_uri;
}
server {
listen 80;
server_name jenkins.domain.com;
location / {
proxy_set_header Host $host:$server_port;
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;
# Fix the "It appears that your reverse proxy set up is broken" error.
proxy_pass http://127.0.0.1:8080;
proxy_read_timeout 90;
proxy_redirect http://127.0.0.1:8080 https://jenkins.domain.com;
# Required for new HTTP-based CLI
proxy_http_version 1.1;
proxy_request_buffering off;
# workaround for https://issues.jenkins-ci.org/browse/JENKINS-45651
add_header 'X-SSH-Endpoint' 'jenkins.domain.com:50022' always;
}
}
However when I go to jenkins.domain.com:80 I get the site cannot be reached page...
proxy_redirect is not needed here. You can use below site configuration. You should create file jenkins in /etc/nginx/site-available(for ubuntu) or /etc/nginx/conf.d/ (centos or rhel) & copy the configuration in that file. You have to create the soft link on Ubuntu in site-enabled.
ln -s /etc/nginx/site-available/jenkins /etc/nginx/site-enabled/jenkins
Jenkins conf file
server {
listen 80;
server_name jenkins.domain.com;
access_log /var/log/nginx-access.log;
error_log /var/log/nginx-error.log;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_read_timeout 150s;
proxy_next_upstream error;
proxy_pass http://127.0.0.1:8080;
# Add HTTP Strict Transport Security for good measure.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;";
}
}

Nginx reverse proxy configuration for subdomain with multiple paths

I have a situation here with my Nginx reverse proxy configuration. My distribution is Ubuntu 14.04
I have a domain, let's call it foo.bar.net, and I want the /grafana endpoint to redirect to my grafana server (localhost:3000), the /sentry endpoint to redirect to my sentry server (localhost:9000) and finally, the /private endpoint to redirect to my django server (localhost:8001). I am using gunicorn for the tuneling between django and nginx.
Here is what I tried :
server {
# listen on port 80
listen 80 default_server;
# for requests to these domains
server_name foo.bar.net;
location /sentry {
# keep logs in these files
access_log /var/log/nginx/sentry.access.log;
error_log /var/log/nginx/sentry.error.log;
# You need this to allow users to upload large files
# See http://wiki.nginx.org/HttpCoreModule#client_max_body_size
# I'm not sure where it goes, so I put it in twice. It works.
client_max_body_size 0;
proxy_pass http://localhost:9000;
proxy_redirect off;
proxy_read_timeout 5m;
allow 0.0.0.0;
# make sure these HTTP headers are set properly
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /grafana {
proxy_pass http://localhost:3000;
proxy_redirect off;
proxy_read_timeout 5m;
allow 0.0.0.0;
# make sure these HTTP headers are set properly
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /private {
proxy_pass http://127.0.0.1:8001;
}
location /private/static/ {
autoindex on;
alias /home/user/folder/private/static/;
}
}
The server won't even start correctly, the config is not loading.
I would also like the / path to redirect to the private endpoint if possible.
Additionally, I am not even sure where to put this configuration (sites-available/??)
Can anyone help me with that ?
Thanks a lot,
There are some missing semicolons and other syntax errors. Look at main nginx error log for details and fix them one by one.
Where to put that config file depends on your distribution. For some of them it should be sites-available directory and symlink to that file inside sites-enabled directory for quick enabling and disabling sites, if you don't have sites-available and sites enabled directory, you should put it into conf.d dir in your distribution.

How can I run multiple Ring apps on the same server?

I’m new to Ring (and Clojure server-side programming in general). I have a Ring-based app that works well in “development mode”, i.e. it can listen on localhost:3000 and it responds appropriately. As part of deploying this app I’d like to change the base URL for the app to something like myserver.com/analytics/v1, so that for example a request that previously went to localhost:3000/foo should now go to myserver.com/analytics/v1/foo.
I guess I have two closely-related questions here: How can I tell Ring/Jetty to listen only at a certain URL that is not the root URL of the server? And how can I set this up so that I could add another app (for example, myserver.com/analytics/v2) without downtime for the first app? Do I need to write another Ring app that will listen on myserver.com/ and route the requests to my other apps as appropriate?
The way I'm currently handling this is let each Ring app run in it's own embedded Jetty instance, each listens on their own port, like for example: 8080 en 8085.
On the server I block these ports externally, so only localhost can access them.
Then I setup Nginx to select the right app based on the subdomain:
http://twitter.michielborkent.nl
http://tictactoe.michielborkent.nl
There are more advanced setups possible, but for me this is the one with least configuration.
Here is my nginx.conf. If you want to have more configuration details, just let me know.
server { listen 80;
server_name twitter.michielborkent.nl;
access_log /var/log/twitter-service.log;
location / {
proxy_pass http://localhost:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}
server { listen 80;
server_name tictactoe.michielborkent.nl;
access_log /var/log/tictactoe.log;
location / {
proxy_pass http://localhost:8085;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}
Here’s how I adapted #Michiel Borkent’s nginx.conf to fit my needs:
server {
listen 80;
server_name www.myserver.com;
location /analytics/v1/ {
proxy_pass http://localhost:3001/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
location /trac/ {
proxy_pass http://localhost:3002/trac/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}
With this situation I can just set my Ring app to serve on port 3001; I have Trac serving on port 3002, or I could have another Ring app or whatever. Both of these applications are accessible from www.myserver.com (port 80), just under different paths.