Nginx - Reverse proxy everything after location specification - nginx

I'm trying to use nginx to reverse proxy a specific location specification, as below:
server {
listen 80;
server_name example.com;
location /example {
proxy_pass http://localhost:8080/test;
}
}
Now, when I try and access a resource at http://example.com/example/css/styles.css I expect it to try and access http://localhost:8080/test/css/styles.css. But alas - I get a 404 from nginx.
When I try and access http://example.com/example it shows me what's on http://localhost:8080/test (so I know the base url segment is working) minus anything being imported into that page from a relative url (e.g. styles and JS files)
How do I get the reverse proxy to work with child url segments?

Per Ivan's comment on the original question, here is a working configuration with trailing slashes added:
server {
listen 80;
server_name example.com;
location /example/ {
proxy_pass http://localhost:8080/test/;
}
}

Related

nginx - how to route a specific uri endpoint without having it ending with a slash

my current setup:
user inputs www.example.com/example/xx/healthcheck
kubernetes traefik ingress with backend as /example and goes to a nginx service
nginx with this small configuration:
server {
listen 80;
listen [::]:80;
server_name localhost;
location /example/xx/healthcheck {
root /usr/share/nginx/html;
index output.html;
}
i placed output.html inside /usr/share/nginx/html/example/xx/healthcheck
My issue is that when i hit www.example.com/example/xx/healthcheck it doesn't load the page. it only works if it has a slash at the end
www.example.com/example/xx/healthcheck/
is there a proper and better way to do this? I want www.example.com/example/xx/healthcheck and it hits the page.
Thank you
You can use
location = /example/xx/healthcheck {
default_type text/html;
alias /any/path/to/output.html;
}
Beware - the slash-ended request www.example.com/example/xx/healthcheck/ won't work anymore with this config, although it would work if you use your location /example/xx/healthcheck { ... } as the second one or use a rewrite rule like
rewrite ^/example/xx/healthcheck/$ /example/xx/healthcheck;

How can I hide a file from the browser, yet still use it on the webserver with NGINX?

Here's my scenario:
I have a vagrant cloud set up at an IAAS provider. It uses a .json file as its catalog to direct download requests from vagrant over to their corresponding .box files on the server.
My goal is to hide the .json file from the browser so that a surfer cannot hit it directly at, say: http://example.com/catalog.json and see the json output as that output lists the url of the box file itself. However, I still need vagrant to be able to download and use the file so it can grab the box.
In the NGINX docs, it mentions the "internal" directive which seems to offer what I want to do via try_files, but I think I'm either mis-interpreting what it does or just plain doing it wrong. Here's what I'm working with as an example:
First, I have two sub-domains.
One for the .json catalog at: catalog.example.com
A second for the box files at: boxes.example.com
These are mapped, of course, to respective folders on the server, etc.
With that in mind, in sites-available/site.conf, I have the following server blocks:
server {
listen 80;
listen [::]:80;
server_name catalog.example.com;
server_name www.catalog.example.com;
root /var/www/catalog;
# Use try_files to trigger internal directive to serve json files
location / {
try_files $uri =404;
}
# Serve json files to scripts only with content type header application/json
location ~ \.json$ {
internal;
add_header Content-Type application/json;
}
}
server {
listen 80;
listen [::]:80;
server_name boxes.example.com;
server_name www.boxes.example.com;
root /var/www/boxes;
# Use try_files to trigger internal directive to serve json files
location / {
try_files $uri =404;
}
# Serve box files to scripts only with content type application/octet-stream
location ~ \.box$ {
internal;
add_header Content-Type application/octet-stream;
}
}
The NGINX documentation for the internal directive states:
Specifies that a given location can only be used for internal requests. For external requests, the client error 404 (Not Found) is returned. Internal requests are the following:
requests redirected by the error_page, index, random_index, and try_files directives;
Based on that, my understanding is that my server blocks grab any path for those sub-domains and then, passing it through try_files, should make that available when called via vagrant, yet hide it from the browser if I hit the catalog or a box url directly.
I can confirm that the files are not accessible from the browser; however, they're also unaccessible to vagrant as well.
Am I mis-understanding internal here? Is there a way to achieve my goal?
Make sure for the sensitive calls the server listens on localhost only
Create a tunnel between the machine running vagrant (using an arbitrary port) and your IAAS provider machine (on the web server port, for example).
Create a user on your IAAS machine who is only allowed to interact with the forwarded web-server port (via sshd_config)
Use details from below
https://askubuntu.com/questions/48129/how-to-create-a-restricted-ssh-user-for-port-forwarding
Reference the tunneled server using http://:/path in both your catalog.json url and your box file url
Use a server block in your NGINX config which listens to the 127.0.0.1:80 only and doesn't use server_name. You can even add default_server to this so that anything that doesn't match other virtual host will hit this block
Use two locations in your config with different roots to serve files from /var/www/catalog and /var/www/boxes respectively.
Set regex locations for your .json and .box files and use a try_files block to accept the $uri or redirect to 444 (so you know it hit your block)
Deny the /boxes and /catalog otherwise.
See the below nginx config for example
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name example.com;
server_name www.example.com;
root /var/www;
location ~ /(catalog|boxes) {
deny all;
return 403;
}
}
server {
listen 80;
listen [::]:80;
server_name store.example.com; # I will use an eCommerce platform eventually
root /var/www/store;
}
server {
listen 127.0.0.1:80;
listen [::]:80;
root /var/www;
location ~ \.json$ {
try_files $uri $uri/ =444;i
add_header Content-Type application/json;
}
location ~ \.box$ {
try_files $uri $uri/ =444;
add_header Content-Type octet/stream;
}
location ~ /(catalog|boxes) {
deny all;
return 403;
}
}
I think all you need here is to change the access level to the file. There is 3 access level (execute, read and write) you can remove the execute access level from your file. On the server consul run the command:
chmod 766 your_file_name
you can see:
here
and here
for more information.

nginx redirect twice from https to http

i have 2 servers, one has ssl and i config it like this,
in the server with SSL certification(which is https:// www.example.com):
location ~^/abc/.* {
proxy_pass http://www.example.com:8214/
}
in another server(which is http:// www.anotherExample.com):
server {
listen 8214;
server_name www.anotherExample.com;
rewrite ^/(.*)$ http://www.anotherExample.com:8080/$1 permanent;
}
and after access https:// www.example.com/abc/api/getGroup
it can't redirect to http:// www.anotherExample.com:8080/api/getGroup
Anything wrong???
There are a couple of things you could do to improve your configuration.
location ^~ /abc/ {
proxy_pass http://www.example.com:8214$uri;
#You should have other directives set here as well.
}
Also, consider setting up an upstream.
Then, for your server block:
server{
listen 8124;
server_name www.anotherExample.com;
rewrite ^/abc/(.*)$ http://www.anotherExample.com:8080/$1 permanent;
}
server{
listen 8080;
server_name www.anotherExample.com;
location ^~ /api/ {
#your_config_here
}
}
The explanation:
In your first location block, you shouldn't have .* in the expression. Nginx will match this for you. Then, when you're proxying, you can explicitly tell Nginx to send the URI as well.
Next, you're sending the URI www.anotherExample.com:8124, which includes /abc/, so you want to extract everything after that.
Lastly, because you've rewritten it to point to 8080 port, you'll need to define a separate server block for this.
I don't know what you're aiming to achieve, but so much proxying and redirects isn't necessary in most cases, and might lead to poor performance. Another consideration that you should take into account is you're sending unencrypted information to anotherExample.com, which, if not on the same local network, might be a security vulnerability.

redirecting request to a different listener nginx

I am having two listener 80 and 777. Port 80 act as a reverse proxy. And port 777 does some extra stuff and want to redirect to port 80. How do I redirect to a different port in nginx? I was trying with rewrite but later figured out that it is only used for change of path
###
server{
listen 80;
server_name _;
location / {
proxy_pass "http://upstream0;#" is included since links are not allowed in the post
}
}
server{
listen 777;
server_name _;
#doing some other extra stuf
//Want to redirect to port 80 under some condition
}
Is it possible?
Thanks
as far as nginx is concerned there's no real difference to passing something to another nginx listener/server and passing someting ot apache/mongrel/thin/... or any other http server
in other words if you want to pass things through to another listener you'd use proxy_pass
so what you want to do is something like
location / {
if (some condition) {
proxy_pass http://$host:80
}
}
see http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass

Nginx rewrite non-www-prefixed domain to www-prefixed domain

I see the Nginx HttpRewriteModule documentation has an example to rewrite a www-prefixed domain to a non-www-prefixed domain:
if ($host ~* www\.(.*)) {
set $host_without_www $1;
rewrite ^(.*)$ http://$host_without_www$1 permanent; # $1 contains '/foo', not 'www.mydomain.com/foo'
}
How can I do the reverse-- rewrite a non-www-prefixed domain to a www-prefixed domain? I thought maybe I could do something like the following but Nginx doesn't like the nested if statement.
if ($host !~* ^www\.) { # check if host doesn't start with www.
if ($host ~* ([a-z0-9]+\.[a-z0-9]+)) { # check host is of the form xxx.xxx (i.e. no subdomain)
set $host_with_www www.$1;
rewrite ^(.*)$ http://$host_with_www$1 permanent;
}
}
Also I wanted this to work for any domain name without explicitly telling Nginx to rewrite domain1.com -> www.domain1.com, domain2.com -> www.domain2.com, etc. since I have a large number of domains to rewrite.
As noted in the Nginx documentation, you should avoid using the if directive in Nginx where possible, because as soon as you have an if in your configuration your server needs to evaluate every single request to decide whether to match that if or not.
A better solution would be multiple server directives.
server {
listen 80;
server_name website.com;
return 301 $scheme://www.website.com$request_uri;
}
server {
listen 80;
server_name www.website.com;
...
}
If you're trying to serve an SSL (HTTPS) enabled site, you got more or less three different options.
Set up multiple IP addresses having each server directive listening on their own IP (or different ports if that's an option for you). This options needs SSL certificates for both website.com and www.website.com, so either you have a wild card certificate, a UNI certificate (multiple domains) or just plainly two different certificates.
Do the rewrite in the application.
Use the dreaded if directive.
There is also an option to use SNI, but I'm not sure this is fully supported as of now.
if ($host !~* ^www\.) {
rewrite ^(.*)$ http://www.$host$1 permanent;
}
Well I guess I don't really need the outer "if" statement since I'm only checking for domains of the form xxx.xxx anyways. The following works for me, though it's not robust. Let me know if there is a better solution.
if ($host ~* ^([a-z0-9\-]+\.(com|net|org))$) {
set $host_with_www www.$1;
rewrite ^(.*)$ http://$host_with_www$1 permanent;
}
Edit: Added hyphen to the regular expression since it is a valid character in a hostname.
if ($host ~* ^[^.]+\.[^.]+$) {
rewrite ^(.*)$ https://www.$host$1 permanent;
}
It's only possible to get valid hostnames because the request will never make it to your server otherwise, so there's no need to build your own validation logic.
The nginx documentation cautions against the use of if for rewriting. Please see the link here: http://wiki.nginx.org/Pitfalls#Server_Name
HTTP & HTTPS without if conditions:
server {
listen 80;
listen 443;
server_name website.com;
return 301 $scheme://www.website.com$request_uri;
}
server {
listen 80;
listen 443 default_server ssl;
server_name www.website.com;
# Your config goes here #
}
Solution for multiple domains, working on nginx 1.17 for me:
server {
listen 80;
server_name .example.com;
set $host_with_www $host;
if ($host !~* www\.(.*)) {
set $host_with_www www.$host;
}
return 301 https://$host_with_www$request_uri;
}
In this config example additionally rewrites HTTP on HTTPS, if you don't want rewrite — replace https:// with http:// in return string.
If you want keep protocol — use $scheme variable.

Resources