Nginx - Localhost port is lost when clicking on anchor links - docker

I'm using Docker to run nginx on macOS to serve static files on http://localhost:8080.
When clicking on an anchor link, the port is being lost.
For example, clicking on <a href="/foo"> will link to http://localhost/foo instead of http://localhost:8080/foo.
My nginx config is:
server {
listen 80 default_server;
server_name _;
root /var/www/root;
index index.html index.php;
location ~ /\. {
deny all;
}
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
error_page 404 /404.html;
location / {
try_files $uri $uri/ $uri.html =404;
}
location ~ \.php$ {
include /etc/nginx/fastcgi_params;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
}
The directory structure of /var/www/root is:
├── foo
│   └── index.html
├── bar
│   └── index.html
└── baz
└── index.html
However, when changing:
try_files $uri $uri/ $uri.html =404;
to:
try_files $uri/index.html $uri $uri/ $uri.html =404;
the anchor links link correctly.
However, the following does not work:
try_files $uri/ $uri $uri.html =404;
What is going on exactly? Is there a way to exclude $uri/index.html from try_files and have the anchor links preserve the port?
Update
This works, but I'm not quite sure why.
if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$uri/ permanent;
}
location / {
try_files $uri $uri/ $uri.html =404;
}

I can't see the Dockerfile but I'm assuming you run the container with a port mapping of 8080:80. However, nginx is probably still sending the Host header with port 80 and the anchor tag uses that host given how the base URI is calculated:
User agents must calculate the base URI according to the following precedences (highest priority to lowest):
The base URI is set by the BASE element.
The base URI is given by meta data discovered during a protocol interaction, such as an HTTP header (see [RFC2616]).
By default, the base URI is that of the current document. Not all HTML documents have a base URI (e.g., a valid HTML document may appear in an email and may not be designated by a URI). Such HTML documents are considered erroneous if they contain relative URIs and rely on a default base URI.
Reference: https://www.w3.org/TR/html401/struct/links.html#edef-BASE
For development, it sounds like running the container with 8080:8080 should work. For production you might wanna set the appropriate server_name or Host header via an envvar.

Related

How to rewrite clean url to .html in a nginx docker container?

I can run nginx in a Docker container, port 4000. It should serve a static website with the following structure:
/usr/share/nginx/html/
index.html
app.html
app/
some-other.html
The port was mapped to port 3000 on localhost. Now I am trying to use clean urls as following: http://localhost:3000/app. However, when I try to access this url it is redirecting me to http://localhost:4000/app/ (notice the suffixing /) with status code 301 - Moved Permanently.
I've tried using try_files in different locations and order (it's commented below). Nothing worked. In addition, I've also used a rewrite without success. Always resulting in the same redirect.
site.conf
server {
listen 4000;
server_name localhost;
root /usr/share/nginx/html;
# location / {
# index index.html
# try_files $uri $uri.html $uri/ =404;
# }
location / {
index index.html
try_files $uri #htmlext $uri/;
}
location ~ \.html$ {
try_files $uri =404;
}
location #htmlext {
rewrite ^(.*)$ $1.html last;
}
error_page 404 /404;
}
I'd expect nginx to return the content of app.html without touching the url in any form. Can't I do this without adding location /app too?

Nginx reverse proxy static assets 404 not found

I am using Nginx and reverse proxy, also Docker.
I have two Docker containers.
319f103c82e5 web_client_web_client "nginx -g 'daemon of…" 6 minutes ago Up 5 minutes 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp web_client
7636ddaeae99 admin_web_admin "nginx -g 'daemon of…" 2 hours ago Up 2 hours 0.0.0.0:6500->80/tcp, 0.0.0.0:7000->443/tcp web_admin
This is my two containers. When I enter http://website.com, it goes to web_client_web_client container. When I enter http://website.com:6500, it goes to admin_web_admin container. This is the flow right now.
What I want is I don't want my admin users to type http://website.com:6500 to get to the admin page. I prefer them to type http://website.com/admin. So I decided to use proxy_pass which means, when accessing http://website.com/admin, it should proxy_pass to https://website.com:7000
So now, I am posting a Nginx config for web_client_web_client since it's the one which handles requests for port 80 and 433.
Here it is:
server {
listen 80 default_server;
server_name website.com;
location / {
rewrite ^ https://$host$request_uri? permanent;
}
location /admin {
proxy_pass https://website.com:7000/;
}
# I also tried
#location /admin/ {
# proxy_pass https://website.com:7000/;
#}
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
server {
listen 443 ssl;
server_name website.com;
gzip on;
gzip_min_length 1000;
gzip_types text/plain text/xml application/javascript text/css;
ssl_certificate /etc/letsencrypt/live/website.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/website.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
root /usr/share/nginx/html;
location / {
add_header Cache-Control "no-store";
try_files $uri $uri/index.html /index.html;
}
location ~ \.(?!html) {
add_header Cache-Control "public, max-age=2678400";
try_files $uri =404;
}
}
Now, what happens is, static files (css and js files are not loaded) and when inspecting from chrome, request gets made as https://website.com/static/css/app.597efb9d44f82f809fff1f026af2b728.css instead of https://website.com:7000/static/css/app.597efb9d44f82f809fff1f026af2b728.css. So it says 404 not found. I am not sure why I cannot understand such a simple thing.
Your main problem is not really with nginx but with how the 2 applications are setup. I don't have your code but this is what I can infer from your post:
In your pages you load the static content using absolute paths: /static/css/...
So even when you call your pages with /admin in front they will still try to load the static content from /static/
One solution is to use relative paths for your static content. Depending on how complex your application is this might require some work... You need to change the path to static files to something like "./static/css/..." and make sure your files still work. Then your setup in nginx will work because admin pages will try to load '/admin/static/...'
Another solution is to rename the 'static' folder in the admin app to something else and then proxypass that new path as well in your nginx config.
One last thing, your post mentions 2 ports: 6500 and 7000. I am assuming that is a mistake so can you correct it? Or did I understand wrong?

Nginx on kubernetes docker doing infinite redirect when generating conf

I have an nginx pod deployed in my kubernetes cluster to serve static files. In order to set a specific header in different environments I have followed the instructions in the official nginx docker image docs which uses envsubst to generate the config file from a template before running nginx.
This is my nginx template (nginx.conf.template):
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
server {
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
root /usr/share/nginx/html;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location ~ \.css {
add_header Content-Type text/css;
}
location ~ \.js {
add_header Content-Type application/x-javascript;
}
location / {
add_header x-myapp-env $MYAPP_ENV;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
}
I use the default command override feature of Kubernetes to initially generate the nginx conf file before starting nginx. This is the relevant part of the config:
command: ["/bin/sh"]
args: ["-c", "envsubst < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && nginx -g 'daemon off;'" ]
Kubernetes successfully deploys the pod however when I make a request I get a ERR_TOO_MANY_REDIRECTS error in my browser.
Strangely, when I deploy the container without running the command override using an nginx.conf almost identical to the above (but without the add_header directive) it works fine.
(All SSL certs and files to be served are happily copied onto the container at build time so there should be no issue there)
Any help appreciated.
I am pretty sure envsubst is biting you by making try_files $uri $uri/ /index.html; into try_files / /index.html; and return 301 https://$host$request_uri; into return 301 https://;. This results in a loop of redirections.
I suggest you run envsubst '$MYAPP_ENV' <template >nginx.conf instead. That will only replace that single variable and not the unintended ones. (Note the escaping around the variable in the sample command!) If later on you need to add variables you can specify them all like envsubsts '$VAR1$VAR2$VAR3'.
If you want to replace all environment variables you can use this snippet:
envsubst `declare -x | sed 's/^declare -x \([^=]*\)=.*/$\1/' | tr -d '\n'` <template >nginx.conf
Also, while it's not asked in the question you can save yourself some trouble by using ... && exec nginx -g 'daemon off;'. The exec will replace the running shell (pid 1) with the nginx process instead of forking it. This also means that signals will be received by nginx, etc.

nginx location directive

I have an nginx server with a rails app.
my nginx.conf is
server {
listen 80;
server_name nerto.it;
root /var/www/current/public;
passenger_enabled on;
location /m {
passenger_enabled off;
index index.html index.php;
try_files $uri $uri/ /m/index.html?q=$uri;
}
...
}
It works except address like
nerto.it/mprove or nerto.it/mtry
where the initial letter is /m and nginx take this address like nerto.it/m
How can i solve it?
location /m/ {
passenger_enabled off;
index index.html index.php;
try_files $uri $uri/ /m/index.html?q=$uri;
}
or
location = /m {
...
}
It is unclear from your question what behaviour do you expect.
Please, read the documentation: http://nginx.org/r/location
I think you should try smth like this:
location ~* ^/m(.*) {
Regular expressions are specified by prepending them with the “~*” prefix (for case-insensitive matching), or with the “~” prefix (for case-sensitive matching).
nginx location directive

adding wordpress into site that was built in rails

I'm trying to add a Wordpress blog into a site that was built in ruby on rails. I just need it to be in a sub directory. I made a folder in the public directory and put the Wordpress files in there and now i'm getting a routing error and i'm really not that familiar with rails. Can someone help me figure out a way to do this?
You can get PHP and rails working in the same project if you have access to the server configuration. I was able to get things working on a test VPS in just a few minutes. I didn't test with wordpress, just a simple phpinfo() call, but I don't see any reason why it would fail.
My install uses NGINX for the web server, Unicorn for Rails, and spawn-fcgi and php-cgi for the PHP processing.
I already had a rails app working so I just added PHP to that. The rails app uses NGINX to proxy requests to Unicorn, so it was already serving the public directory as static. I will post my virtual host file below so you can see how it was done.
This was all done on an ArchLinux VPS, but other distros should be similar.
My virtual host file:
upstream unicorn {
server unix:/tmp/unicorn.jrosw.sock fail_timeout=0;
}
server {
listen 80 default deferred;
server_name example.com www.example.com;
root /home/example/app/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #unicorn;
location #unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
location ~ \.php$ {
try_files $uri =404;
include /etc/nginx/conf/fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/example/app/current/public$fastcgi_script$
}
}
And then a small script to bring up php-cgi:
#!/bin/sh
# You may want to just set this to run as your app user
# if you upload files to the php app, just to avoid
# permissions problems
if [ `grep -c "nginx" /etc/passwd` = "1" ]; then
FASTCGI_USER=nginx
elif [ `grep -c "www-data" /etc/passwd` = "1" ]; then
FASTCGI_USER=www-data
elif [ `grep -c "http" /etc/passwd` = "1" ]; then
FASTCGI_USER=http
else
# Set the FASTCGI_USER variable below to the user that
# you want to run the php-fastcgi processes as
FASTCGI_USER=
fi
# Change 3 to the number of cgi instances you want.
/usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -C 3 -u $FASTCGI_USER -f /usr/bin/php-cgi
The only problem I had was getting the fastcgi_index option to work, so you'd probably need to look into nginx's url rewriting capabilities to get wordpress' permalink functionality working.
I know this method isn't ideal, but hopefully it gets you on the right track.

Resources