Serve single file Angular app from S3 with nginx - angularjs

I have an Angular app that consists of a single index.html file. The only others files are main.css and some image assets. I've already rediscovered there is no way to use S3 web hosting to serve it so I'm trying to set up nginx as a proxy. I have done this before but it was years ago and it wasn't with an Angular app and HTML5 push state. Here is the current nginx config server block I have.
server {
server_name foo.com;
set $s3_bucket 'foo.com.s3.amazonaws.com';
proxy_http_version 1.1;
proxy_set_header Host $s3_bucket;
proxy_set_header Authorization '';
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
proxy_hide_header Set-Cookie;
proxy_ignore_headers "Set-Cookie";
proxy_buffering off;
proxy_intercept_errors on;
resolver 172.16.0.23 valid=300s;
resolver_timeout 10s;
location ~* ^/(assets|styles)/(.*) {
set $url_full '$1/$2';
proxy_pass http://$s3_bucket/live/$url_full;
}
location / {
rewrite ^ /live/index.html break;
proxy_pass http://$s3_bucket;
}
}
I don't assume anything with this config. It could all be completely wrong.
It does "work". I can go to foo.com and the site serves and I can navigate and it all work wonders. But it won't load any URL that is not /. All other redirect to / and that is a problem.
What am I doing wrong? All help appreciated.

Related

How to delpoy drf static files and react app nginx config

I have a simple nginx config. But when I uncomment Django static path for admin react starts return 404 and can't find its main.js files. React serves with serve -s build from their docs. Please, help.
server {
listen 80;
server_name server_ip;
location / {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://localhost:3000;
}
#location /static/ {
# root /var/www/html;
#}
location ~* ^/(api|path/admin) {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
Instead of just having var/www/html which is really anything in the root directory because I think that can create conflicts. because nginx is trying to find the build folder inside of the static folder which is not true.
I think if you copied the build folder in the /static I think it would work.
I have not really gone through this kind of issue before but I hope it helps.

nginx config for react Single Page Apps (SPA) hosted on s3 not working

I am trying to get nginx with single page apps hosted on s3 folders to work. For master pushes we use the regular cloudfront, s3 and route 53. But for branch deployments we do not want cloudfront for each developer. So what we do is push the assets to s3 bucket my.example.com/branch-name/ and we create a route 53 A record - branch-name.my.example.com which points to the nginx server.
Here is my nginx config :
server {
listen 8443;
server_name *.my.example.com;
index index.html;
location / {
resolver 8.8.8.8;
set $bucket "my.example.com.s3-website-us-west-1.amazonaws.com";
rewrite ^([^.]*[^/])$ $1/ permanent;
# matches: branch-name
if ($host ~ ^([^.]*)\.my\.example\.com) {
set $branch $1;
proxy_pass http://$bucket/${branch}$uri;
}
try_files '' /index.html =404;
proxy_intercept_errors on;
proxy_redirect off;
proxy_set_header Host $bucket;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
}
}
The behaviour I am looking for is if I go to http://branch-name.my.example.com it should load the http://$bucket/${branch}/index.html.
If I go to http://branch-name.my.example.com/garbage-nonexistent-path/more-garbage/... it should preserve the url in the browser but still load http://$bucket/${branch}/index.html
If I go to http://branch-name.my.example.com/correct-path/ it should be routed to the correct tab in my app as react router has a component for /correct-path.
If I go to http://branch-name.my.example.com/correct-path/sdcsd/sdcsd/ it should still route to the correct page but preserve the url.
Right now the behaviour I get is I can only go to the root url i.e http://branch-name.my.example.com and I get routed to the proper index.html and then I click on tab "correct-path" on that page and the url will change accordingly http://branch-name.my.example.com/correct-path/ and its fine.
But if I try to go to http://branch-name.my.example.com/correct-path/ directly I just get the following error from s3:
404 Not Found
Code: NoSuchKey
Message: The specified key does not exist.
Key: branch-name/correct-path/index.html
RequestId: 92ADA29566570E8C9
HostId: wT4JdQrkB7KI0+Gnb4+88WNdnX0NXCHB6/KP5RxqJMozAx8z3RlC4T6uefk2LA=
An Error Occurred While Attempting to Retrieve a Custom Error Document
Code: NoSuchKey
Message: The specified key does not exist.
Key: index.html
If I go to http://branch-name.my.example.com/correct-path (note there is no ending slash), it just keeps loading indefinitely.
Please help. Thanks.
Try this config
server {
listen 80;
location = / {
proxy_pass http://bucketname-ap-southeast-2.amazonaws.com;
proxy_intercept_errors on;
error_page 404 =200 #spa-root;
}
location / {
proxy_pass http://bucketname-ap-southeast-2.amazonaws.com;
proxy_intercept_errors on;
error_page 404 =200 #spa-root;
}
location #spa-root {
proxy_pass http://bucketname-ap-southeast-2.amazonaws.com;
}
}

Deploy express server that serves react behind nginx

I have an express server on an ec2 instance that has an api (/api) and a client (everything that's not /api handled in react.)
Goging to http://ip.address:3000 works fine. It shows the app and everything works.
However going to https://ip.address (forwarded by nginx) doesn't work fine. It loads my index.html correctly but 404s on all the /static/bundle.js and /static/bundle.css files.
nginx
# redirect to node for the dynamic stuff
location / {
proxy_pass https://localhost:8003/index.html;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_hide_header X-Powered-By;
}
express
let router: express.Router = express.Router();
router.use(express.static(path.join(__dirname, "/build")));
const api: ApiRouter.Api = new ApiRouter.Api();
router.use("/api", api.router.bind(api.router));
//Catch all for react - client side routing
router.get("*", function(req, res) {
res.sendFile(path.resolve(__dirname, "/build/index.html"));
});
If you tell Nginx to
proxy_pass https://localhost:8003/index.html;
Then every request which matches that location block will have the matching part of its URL path replaced with whatever path you have added to your proxy_pass URL.
Your location block is simply / So if I request www.yoursite.com/static/bundle.js then Nginx will match the first /, replace it with your path and add the rest. So it will try and serve www.yoursite.com/index.htmlstatic/bundle.js
You should proxy /api through Nginx too, and if the static files are coming from an upstream proxy you should enable proxy caching within Nginx too

Nginx conf for prerender + reverse proxy to django + serving angular in html5 mode

The mouthful of a title says it all:
We've got an Angular frontend with a Django backend providing a REST API that is exposed independently at endpoints example.com/api/v1/*
The Angular app runs in HTML5 mode, and we want hard-links to example.com/foo/bar to bring users into the app at the foo.bar state as if it were a static page rather than an app state (where foo is anything but api).
We're running behind nginx,and our basic strategy in the conf was to define locations at ^~ /scripts, /images etc. for serving static content directly, as well as a ^~ /api/* location that gets routed to django. Below that, we have a location ~ ^/.+$ that matches any path not matched by any of the above and "sends it to Angular" - i.e. serves our index page to it and appends the path to the base url, allowing our angular router to handle it from there.
This is our conf in full:
upstream django {
server 127.0.0.1:8000 fail_timeout=0;
}
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443;
server_name example.com;
client_max_body_size 10M;
ssl on;
ssl_certificate /etc/ssl/thawte/example_com.crt;
ssl_certificate_key /etc/ssl/thawte/example_com.key;
ssl_verify_depth 2;
gzip on;
gzip_types text/plain text/html application/javascript application/json;
gzip_proxied any;
index index.html;
location ^~ /index.html {
gzip_static on;
root /www/dist;
}
location ^~ /images/ {
expires max;
root /www/dist;
}
location ^~ /scripts/ {
expires max;
gzip_static on;
root /www/dist;
}
location ^~ /favicon.ico {
expires max;
root /www/dist;
}
location ^~ /api {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
proxy_pass http://django;
}
//Send anything else to angular
location ~ ^/.+$ {
rewrite .* /index.html last;
}
}
This has worked perfectly for us, but we now need to set it up to work with prerender.io. We've tried doing this several ways, making modifications on the official prerender nginx example, but none have worked - crawlers are getting the same code users are rather than cached pages.
How can we get this working?
(note: this is new territory for everyone involved here, so if the best way to handle this involves making different choices a few steps back, please suggest them)
So it turns out the config posted above was working the whole time.
I realized this when it finally occurred to me to try putting https://example.com/anything through the crawler debugger (instead of https://example.com, which is all I had been testing previously), and it worked - the crawler was served the cached page as expected.
This was simply because the greedy quantifier in:
location ~ ^/.+$ {
did not match the empty string. With an additional
location = / {
try_files $uri #prerender;
}
, my conf is working as expected.
Hopefully the handprint on my forehead d only been putting https://example.com through the crawler debugger - which was not working.
On the upside, I'm thinking I can turn this handprint on my forehead into a nice Halloween costume next weekend....
Still not sure I've gone about this the best way, and welcome alternative suggestions.

Angular route without hash

I have an app where the admin side is built in angular, but the front consumer facing side is not.. and lives on a different server.
I'd like if visitors coming to:
domain.com/#/admin => angular app
But if they got to:
domain.com
This goes to the other server
Is this possible?
Thanks.
For a little more clarity, I think what I need is this:
I have a rails app as an api that angular consumes. Rails lives on server A and Angular lives on server B.
Both servers use nginx as the web server.
Also, the rails app serves up its own content, so I want any path other than admin to go to rails.
What I want is if users go to:
mydomain.com --> they hit the rails app and content on server A
when they go to:
mydomain.com/admin or mydomain.com/#/admin --> they hit the angular app on server B
I think I almost figured it out with the following nginx configs on the rails server, server A:
upstream serverA {
server rails;
server unix:///var/www/rails/shared/tmp/sockets/puma.sock;
}
upstream serverB {
server angular;
}
server {
listen 80;
server_name serverA;
root /var/www/rails/current/public;
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://serverA;
}
location ~* ^/assets/ {
expires 1y;
add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
break;
}
location /admin/ {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://serverB/#/;
}
}
While this gets me close, my angular scripts and styles that are references by my angular index.html file are not being found.

Resources