Serving two sites from one server with Nginx - proxy

I have a Rails app up and running on my server and now I'd like to add another one.
I want Nginx to check what the request is for and split traffic based on domain name
Both sites have their own nginx.conf symlinked into sites-enabled, but I get an error starting nginx Starting nginx: nginx: [emerg] duplicate listen options for 0.0.0.0:80 in /etc/nginx/sites-enabled/bubbles:6
They are both listening on 80 but for different things.
Site #1
upstream blog_unicorn {
server unix:/tmp/unicorn.blog.sock fail_timeout=0;
}
server {
listen 80 default deferred;
server_name walrus.com www.walrus.com;
root /home/deployer/apps/blog/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #blog_unicorn;
location #blog_unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://blog_unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
Site two:
upstream bubbles_unicorn {
server unix:/tmp/unicorn.bubbles.sock fail_timeout=0;
}
server {
listen 80 default deferred;
server_name bubbles.com www.bubbles.com;
root /home/deployer/apps/bubbles/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #bubbles_unicorn;
location #bubbles_unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://bubbles_unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}

The documentation says:
The default_server parameter, if present, will cause the server to become the default server for the specified address:port pair.
It's also obvious, there can be only one default server.
And it is also says:
A listen directive can have several additional parameters specific to socket-related system calls. They can be specified in any listen directive, but only once for the given address:port pair.
So, you should remove default and deferred from one of the listen 80 directives. And same applies to ipv6only=on directive as well.

Just hit this same issue, but the duplicate default_server directive was not the only cause of this message.
You can only use the backlog parameter on one of the server_name directives.
Example
site 1:
server {
listen 80 default_server backlog=2048;
server_name www.example.com;
location / {
proxy_pass http://www_server;
}
site 2:
server {
listen 80; ## NOT NOT DUPLICATE THESE SETTINGS 'default_server backlog=2048;'
server_name blogs.example.com;
location / {
proxy_pass http://blog_server;
}

I was having the same issue. I fixed it by modifying my /etc/nginx/sites-available/example2.com file. I changed the server block to
server {
listen 443 ssl; # modified: was listen 80;
listen [::]:443; #modified: was listen [::]:80;
. . .
}
And in /etc/nginx/sites-available/example1.com I commented out listen 80 and listen [::]:80 because the server block had already been configured for 443.

Related

How to alter existing nginx config into a reverse proxy for multiple local machines with working lets-encrypt certificate

Let me preface this by saying most of my "experience" comes from blindly copypasting config lines from various blogs and sites like this and hoping for the best.
Currently I have a setup like this:
Dynamic ddns hostname pointed at my public IP redirecting incoming traffic from ports 80 and 443 to a LXC container with nextcloud + certificate from letsencrypt for the same ddns hostname. So far so good.
this is the relevant nginx config:
server {
server_name stats;
listen 9753 default_server;
listen [::]:9753 default_server;
location /nginx-status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow ::1;
deny all;
}
location ^~ /.well-known/acme-challenge {
proxy_pass http://127.0.0.1:81;
proxy_set_header Host $host;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
server_name nextcloud;
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
client_max_body_size 10240M;
root /var/www/nextcloud/;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
location / {
rewrite ^ /index.php;
}
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ^~ /apps/rainloop/app/data {
deny all;
}
location ~ \.(?:flv|mp4|mov|m4a)$ {
mp4;
mp4_buffer_size 100M;
mp4_max_buffer_size 1024M;
fastcgi_split_path_info ^(.+?.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
include php_optimization.conf;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+).php(?:$|\/) {
fastcgi_split_path_info ^(.+?.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
include php_optimization.conf;
}
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}
location ~ .(?:css|js|woff2?|svg|gif|map|png|html|ttf|ico|jpg|jpeg)$ {
try_files $uri /index.php$request_uri;
access_log off;
expires 30d;
}
}
My original vision for altering this was to use nextcloud.ddns.net to access my nextcloud as before, but also be able to reverse proxy to different local machines via nextcloud.ddns.net/whatever, nextcloud.ddns.net/something etc. Why? Because in my absolute failure of understanding the underlying technology I envisioned this would keep using the already valid ssl certificate for nextcloud.ddns.net without me having to obtain a new certificate for each destination. Does it work like this? I still do not know, but that didn't stop me from trying. I tried including a new location /whatever directive in various places, but all I achieved was a) it not working at all, b) redirecting me to the original nextcloud, c) only working while connected to local lan.
Seeing as I wasn't getting anywhere I went the other way and registered another ddns hostname, pointed that at the same public IP and included this block at the top of the nginx.conf:
server {
listen 443 ssl;
server_name other_hostname.ddns.net;
location / {
proxy_pass http://different_local_machine.lan/;
}
}
This works but obviously complains about the certificate being issued to nextcloud.ddns.net not to other_hostname.ddns.net
Onto my questions then:
Is it at all possible to set it up as I originally thought of with using 1 ddns hostname with different /suffixes or is this not how it works at all?
How would I go about getting multiple lets-encrypt certificates in the working scenario with multiple ddns hostnames? I'm worried if I follow the same instructions as I did to get the cert for the nextcloud I'll end up messing that, as that is still the only internet-facing nginx.
How "safe" would I be in just ignoring the warning? I mean I know the certificate is for different hostname, but I actually know it's still a valid certificate.
Again I apologise for my technical shortcomings, it took me few days to find out that what I want to achieve is called reverse proxy and it didn't improve much from there, but I think what I want to achieve should be possible with help from internet strangers without me having to complete a semester of Computer Science
Thanks for any help!
Here is an example that can be used to handle this ... you may need to tweak the setup for your own needs ...
I use this in an nginx docker that is networked to two containers
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
In the /etc/nginx/conf.d/ directory resides the configuration files for each container ..
-- in site1.conf --
upstream production{
server container_name1:80;
}
server {
server_name site1.com;
location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
add_header Access-Control-Allow-Origin *;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://production/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/site1.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/site1.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 = site1.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name site1.com;
listen 80;
return 404; # managed by Certbot
}
-- in site2.conf --
upstream production_admin{
server container_name2:80;
}
server {
server_name admin.site1.com;
location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
add_header Access-Control-Allow-Origin *;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://production_admin/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/site1.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/site1.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 = admin.site1.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name admin.site1.com;
listen 80;
return 404; # managed by Certbot
}
Upstream will set the name that is used in the proxypass and just serve off port 80 for server. This should get you started
For what it's worth if anybody is as hopeless as I am and has this exact same problem, I managed to discover solution on another forum. 1 freaking line, that was it. proxy_set_header Referer $http_referer; What does it do? How should I know? It makes my stuff work as I want though and that's all I care about.
So the full working location block looks like:
location ~ /something {
proxy_pass http://somehost.lan:someport;
proxy_set_header Referer $http_referer;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
}
discussion that led me to the "discovery" : https://unix.stackexchange.com/questions/290141/nginx-reverse-proxy-redirection Bottom post, 1st comment.

Nginx redirect only specific paths of a server from http to https

I want to redirect the paths from http to https like the following:
http://localhost:80/ to same Http url
http://localhost:80/api/ to https://localhost:80/api/ which in turn redirects to https://localhost:55555/api/
I have a configuration file:
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80;
listen [::]:80;
server_name localhost;
return 301 https://$host$request_uri;
}
# HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate certs/myservice.crt;
ssl_certificate_key certs/myservice.key;
server_name myservice.com localhost;
location /api/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_pass https://localhost:55555/api/;
client_max_body_size 500G;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 3600;
send_timeout 300;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:80/;
client_max_body_size 500G;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 3600;
send_timeout 300;
}
location ~ /\.ht {
deny all;
}
}
}
When i tried with this, the second requirement was fulfilled. But the first one of keeping http://localhost:80/ the same fails. It is unnecessarily redirected as https://localhost .
In short, nginx redirects all the HTTP requests coming to port 80 on the localhost server to HTTPS.
I also tried removing the location / { } section from second server block.
Then tried specifying in fist server block as:
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
proxy_pass http://localhost:80/
}
location /api/ {
return 301 https://$host$request_uri;
}
}
Both of them didn't worked.
What is the correct way of redirecting only specific paths of a server from http to https in Nginx?
This section in the second server block won't work. Because it again redirects to https.
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:80/;
client_max_body_size 500G;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 3600;
send_timeout 300;
}
So exposed that application to a different host port other than 80, say 88. Then changed this proxypass URL to:
proxy_pass http://localhost:88/;
Now it works fine.

Redirect all HTTP traffic to HTTPS seems to be impossible

I am posting the question because the previous attempts have proved to be futile.
I have a rails server using nginx, and I am trying to redirect all http traffic to https.
Here is my nginx.conf file:
upstream backend {
server unix:PROJECT_PATH/tmp/thin1.sock;
server unix:PROJECT_PATH/tmp/thin2.sock;
server unix:PROJECT_PATH/tmp/thin3.sock;
server unix:PROJECT_PATH/tmp/thin4.sock;
server unix:PROJECT_PATH/tmp/thin5.sock;
server unix:PROJECT_PATH/tmp/thin6.sock;
server unix:PROJECT_PATH/tmp/thin7.sock;
server unix:PROJECT_PATH/tmp/thin8.sock;
}
server {
listen 80 default_server;
listen 443 default_server ssl;
server_name app_name;
ssl_certificate path_to_certificate_file.crt;
ssl_certificate_key path_to_certificatefile.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
root PATH_TO_PUBLIC_FOLDER;
access_log path_to_project/log/access.log;
error_log path_to_project/log/error.log;
client_max_body_size 10m;
large_client_header_buffers 4 16k;
location /ping {
echo "pong"
return 200;
}
# Cache static content
location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|swf|wav)$ {
expires max;
log_not_found off;
}
# Status, local only (accessed via ssh+wget)
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
# double slash removal
set $test_uri $host$request_uri;
if ($test_uri != $host$uri$is_args$args) {
rewrite ^/(.*)$ /$1 break;
}
location / {
if ($http_x_forwarded_proto = 'http') {
return 301 https://$server_name$request_uri;
}
try_files $uri #proxy;
}
location #proxy {
proxy_redirect off;
# Inform we are on SSL
proxy_set_header X-Forwarded-Proto https;
# force timeouts if one of backend is died
proxy_next_upstream error timeout invalid_header http_502 http_503;
# Set headers
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://backend;
}
error_page 500 502 503 504 /500.html;
}
The current configuration causes:
400 Bad Request The plain HTTP request was sent to HTTPS port
You may notice the /ping location. That's because I have the servers behind a GCE balancer that performs a health check, and this is THE ONLY one I do not want to redirect. Everything else should be redirected to HTTPS.
Previous attempts:
server {
listen 80;
server_name app_name;
location /ping {
echo "pong";
return 200;
}
location / {
return 301 https://$server_name$request_uri;
}
}
With the https server part like the current config (with listen 80 default_server commented). This causes a too many redirections error.
I tried to simply redirect ALL traffic to https, including the health check. GCE expects a 200 response and instead it gets a 301, thus marking the machine as unhealthy and rendering the application useless.
I also tried the ssl on; on the https server config, same result (400)
I also tried to toggle the config.force_ssl = true in the rails project to no avail. Every other solution I try fails too.
Did anyone stumble on this also?
It seems the problem was not the Nginx config, but the certificates.
Putting a valid certificate led me to create an https backend and health check. Everything is working fine now.

Nginx increased response time after redirecting naked domains

I've experienced this strange increase in endpoint latency after redirecting naked domains to their www. counterpart. I eventually rolled back.
(the different lines are different locations that the endpoint is tested from)
The change in the nginx.conf consisted in creating the following lines for each hosted domain (6 domains in total):
server {
listen 80;
server_name example.com;
return 301 http://www.example.com$request_uri;
}
server {
listen 80;
server_name www.example.com;
root /usr/share/nginx/www;
access_log /var/log/nginx/access.log log_format;
location = /favicon.ico {
root /home/www/example;
}
location = /robots.txt {
root /home/www/example;
}
location /static {
root /home/www/example;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
While before I had something like:
server {
listen 80; ## listen for ipv4; this line is default and implied
#listen [::]:80 default_server ipv6only=on; ## listen for ipv6
root /usr/share/nginx/www;
#index index.html index.htm;
# Make site accessible from http://localhost/
# server_name localhost;
access_log /var/log/nginx/access.log log_format;
location = /favicon.ico {
root /home/www/example;
}
location = /robots.txt {
root /home/www/example;
}
location /static {
root /home/www/example;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Any ideas why this is happening? Thanks!

block direct access on port 8080

I have an app running on a service, behind a nginx server, using unicorn.
If I access http://server.com I get the app, up and running...But I still can access app on port 8080, like http://server.com:8080 but this time, without assets (which are beign served by nginx)
How do I block direct access to port 8080 on my prod. server?
The server is an Ubuntu 12.04
nginx.conf
upstream unicorn {
server 127.0.0.1:8080;
}
server {
listen 80 default deferred;
# server_name example.com;
root /home/deploy/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #unicorn;
location #unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
Make unicorn and nginx use a domain socket. For nginx:
upstream unicorn {
server unix:/path/to/socket fail_timeout=0;
}
Then pass '-l /path/to/socket' to unicorn, or alter your unicorn config file:
listen '/path/to/socket'

Resources