Skip to content

Instantly share code, notes, and snippets.

@bato3
Last active March 23, 2022 09:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bato3/00f86b02ceb0f03bfd187f61ad8f4fb9 to your computer and use it in GitHub Desktop.
Save bato3/00f86b02ceb0f03bfd187f61ad8f4fb9 to your computer and use it in GitHub Desktop.
SvelteKit `adapter-node` self hosted

Do I need to set nginx and proxy or there is a better way to directly running build without giving :3000?

No, you can use also Apache or Caddy xD

The Node http server takes a lot of effort to do what classic web servers like nginx/...

For example: request queuing. There are no restrictions in Node. It opens new connections until the server use all resources. Also I have experienced problems with keep-alive header. And HTTPS should be provided...

Of course, this can be handled directly in Node.

And CORS. And some deny IPs. And some rate-limiting for IP...

You can reinvent the wheel, and you can use best practices from the web server. BTW: remember that Node is single-threaded and Node's handling of it is inefficient.

Is reverse-proxy the only thing I need?

Of course not. It is good practice to provide 2 things:

  1. Keep your app running in background.
  2. Serve static files outside node.

Why shouldn't you use Node to serve static files? (#2)

Because the web server is better at it. Thanks to this, you can get a few percent increase in performance.

Running in background? (#1)

Yes, You must have something that will start your application after server restart. Or restart app after fatal-error. You can:
  1. create systemd service
  2. use some node manager like: pm2 or fotrever (Is it still maintained?)
  3. I'm prefer Passenger

SSL?

  1. Buy
  2. https://letsencrypt.org/
  3. Caddy
  4. Full secure your service by Cloudflare. (DDOS protection + CDN + WAF + SSL + ...)
# nginx rate limiter https://www.nginx.com/blog/rate-limiting-nginx/
limit_req_zone $binary_remote_addr zone=api:10m rate=2r/s;
# private localhost server
server {
# passenger_friendly_error_pages on;
listen 81;
listen [::]:81;
root /var/www/api2/public;
server_name _;
allow 127.0.0.1;
allow ::1;
deny all;
passenger_app_env production;
passenger_app_group_name "api2 - Wersja API w Node (TypeORM)";
passenger_group www-data;
passenger_nodejs /home/node14/.nvm/versions/node/v14.16.1/bin/node;
passenger_user node14;
error_log /var/log/nginx/api2.localhost.error.log;
access_log /var/log/nginx/api2.localhost.access.log;
passenger_sticky_sessions on;
passenger_enabled on;
passenger_app_type node;
passenger_startup_file /var/www/api2.shinden.pl/app.js;
passenger_force_max_concurrent_requests_per_process 20;
}
server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443;
include snippets/snakeoil.conf;
ssl_protocols TLSv1.2;
root /var/www/example.com/build;
server_name example.com;
access_log /var/log/nginx/example.com.access.log;
# passenger_friendly_error_pages on; # see what you wrong configure
# serve static diles through nginx. (It's example for `next`)
location _next/static {
alias /var/www/example.com/build/static;
expires 30d;
access_log on;
}
set $cors 0;
if ($http_origin ~ '^https?://(localhost(:[35]000)|([a-z\d]+\.)?example\.(pl|com|org))') {
set $cors 1;
}
if ($cors) {
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,Cookie' always;
# required to be able to read Authorization header in frontend
#add_header 'Access-Control-Expose-Headers' 'Authorization' always;
}
if ($request_method = 'OPTIONS') {
# Tell client that this pre-flight info is valid for 20 days
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# connect another app to route
location /api2/ {
limit_req zone=api;
proxy_pass http://localhost:83/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# use this backend istead main app. see Location priority https://stackoverflow.com/questions/5238377/nginx-location-priority
location ~ ^/api/v2/(.+)$ {
proxy_pass http://localhost:81/$1$is_args$args;
}
passenger_app_env production;
passenger_app_group_name "example.com (app)"; # unique name
passenger_group www-data; # www user group
# I created user for each node version and install `node` through `nvm`
passenger_user node10;
passenger_nodejs /home/node10/.nvm/versions/node/v10.24.1/bin/node;
passenger_sticky_sessions on;
passenger_enabled on;
passenger_app_type node;
passenger_startup_file /var/www/example.com/build/index.js;
passenger_force_max_concurrent_requests_per_process 20;
}
#!/bin/bash
# Location of the nginx config file that contains the CloudFlare IP addresses.
CF_NGINX_CONFIG="/etc/nginx/conf.d/cloudflare.conf"
# The URLs with the actual IP addresses used by CloudFlare.
CF_URL_IP4="https://www.cloudflare.com/ips-v4/"
CF_URL_IP6="https://www.cloudflare.com/ips-v6/"
# Temporary files.
CF_TEMP_IP4="/tmp/cloudflare-ips-v4.txt"
CF_TEMP_IP6="/tmp/cloudflare-ips-v6.txt"
# Download the files.
if [ -f /usr/bin/curl ];
then
curl --silent --output $CF_TEMP_IP4 $CF_URL_IP4
curl --silent --output $CF_TEMP_IP6 $CF_URL_IP6
elif [ -f /usr/bin/wget ];
then
wget --quiet --output-document=$CF_TEMP_IP4 --no-check-certificate $CF_URL_IP4
wget --quiet --output-document=$CF_TEMP_IP6 --no-check-certificate $CF_URL_IP6
else
echo "Unable to download CloudFlare files."
exit 1
fi
# Generate the new config file.
echo "# CloudFlare IP Ranges" > $CF_NGINX_CONFIG
echo "# Generated at $(date) by $0" >> $CF_NGINX_CONFIG
echo "" >> $CF_NGINX_CONFIG
echo "# - IPv4 ($CF_URL_IP4)" >> $CF_NGINX_CONFIG
awk '{ print "set_real_ip_from " $0 ";" }' $CF_TEMP_IP4 >> $CF_NGINX_CONFIG
echo "" >> $CF_NGINX_CONFIG
echo "# - IPv6 ($CF_URL_IP6)" >> $CF_NGINX_CONFIG
awk '{ print "set_real_ip_from " $0 ";" }' $CF_TEMP_IP6 >> $CF_NGINX_CONFIG
echo "" >> $CF_NGINX_CONFIG
echo "real_ip_header CF-Connecting-IP;" >> $CF_NGINX_CONFIG
echo "" >> $CF_NGINX_CONFIG
echo "" >> $CF_NGINX_CONFIG
echo "geo \$realip_remote_addr \$cloudflare_ip {" >> $CF_NGINX_CONFIG
echo " default 0;" >> $CF_NGINX_CONFIG
awk '{ print " " $0 " 1;" }' $CF_TEMP_IP4 >> $CF_NGINX_CONFIG
awk '{ print " " $0 " 1;" }' $CF_TEMP_IP6 >> $CF_NGINX_CONFIG
echo " 192.168.0.2 1; #bato@home" >> $CF_NGINX_CONFIG
echo " 192.168.0.3 1; #bato@work" >> $CF_NGINX_CONFIG
echo " 127.0.0.1 1;" >> $CF_NGINX_CONFIG
echo "}" >> $CF_NGINX_CONFIG
# Remove the temporary files.
rm $CF_TEMP_IP4 $CF_TEMP_IP6
#cd /root/nginx/nginx && /usr/bin/make install
# Reload the nginx config.
/usr/sbin/service nginx reload
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment