I'm trying to show users visiting a wildcard subdomain a subfolder:
abc.example.com -> example.com/xyz
This NGINX server block code is working:
server {
# server name with regexp
server_name ~^(?<sub>[^.]+)\.example\.com$;
# this server catches all requests to xxxx.example.com
# and put "xxxx" to $sub variable
location / {
# finally we want to request different URI from remote server
proxy_pass http://localhost:8000;
# proxy_redirect will rewrite Location: header from backend
# or you can leave proxy_redirect off;
proxy_redirect http://localhost:8000 http://$sub.localhost:8000;
}
[certbot code]
}
(found in question 5249883).
But when replacing proxy_pass value "https://localhost:8000" with "https:localhost:8000/xyz", I get these errors and a blank page:
Uncaught SyntaxError: Unexpected token '<'
in both socket.io.js and commons.js.
The app I'm running on example.com is built with React/Gatsby. example.com/demo is working.
EDIT: I put the wrong error messages, those errors appeared when I tried something different.
The problem was (as I understand it now), that Gatsby hosts scripts at example.com/[script-address] and using NGINX proxy_pass, the script address is also changed to example.com/[subfolder]/[script-address].
The solution to this is to set the "path-prefix" value in gatsby.config as explained here: Gatsby documentation.
With doing that, I set a prefix for my complete application, which is not really what I want to do, as the main application is still hosted on example.com, I only want the subdomains to get passed to some subpages. (The subdomains are user created and served dynamically by the main application).
Surprisingly, both (main application and subdomains) work after changing the path prefix.
This seems only to work for the production build (you have to pass a flag when building), so I'm currently still not sure what to do while developing.
If you have an idea how I could solve that better, please message me :)
Thanks to Ivan and Richard for putting me on the right track!
EDIT: Asset prefixed would be the better way: https://www.gatsbyjs.org/docs/asset-prefix/
It's still ugly and I think there's a way to solve this via NGINX.
I still can't use the development build this way.
EDIT 2:
After I've been messing with this for 3 days now, I've again tried to find a similar question & got lucky: https://serverfault.com/questions/840654/nginx-map-subdomain-to-a-subdirectory-on-proxied-server
I've changed my code to:
location / {
proxy_pass http://localhost:8000/xyz$uri/;
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;
}
and it finally works :)
Related
Build is working perfectly on my local PC with pm2, no errors at all. Every page loads perfectly, there are no 404 or 500 errors in fetching files. It's great! This is EXACTLY how I want it to run.
But when I try and deploy this on Ubuntu with pm2 I am getting two sets of errors:
I'll put screenshots here:
https://i.imgur.com/IdnEH7r.png
Written form:
Script_app-a44cfb7405f734c3.js
Script_buildManifest.js
Script_ssgManifest.js
Script_middlewareManifest.js
(And others) all are giving me a 500 Internal Server Error no matter what I do.
Attempted Solutions
I've tried many approaches and all of them end with this error/failure when I am navigating to my deployed website.
Upload manually with filezilla.
Git clone from my repository, build on the server (no build errors) and then deploy with pm2. No errors with pm2 either! But then I am given 404/500 errors.
I've tried this in different folders, I've tried it with a host of different commands. I am completely out of ideas and I've uploaded and tried to get my files on there and install packages and more.
Nginx error?
This might be nginx error? But the nginx settings work perfectly fine for a brand new "npx create-next-app#latest" Following this exact tutorial to the letter: https://www.youtube.com/watch?v=x6ci2iCckWc&t=658s&ab_channel=DigitalCEO
My nginx file
"server {
server_name specialservername.com;
gzip on;
gzip_proxied any;
gzip_types application/javascript application/x-javascript text/css text/javascript;
gzip_comp_level 5;
gzip_buffers 16 8k;
gzip_min_length 256;
location /_next/static/ {
alias /var/www/frontend/.next/static/;
expires 365d;
access_log off;
}
#EDITS
location ~ ^/_next/static/(.*)$ {
root /.next;
try_files "/static/$1" "/server/static/o$1" #proxy_pass;
}
#END EDITS
location / {
proxy_pass http://127.0.0.1:3000; #change to 3001 for second app, but make sure second nextjs app starts on new port in packages.json "start": "next start -p 3001",
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
add_header Access-Control-Allow-Origin *;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/specialservername.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/specialservername.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host =specialservername.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name specialservername.com;
return 404; # managed by Certbot
}"
What I was Expecting
The NextJS build to be deployed on this server no different than it is on my local machine. On my local machine it's BEAUTIFUL!
If you're seeing an "Internal Server Error" when trying to access your Next.js application on Ubuntu with nginx, it's likely that there's an issue with your configuration.
Here are a few things you can try:
Check your nginx error logs: Look in your nginx error logs (typically located in /var/log/nginx/error.log) for any error messages that might indicate what's causing the issue.
Check your Next.js logs: You should also check your Next.js logs (usually located in the .next directory of your application) for any error messages that might indicate what's causing the issue.
Check your Next.js configuration: Make sure your Next.js configuration is set up correctly for production deployment. You should make sure that your next.config.js file has the necessary settings for production deployment, such as setting target: 'server', configuring your build options, and setting your asset prefix if necessary.
Check your environment variables: Make sure any environment variables that your application depends on are set correctly on your Ubuntu server.
Check for permission: Make sure file, build files on server has enough permissions.
Also if everything from above works fine than try dockerizing your application with nginx and run on local then simply mimic the same on server(ubuntu) that would definatly give you some clue.
and lastly, don't panic. 😃
I have docker swarm app with multiple services and have problems with one service which is a react app. My config looks like this:
location /web/ {
include /etc/nginx/proxy-options/proxy.conf;
set $webapp webapp;
proxy_cache_bypass $http_pragma;
proxy_pass http://$webapp$uri$is_args$args;
sub_filter 'action="/' 'action="/web/';
sub_filter 'href="/' 'href="/web/';
sub_filter 'src="/' 'src="/web/';
sub_filter_once off;
}
location /app-info/ {
include /etc/nginx/proxy-options/proxy.conf;
proxy_ssl_session_reuse off;
proxy_redirect off;
set $webapp webapp;
proxy_pass http://$webapp$uri/$is_args$args;
sub_filter 'action="/' 'action="/web/';
sub_filter 'href="/' 'href="/web/';
sub_filter 'src="/' 'src="/web/';
sub_filter_once off;
}
Webapp is the react application hosted in docker also served with nginx. One of it's pages is webapp/app-info.
When setup through reverse proxy when I try to access anything on webapp I just get white screen and in the console I can see these errors. At this moment I'm out of ideas what I'm doing wrong.
Refused to apply style from 'https://example.com/web/static/css/main.23d98db9.chunk.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
Now what I'm trying to achieve (beside just making /web work) is making only /app-info/ accessible from outside. I would like to (temporary) block any access to webapp, beside the requests to /app-info. Is that possible?
Edit: Forgot to mention - everything works perfectly fine with
location / {
include /etc/nginx/proxy-options/proxy.conf;
set $webapp webapp;
proxy_pass http://$webapp$uri$is_args$args;
proxy_redirect http://$webapp/ $scheme://$http_host/;
}
I may be twisting things about horribly, but... I was given a ReactJS application that has to be served out to multiple sub-domains, so
a.foo.bar
b.foo.bar
c.foo.bar
...
Each of these should point to a different instance of the application, but I don't want to run npm start for each one - that would be a crazy amount of server resources.
So I went to host these on S3. I have a bucket foo.bar and then directories under that for a b c... and set that bucket up to serve static web sites. So far so good - if I go to https://s3.amazonaws.com/foo.bar/a/ I will get the index page. However most things tend to break from there as there are non-relative links to things like /css/ or /somepath - those break because they aren't smart enough to realize they're being served from /foo.bar/a/. Plus we want a domain slapped on this anyway.
So now I need to map a.foo.bar -> https://s3.amazonaws.com/foo.bar/a/. We aren't hosting our domain with AWS, so I'm not sure if it's possible to front this with CloudFront or similar. Open to a solution along those lines, but I couldn't find it.
Instead, I stood up a simple nginx proxy. I also added in forcing to https and some other things while I had the proxy, something of the form:
server {
listen 443;
server_name foo.bar;
ssl on;
ssl_certificate /etc/pki/tls/certs/server.crt;
ssl_certificate_key /etc/pki/tls/certs/server.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
# Redirect (*).foo.bar to (s3bucket)/(*)
location / {
index index.html index.htm;
set $legit "0";
set $index "";
# First off, we lose the index document functionality of S3 when we
# proxy requests. So we need to add that back on to our rewrites if
# needed. This is a little dangerous, probably should find a better
# way if one exists.
if ($uri ~* "\.foo\.bar$") {
set $index "/index.html";
}
if ($uri ~* "\/$") {
set $index "index.html";
}
# If we're making a request to foo.bar (not a sub-host),
# make the request directly to "production"
if ($host ~* "^foo\.bar") {
set $legit "1";
rewrite /(.*) /foo.bar/production/$1$index break;
}
# Otherwise, take the sub-host from the request and use that for the
# redirect path
if ($host ~* "^(.*?)\.foo\.bar") {
set $legit "1";
set $subhost $1;
rewrite /(.*) /foo.bar/$subhost/$1$index break;
}
# Anything else, give them foo.bar
if ($legit = "0") {
return 302 https://foo.bar;
}
# Peform the actual proxy forward
proxy_pass https://s3.amazonaws.com/;
proxy_set_header Host s3.amazonaws.com;
proxy_set_header Referer https://s3.amazonaws.com;
proxy_set_header User-Agent $http_user_agent;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Accept-Encoding "";
proxy_set_header Accept-Language $http_accept_language;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
sub_filter google.com example.com;
sub_filter_once off;
}
}
This works - I go to a.foo.bar, and I get the index page I expect, and clicking around works. However, part of the application also does an OAuth style login, and expects the browser to be redirected back to the page at /reentry?token=foo... The problem is that path only exists as a route in the React app, and that app isn't loaded by a static web server like S3, so you just get a 404 (or 403 because I don't have an error page defined or forwarded yet).
So.... All that for the question...
Can I serve a ReactJS application from a dumb/static server like S3, and have it understand callbacks to it's routes? Keep in mind that the index/error directives in S3 seem to be discarded when fronted with a proxy the way I have above.
OK, there was a lot in my original question, but the core of it really came down to: as a non-UI person, how do I make an OAuth workflow work with a React app? The callback URL in this case is a route, which doesn't exist if you unload the index.html page. If you're going directly against S3, this is solved by directing all errors to index.html, which reloads the routes and the callback works.
When fronted by nginx however, we lose this error->index.html routing. Fortunately, it's a pretty simple thing to add back:
location / {
proxy_intercept_errors on;
error_page 400 403 404 500 =200 /index.html;
Probably don't need all of those status codes - for S3, the big thing is the 403. When you request a page that doesn't exist, it will treat it as though you're trying to browse the bucket, and give you back a 403 forbidden rather than a 404 not found or something like that. So in this case a response from S3 that results in a 403 will get redirected to /index.html, which will recall the routes loaded there and the callback to /callback?token=... will work.
You can use Route53 to buy domain names and then point them toward your S3 bucket and you can do this with as many domains as you like.
You don't strictly speaking need to touch CloudFront but it's recommended as it is a CDN solution which is better for the user experience.
When deploying applications to S3, all you need to keep in mind is that the code you deploy to it is going to run 100% on your user's browser. So no server stuff.
Imagine company ABC has two teams developing two SPA apps: app1, app2
each app has its own index.html and associated static assets, for example:
build/
index.html
main.js
This is run from: host1. app follows the same conventions
We would like:
abc.com/app1 to route to host1
abc.com/app2 to route to host2
Say there is a load balancer setup to perform the routing properly.
What solution do I have for this kind of situation?
I tried to use https://github.com/zeit/serve (suggested by create-react-app) but there are tons of problems.
First, rewritePath feature does not work (entirely fails to do anything useful)
Second, I tried to put my static assets 1 layer deeper on the host, the request host1/app1 is a directory listing rather than the index.html page
Even after solving these issues through configs, there are still a ton of issues with React Router (SPA router) and authentication callbacks
What is the actual best practice for this scenario? I imagine it is a very common scenario. As I can see AWS's web console uses a similar approach for routing apps
Basically you want a reverse-proxy like nginx to server two different sites at different URIs. The SPAs should not know about each other, because it should not concern them how they are reached, it's the job of the reverse proxy.
Basic config for nginx:
server {
listen 80;
listen [::]:80;
server_name abc.com;
location /app1 {
rewrite /app1(/?(.*)) /$2 break;
proxy_pass http://localhost:XXXX;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection "";
proxy_set_header Host $host;
}
location /app2 {
rewrite /app2(/?(.*)) /$2 break;
proxy_pass http://localhost:YYYY;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection "";
proxy_set_header Host $host;
}
}
Of course this config misses important stuff like ssl, gzip, caching and so on, but it should give you and idea.
In this example, the SPAs could run in two different docker containers, reachable by ports XXXX and YYYY.
Hope this helps,
lgG
What you’re looking for is a reverse proxy. any modern web server can do this including Nginx or Apache. The trick of it will be getting your rewrite routes just right which is what I suspect you’re running into with React.
I have nginx listening on port 80, and passing requests through to meteor (wraps node.js) on port 3000.
That is pretty easy to do, and I got it working.
However, I want nginx to ONLY process requests for valid virtual host domains. There are going to be lots, so I thought it would make sense to connect NGINX to PostgreSQL and store them there.
I compiled NGINX with this module: http://labs.frickle.com/nginx_ngx_postgres/ to achieve that.
Here is the nginx.conf stanza:
http {
upstream database {
postgres_server 127.0.0.1 dbname=some_name user=some_user password=some_password;
}
server {
location / {
postgres_pass database;
postgres_query "SELECT * FROM sites WHERE domain='$http_host'";
proxy_pass http://localhost:3000;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
At first I thought it was working fine, because I was getting the correct results for 3 test domains I added to the DB. But, then I removed one, and it still worked!
So, clearly, I've configured it wrong. This is a unique enough case, Google is not helping. :(
Anyone done this?
Thanks in advance!