nginx and trailing slash with proxy pass - http

I am using the following configuration for nginx 1.4.1:
server {
listen 8000;
server_name correct.name.gr;
location /test/register {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1;
}
}
What I want to do is when the users vist http://correct.name.gr:8000/test/register/ they should be proxied to the apache which runs on port 80.
When I visit http://correct.name.gr:8000/test/register/ I get correct results (index.php).
When I visit http://correct.name.gr:8000/test/register/asd I get correct results (404 from apache).
When I visit http://correct.name.gr:8000/test/asd I get correct results (404 from nginx).
When I visit http://correct.name.gr:8000/test/register123 I get correct results (404 from apache).
The problem is when I visit http://correct.name.gr:8000/test/register. I get a 301 response and I am redirected to http://localhost/test/register/ (notice the trailing slash and of course the 'localhost')!!!
I haven't done any other configurations to nginx to put trailing slashes or something similar. Do you know what is the problem ? I want http://correct.name.gr:8000/test/register to work correctly by proxying to apache (or if not possible at least to issue a 404 error and not a redirect to the localhost of the user).
Update 1: I tried http://correct.name.gr:8000/test/register from a different computer than the one with which I had the bad behavior yesterday.. Well, it worked: I just got a 301 response that pointed me to the correct http://correct.name.gr:8000/test/register/! How is it possible to work from one computer but not from the other (I'm using the same browser-Chrome in both computers)? I will try again tomorrow to test from a third one to see the behavior.
Thank you !

My guess is that your upstream server (either apache or your script) triggered a redirect to the absolute url http://localhost/test/register/. Because you use http://127.0.0.1 in your proxy_pass directive, nginx doesn't find a match of the domain name and returns the Location header as is.
I think the right solution is to NOT use absolute redirect if the redirect is to an internal url. This is always a good practice.
However, without changing upstream server, there are two quick solutions.
you can use
proxy_pass http://localhost;
This will tell nginx the domain name of upstream is localhost. Then nginx will know to replace http://localhost by http://correct.name.gr:8000 when it finds that part in the Location header from upstream.
Another one is to add a proxy_redirect line to force nginx to rewrite any location header with http://localhost/ in it.
proxy_pass http://127.0.0.1;
proxy_redirect http://localhost/ /;
I prefer to the first solution because it's simpler. There is no DNS lookup overhead of using proxy_pass http://localhost; because nginx does the lookup in advance when it starts the web server.
reference: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect

Did you already try playing with server_name_in_redirect?
However, I found you question via google because I run the same issue with the trailing slash. Nginx forces a 301 to the same URL with an trailing slash.

Related

DO Spaces [or S3] + Nginx with subdomain

All, i'm running into issues proxying a subdomain to a DO space (suspect that AWS/S3 would act the same way); in this case, trying to serve logos off the bucket, with the requested url being logos.mysite.com/dev/logo1. Nginx should proxy_pass this to https://mybucket.dospace.com/logos/dev/logo1.png (and it will always be .png).
NGINX setup is currently:
server{
server_name logos.mysite.com
location / {
set $bucket "mybucket.dospace.com"
proxy_pass https://$bucket/logos$request_uri.png;
proxy_set_header Host $host;
... other proxy_set/hide settings ...
}
Above is simplest version of many rewrite/return/regex location attempts, but nothing works. In the example above (using chrome), a redirect to https://mybucket.dospace.com/dev/logo1.png occurs, which is missing /logos in the path. Moreover, I don't want chrome to redirect at all - it's more convenient for the user to see the original request from logos.mysite.com (if that's possible).
Was anyone able to execute on a similar setup?

phpMyAdmin inside docker container via nginx reverse proxy

I installed phpMyAdmin docker image and run it with
# docker run --name phpmyadmin -d --link mariadb:db -p 8081:80 -e PMA_ABSOLUTE_URI=http://servm3/pma --restart unless-stopped phpmyadmin/phpmyadmin
Accessing http://servm3:8081 works fine. The variable PMA_ABSOLUTE_URI is for reverse proxies as seen on the docker page.
Then I set up nginx (locally installed, not inside docker) to act as a reverse proxy (working for several other apps like guacamole).
Inside my nginx.conf I have:
location /pma/ {
proxy_pass http://localhost:8081/;
proxy_buffering off;
}
Accessing http://servm3/pma shows the favicon on the browser tab but instead of the login page only a blank page is shown. Removing the preceding "/" and restarting nginx only gives a 404.
# docker logs phpmyadmin
shows nothing except from the php and nginx service start info, nothing related to phpmyadmin.
Local nginx access log shows several 304 and 404 codes and some 200, error log is not present. Detailled log can be found here on pastebin.
I hope somebody will be able to tell me how to make nginx work as a reverse proxy for the phpMyAdmin docker container.
If some important information is missing please let me know.
Be sure to include the rewrite:
location ~ \/pma {
rewrite ^/pma(/.*)$ $1 break;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://localhost:8081;
}
You'll also want to set the PMA_ABSOLUTE_URI environment variable in your docker-compose.yml:
PMA_ABSOLUTE_URI: https://yourdomain.com/pma/
Provided you're running 4.6.5 or later of the docker phpmyadmin you should be set. To update you can docker pull to pull down the latest. i.e.
docker pull phpmyadmin/phpmyadmin
Just remove the ending backslash of /pma/:
location /pma {
proxy_pass http://localhost:8081/;
proxy_buffering off;
}
With it the browser treats it as a directory and request for assets accordingly, which is unexpected for PMA.
Don't need rewrite.
nginx.conf:
location ^~ /pma/ {
proxy_pass http://pma-container/;
absolute_redirect off;
}
docker-compose.yml:
PMA_ABSOLUTE_URI: https://yourdomain.com/pma/
Notice: keep trailing slash on location, proxy_pass, PMA_ABSOLUTE_URI
There's not a lot that can be done. Problem is that phpmyadmin is serving its pages from localhost, and even if nginx translates accesses from http://servm3/pma to http://localhost, all links in HTML content ignore the lattest and, even if they're relative, they don't take into account the /pma part. So, all those 404 errors you're seeing are from resources that, inside HTML, are referenced as relative links like styles.css, that when referenced/clicked become http://servm3/styles.css, that doesn't exist in the server.
Unfortunately reverse proxies work at the header level so, even if they are able to change headers like Location on the fly, they leave HTML content untouched, and hence the problems. There're workarounds involving changing HTML code on the fly, but they're not easy, are unreliable at most and they hinder performance considerably, so the only practical solution is that websites explicitly support some kind of base path setting. In this case, the solution would be that phpmyadmin Docker image allowed setting the base path using an environment variable in docker-compose.yaml, instead of defaulting to root.
Another workaround in the mean time would be not using a relative path, but a subdomain. If you're in control of DNS settings for servm3, you could use something like phpmyadmin.servm3, and proxy_pass without problems.
If you have a docker setup that will have many different domains but one database(like a multistore magento shop, or a multi domain wordpress) it might be undesireable to forward all the traffic to one single domain. Or even having to hardcode it in the ENV variable.
It's easier to portforward based on subdomain.
In your nginx config define a map, listing your port numbers
map $subdomain $port_number {
default "013";
phpmyadmin "014";
mailhog "017";
}
This will make it so that when the variable $subdomain matches any entry in the map below, the variable $port_number will be set to the corresponding value. In my example is default "013" if nothing is matched.
Then add the following directive to your server directive.
server_name server_name ~^(?:(?<subdomain>[^.]+)\.|)(?<base_domain>.+\.localhost)$;
These regexes set 2 variables $subdomain and base_domain
If the request is example.com.localhost
$subdomain will have example
$base_domain will have com.localhost
$port_number will have 013
If the request is example.com.localhost
$subdomain will have phpmyadmin
$base_domain will have example.com.localhost
$port_number will have 014
[Optional] If you need the actual domain in a variable you could use an if statement like this:
set $lookup_domain $host;
if ($port_number != "013") {
set $lookup_domain $base_domain;
}
and $lookup_domain will contain the actual domain you're interested in.
You can then use a proxy pass like this:
location / {
proxy_pass http://docker-host:${backend}${port_number};
proxy_pass_header Content-Type;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # allow websockets
proxy_pass_header Connection;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
}
Where the $backend variable contains in my case the base port range for my webapp set.
So the proxy url becomes someting like http://docker-host:20013 depending on the app. Feel free to skip that and set the ports to actual ports or whatever, or to modify it to follow any other logic.
don't know if you're still looking for an answer.
just follow this answer from Joshua Ostrom.
The problem is I dont know why it doesn't behave like it should but, you need to add index.php at the end like https://yourdomain.com/pma/index.php.
I had the same issue

Running gogs as nginx subdomain

I am trying to run gogs off of my nas. I run other stuff off my nas, so I decided to make gogs a subdomain. Here is what I tried:
/etc/nginx/sites-enabled/default:
server {
listen 80;
server_name gogs.nas.me;
location / {
proxy_pass http://127.0.0.1:3237;
proxy_set_header Host $host;
proxy_buffering off;
}
}
I do not have a domain name for it, but I have nas.me pointing towards 192.168.0.120 in /etc/hosts.
When I go to gogs.nas.me, I get gogs.nas.me’s server DNS address could not be found. When I go to nas.me, I get the index of my nas. What am I doing wrong?
Edit: I also tried using nas.me/gogs, which worked but all of the assets did not get the /gogs prefix so I got 404s on everything but /.
You should add gogs.nas.me to the file /etc/hosts like this:
192.168.0.120 nas.me gogs.nas.me
Both names should be on the same line, otherwise there will be wrong behavior (look at this answer - unix.stackexchange.com/a/102663)
Usually adding a new DNS entry in /etc/hosts does not need restarting - but just to be safe, you can do a restart.

NGINX not responding

I have three sites configured on my server using NGINX and the first two are working fine. One is a static site and one is running Rails (using Unicorn). I have attempted to mirror the NGINX/Unicorn configurations.
For the non-working site, I get "problem loading site" in my browser and absolutely nothing in my NGINX error logs (even at debug level) or my Unicorn log. I also get nothing when I attempt to cURL to the site.
I have double checked DNS by pinging domain name and am running out of ideas. I've also tried making this the default server and browsing by IP address.
Thoughts on how I should go about debugging? I would like to at least understand if NGINX is seeing these requests or not.
NGINX configuration:
upstream unicorn-signup {
server unix:/home/signup/app/tmp/sockets/unicorn.sock;
}
server {
listen 80;
listen [::]:80;
root /home/signup/app/current/public;
server_name signup.quote2bill.com;
# configure for Unicorn (NGINX acts as reverse proxy)
location / {
try_files $uri #unicorn;
}
location #unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded_Proto $scheme;
proxy_redirect off;
proxy_pass http://unicorn-signup;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
Fixed! It was the dreaded force_ssl flag in my production configuration. For future travelers, here is how I went about troubleshooting:
Went on a Costco run to clear my mind and buy huge quantities of stuff.
To determine if it was a DNS, NGINX or Unicorn/Rails problem, I replaced my NGINX configuration with a very simple one and placed a simple index.html in my public root. This worked fine - which lets DNS off the hook (I could resolve the domain name at the web server).
I diff'd the working and non-working NGINX configuration files for the nth time and made them as close as possible but didn't find anything.
Then I noticed that when I was serving the simple index.html file in #2 above, the domain was not getting redirected to https:// but when switched to my "normal" Unicorn/Rails version, I was always getting redirected.
I searched for Rails redirecting to SSL and remembered the force_ssl flag.
I checked my two projects and noticed the flag was not set in the working project, but set in the non-working one (smoking gun).
I changed, committed, redeployed and reloaded the browser and it... didn't work (!) Fortunately, I had the good sense to clear browser cache and try again and it is all good now.
Hope this helps someone.

Nginx proxy redirect without changing url

I have a nginx (:80) and an upstream server (:8080) running on my machine.
I want to proxy all requests to /assets/(*.?) to upstream's /upstream/$1 location.
The upstream server redirects (302) /upstream/file_id to the /real/file/location.ext
Here is my code:
location /assets/ {
rewrite ^/assets/(.*) /upstream/$1 break;
proxy_pass http://127.0.0.1:8000;
}
This seems to work, but on the client side I get the redirected location:
http://myserver.com/real/file/location.ext
I kinda want to hide it so that it stays:
http://myserver.com/assets/file_id
The idea behind this is to make the upstream server find the real file's location, but let the nginx serve the file without giving away its real location. Is this even possible?
first you're using 8000 in proxy_pass, but you're mentioning your port is 8080.
Second, remove the rewrite line should do the trick, because youre actually using the rewrite rule here and never get to the proxy_pass line. Something like the following should work:
location /assets/ {
include proxy_params;
proxy_pass http://127.0.0.1:8080;
}
There are also proxy_rewrite and proxy_redirect commands which might help you in getting this upstream-redirect handled internally by nginx.
Hope that helps!

Resources