NGINX, proxy_pass and SPA routing in HTML5 mode - html

I have NGINX set up as a reverse proxy for a virtual network of docker containers running itself as a container. One of these containers serves an Angular 4 based SPA with client-side routing in HTML5 mode.
The application is mapped to location / on NGINX, so that http://server/ brings you to the SPA home screen.
server {
listen 80;
...
location / {
proxy_pass http://spa-server/;
}
location /other/ {
proxy_pass http://other/;
}
...
}
The Angular router changes the URL to http://server/home or other routes when navigating within the SPA.
However, when I try to access these URLs directly, a 404 is returned. This error originates from the spa-server, because it obviously does not have any content for these routes.
The examples I found for configuring NGINX to support this scenario always assume that the SPA's static content is served directly from NGINX and thus try_files is a viable option.
How is it possible to forward any unknown URLs to the SPA so that it can handle them itself?

The solution that works for me is to add the directives proxy_intercept_errors and error_page to the location / in NGINX:
server {
listen 80;
...
location / {
proxy_pass http://spa-server/;
proxy_intercept_errors on;
error_page 404 = /index.html;
}
location /other/ {
proxy_pass http://other/;
}
...
}
Now, NGINX will return the /index.html i.e. the SPA from the spa-server whenever an unknown URL is requested. Still, the URL is available to Angular and the router will immediately resolve it within the SPA.
Of course, now the SPA is responsible for handling "real" 404s. Fortunately, this is not a problem and a good practice within the SPA anyway.
UPDATE: Thanks to #dan

Related

Insecure forms and proxy servers?

I have a web application written in Apache Tomcat 8.5 that is proxied behind NGINX. i.e. I am using NGINX to offload SSL and serve static images etc. The app has been working reliably for years.
Now, the Chrome 87 update is causing a warning "The information that you’re about to submit is not secure" on every form submission. I've gone through the code with a fine-toothed comb and I can't figure out what could be triggering it.
The user gets to NGINX on https and the certificate is valid. NGINX forwards the request to Tomcat on port 8080. See config below.
The forms are submitted on the tomcat server as HTTP. But NGINX should prevent the browser from knowing that. It's https as far as the browser knows...
All tags are written as relative links or implied to be the same URL. e.g.
<form action="/login/login.do" method="post"> or <form method="post">.
Can anyone please point out something to look for? Am I missing a header or something?
Thanks in advance
from NGINX conf.d/site.conf:
location ~ \.(do|jsp)$ {
proxy_pass http://127.0.0.1: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;
}
Seems like there was a change in Chrome 87 to give warnings for mixed forms, so that is probably why those errors are appearing.
Perhaps there are some stray absolute links within your application which are still http, and are not being automatically converted when proxied by nginx?
If you are sure all your content is served over https, you can try enabling this header Content-Security-Policy: upgrade-insecure-requests (more info here) to force browsers to upgrade insecure connections automatically.
Had a similar issue, and in my case was the response from my app server being a redirect to a different scheme (http) than the one used by the client (https).
If it's your case as well, adding this to your location definition should do the trick. Assuming your app/app server respects this header, then it should respond with the proper scheme (https) on the Location header.
proxy_set_header X-Forwarded-Proto $scheme;
For completeness, excerpt for X-Forwarded-Proto from MDN docs:
The X-Forwarded-Proto (XFP) header is a de-facto standard header for identifying the protocol (HTTP or HTTPS) that a client used to connect to your proxy or load balancer.

Setting custom Request Headers through nginx ingress controller

I have a kubernetes cluster using nginx controller to proxy requests to the backend. There is an LB in the front.
LB <-> Nginx Ingress <-> WLS in K8s
When I terminate the SSL at the LB, and the backend sends a redirect it will send the redirect with location that starts with http. However, WebLogic recognizes WL-PROXY-SSL request header to send a https redirect.
I am trying to set the request header on the Nginx Ingress controller for a specific URL patterns only.
Tried using
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header WL-PROXY-SSL: "true";
It didn't work.
Even tried ....
more_set_headers "WL-PROXY-SSL: true";
nginx.org/location-snippets: |
proxy_set_header "WL-PROXY-SSL: true";
Also tried the custom-headers module but it sets for all resources. While I see the entry in the nginx.conf, it is not taking effect even with global custom-headers configMap also.
Is there any good example of adding this header to the request ?
Thanks in advance.

Preserve base URL when loading assets on proxied web page

I'm trying to host a site (called site1) nested within an existing domain (www.gateway.com).
e.g. Instead of www.site1.com/profile, it would be www.gateway.com/site1/profile.
I have an NGINX reverse proxy that detects the /site1/ path and proxies it to some upstream machines:
location ~/site1/(.*)$ {
proxy_pass http://upstreams/$1$is_args$args;
proxy_set_header Host $host;
}
The proxy itself is working fine - it redirects all the paths correctly. However, the site's assets (e.g. JS, CSS, etc.) do not preserve the base path (www.gateway.com/site1).
e.g. It is trying to load www.gateway.com/normalize.css, when the actual asset lives at www.gateway.com/site1/normalize.css.
For reference, the HTML for site1 is sourcing assets like so:
<link href="/normalize.css" rel="stylesheet" />
I've also tried removing the leading / in the href, but this results in the asset's path including the full route (less the last fragment) - also not what is desired.
Note that site1 works fine when hosted at the root of a domain (e.g. www.gateway.com/profile).
Any insights would be helpful. Thanks!
You might already have a block that checks for static fields that is messing up your asset delivery. To me what you are doing seems fine, but you have other nginx asset code possibly either on the upstream or site1 messit up. Add a block that checks for static files. If you are only doing this for one or two sites, this is reasonable.
The code below should work until you get us more info on other asset code.
location ~/site1/(.*)$ {
location ~* \.(?:js|css|jpg|jpeg|gif|png|ico|cur|svg)$ {
alias /location/of/site1;
expires 1M;
access_log off;
sendfile on;
sendfile_max_chunk 1m;
add_header Cache-Control public;
}
location ~* {
try_files $uri #nonStatic;
}
}
location #nonStatic {
proxy_pass http://upstreams/$1$is_args$args;
proxy_set_header Host $host;
}

Catch-all configuration except for www.website.com?

Using Nginx, I'm trying to configure my server to accept all domains that point to the IP of my server, by showing them a specific website, but when accessing the www.example.com (main website), I'd show an other content.
Here's what I did so far:
server {
// Redirect www to non-www
listen 80;
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
listen 80;
server_name example.com;
// rest of the configuration
}
server {
// Catch all
listen 80 default_server;
// I also tried
// server_name _;
// Without any luck.
// Rest of the configuration
}
The problem with this configuration is that every request made to this server not being www.example.com or example.com is took under example.com server configuration, not the catch all.
I'd like to cath only www.example.com/example.com in the first two configurations, and all the others in the last configuration.
I suggest putting your server on top of the file :)
I think nginx wants default servers to be on top of -a- file.
I have really much files on my server, but there is one with a default server as first server declaration, and that works.

nginx allow all doesn't work

I am trying to allow pinterest to access to my dev site's images, currently nginx deny.conf is using auth_basic and a list of allow IPs. There is no deny all in there. satisfy any is also in deny.conf
I added allow allto my site's config and restarted/reloaded nginx but still getting access denied from pinterest.
location ^~ ^/(cache|media|static)/ {
allow all;
access_log off;
expires 1y;
}
Any ideas?
try putting satisfy any; in your configuration. That tells nginx to accept either http authentication, or IP restriction. By default, when you define both, it will expect both.
http://wiki.nginx.org/HttpCoreModule#satisfy