why part of my http requests do not use http2 - nginx

Environment:
Ubuntu 16.04 LTS
Nginx version: nginx/1.10.3 (Ubuntu)
OpenSSL version: OpenSSL 1.0.2g 1 Mar 2016
Simple Description:
I have configured nginx to use http2, and some of my requests are using http2, however, some of my requests are using http1.1 instead.
Nginx Configuration:
I use exactly the same configuration as Mozilla generator suggest
server {
listen 80 default_server;
listen [::]:80 default_server;
# Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_certificate /path/to/signed_cert_plus_intermediates;
ssl_certificate_key /path/to/private_key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /path/to/dhparam.pem;
# intermediate configuration. tweak to your needs.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;
## verify chain of trust of OCSP response using Root CA and Intermediate certs
ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
resolver <IP DNS resolver>;
....
}
And my special configuration looks like:
server_name mydomain;
set $root myrootpath;
root $root;
location / {
try_files $uri /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:5000/api/;
proxy_redirect default;
}
location /page/ {
proxy_pass http://127.0.0.1:5000/page/;
proxy_redirect default;
}
More Details:
The http request to /api and /page can use http2 correctly, but when the browser request static resources like images or .js files under / or /static, it will use http1.1, I attached picture
after I clear the cache, and still those static resources are requested via http1.1. However, the response status code is 304 this time
I searched google and do not find the answer, people said OpenSSL and nginx version may cause http2 problem, but my version has no such problem, and it works for /api and /page route request. So what's the problem?

As #sbordet said the requests that were not served by HTTP/2 in the first screenshot were served from the disk cache and were presumably downloaded using HTTP/1.1 originally, hence why that’s what they show.
The requests that were not downloaded by HTTP/2 in the second screenshot were either the same or were 304 Not Modified requests - so the original version from the disk cache was loaded and it was presumably loaded from HTTP/1.1.
Clear your cache properly or, if using Chrome tick the “Disable cache” option in Developer Tools network and reload and they should all be loaded over HTTP/2.

Related

nginx redirecting all subdomains (when it shouldn't)

I have an nginx server running. I want it to redirect http://www.example.com to https://www.example.com, but not touch any other subdomains like http://foo.example.com.
For some reason, no matter what I add in the subdomain, it still gets rerouted. My webpage shows on www.example.com (as it should), but also on foo.example.com and example.com (as it shouldn't)
This is my example.com config file:
server {
listen 80;
server_name www.example.com;
# For debug
add_header X-debug-message "listen:80, server_name:www.example.com, redirect:https://$host$request_uri" always;
# Riderect
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name www.example.com;
# For debug
add_header X-debug-message "listen:443, server_name:www.example.com, redirected:https://$host$request_uri" always;
# SSL
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
root /var/www/example.com;
# Redirect
location / {
proxy_pass http://192.168.1.224:80;
}
}
Going to www.example.com shows me my webpage as expected. But going to foo.example.com also gives me my webpage - which it shouldn't. example.com also gives me the webpage.
Opening www.example.com in my browser, I see the following http header (as expected):
X-debug-message: DEBUG: listen:443, server_name:www.example.com, redirected:https://www.example.com/
Opening foo.example.com in my browser, I see the following http header (not as expected):
X-debug-message: DEBUG: listen:443, server_name:www.example.com, redirected:https://foo.example.com/
How can I make my nginx only redirect www.example.com ?
Ensure that the dns record for foo.yourdomain.com is actually created with
dns provider
Create a second server block for the subdomain 'foo.example.com'
otherwise all request to port 80
will be redirected to available server block, which in your case
www.example.com - the server block should look like this:
server {
server_name foo.example.com;
location / {
root path/to/foo/index.html;
index index.html index.htm;
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;
try_files $uri $uri/ /index.html;
}
listen 443 ssl; # managed by Certbot
ssl_certificate
/etc/letsencrypt/live/example.com/fullchain.pem; # n
managed by Certbot
ssl_certificate_key
/etc/letsencrypt/live/nextoma.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
}
Add ssl certificate to the foo.example.com with the command:
certbot --nginx -d foo.example.com
Restart nginx and recheck foo.example.com again
You need to make the first entry listen on 443 for HTTPS and server name _ and return 404.
server {
listen 443 ssl;
server_name _;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_certificate /etc/nginx/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/nginx/certs/nginx-selfsigned.key;
location / {
return 404;
}
}
By having the typical HTTP to HTTPS redirect in the file (I have it as the last entry):
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
Then all HTTP requests get converted to the HTTPS counterparts. Then, if you request a subdomain that has not been configured in the NGINX configuration file, it will default to the first entry which returns a 404. All other configured subdomains, and the root domain, if you have that as an entry, will resolve correctly.
Also you can keep your wildcard DNS, which is more practical than having to add each subdomain as an entry, as you point out in your answer.
Thank you for all the comments!
For other readers, and future reference, this is now my enlightened understanding.
nginx treats the first entry in it's enabled-sites conf as a default route. Thus, the first entry
server {
listen 80;
server_name example.net www.example.net;
...
}
is in fact treated as
server {
listen 80 default_server;
server_name example.net www.example.net;
...
}
So, my mistake, was to add *.example.com -> MyIP to my DNS, and assuming nginx would just 404 all routes I didn't explicitly define. When in fact, it looks for a route that matches foo.example.com, and if it doesn't, routes it to the default route.
So, I now changed my DNS to explicitly handle all subdomains I want routed, and I list all of them explicitly in nginx.
Now - how I achieve my original plan - to just route *.example.com to my IP, and have nginx 404 all requests except the ones I excplicitly define - I still don't understand.
Explicitly routing all subdomains in the DNS is a bit less flexible, as I need to update the DNS and wait for the change to propagate if I want to test a new service internally. But, I guess that is fine for now.

Stuggling to set up caching for PyPi server via NGINX/UWSGI

I'm trying to configure caching of an PyPi Server via NGINX/uWSGI and failing miserably.
My /sites-available/pypi config is as follows:
uwsgi_cache_path /mnt/pypi/nginx-cache
levels=1:2
keys_zone=pypiserver_cache:10m
max_size=10g
inactive=60m
use_temp_path=off;
server {
listen 80 default_server;
listen 443 default_server ssl;
ssl_certificate /etc/ssl/certs/domain.pem;
ssl_certificate_key /etc/ssl/private/domain.key;
client_max_body_size 5M;
location / {
uwsgi_cache pypiserver_cache;
uwsgi_buffering on;
uwsgi_cache_key $request_uri;
add_header X-uWSGI-Cache $upstream_cache_status;
include uwsgi_params;
uwsgi_pass unix:/run/uwsgi/internal_pypi.socket;
}
}
NGINX runs and reports no errors but requesting the same package multiple times does not cache it (proven by curling the URL and observing the header X-uWSGI-Cache: MISS) and there is nothing being stored in /mnt/pypi/nginx-cache.
Let me know if I can provide any more helpful info, thanks!
References:
https://github.com/pypiserver/pypiserver#serving-thousands-of-packages
http://nginx.org/en/docs/http/ngx_http_uwsgi_module.html
This is resolved, in my case, the pypi server python file needed some changes.
application = pypiserver.app(
root="/mnt/pypi/directory",
redirect_to_fallback=False,
password_file="path/to/file",
cache_control=3600 #this needed to be added
)

How to make NGINX respond with same protocol as in the client request?

I have an application behind NGINX LB. A client makes a series of HTTPS calls that are dependent on each other. Each response from the NGINX server to the client will be used to make the next subsequent API request. Hence, it's critical that NGINX response should have the same protocol and base URL. I tried many options but couldn't get it to work.
upstream testapp {
ip_hash;
server localhost:5555;
server localhost:6666;
server localhost:7777;
keepalive 20;
}
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name localhost;
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
# root html;
proxy_pass http://testapp;
proxy_set_header Host localhost:443;
proxy_redirect http://testapp https://localhost:443;
}
Everything works fine except the protocol that is returned back to the client. So for example, if client makes the following request
**https**://localhost:443/app
NGINX responds to the client as
http://localhost:443/app/v1/getLimits
instead of
**https**://localhost:443/app/v1/getLimits
Any help is truly appreciated. I am in a crunch with this issue.
Thanks
Nick
You may try doing this
proxy_pass $scheme://testapp
according with nginx's documentation
$scheme
request scheme, “http” or “https”
Hope it helps you and this is my answer to your question.
But if yours backend server are inside to a local network (or vpn) and do not have contact with external world, I would try to have the communication between nginx and backend as http 1.1 because it's significantly faster than https. And I would try to maintain an opened connection between them.
And if you want to maintain comunication between nginx and backend via http but you need to know the protocol because some application logic/needs you may inject your $scheme as a header and you can read from your backend server.
doing
proxy_set_header X-Forwarded-Proto $scheme;
May help you to do it :)
I resolved this issue by using ngx_http_sub module that allows me to replace strings in my response with whatever I want. Thank you all for your comments and responses.

'The change you wanted was rejected' error on all 'Devise' actions after installing SSL certificate

I configured nginx to use SSL certificate(got it from sslforfree.com) but a weird behavior is happening after that. Site is running fine but I'm unable to do any Devise action, e.g. If someone was logged in before using SSL, they can't logout and others can't login/register.
I'm configuring this on Digital-Ocean one-click rails droplet.
Following observations may help:
Nginx.error.log
1 - client closed connection while SSL handshaking
2 - SSL_do_handshake() failed (SSL: error:1408F10B:SSL routines:ssl3_get_record:wrong version number)
- I researched and found out it is happening due to problem in SSL configurations, I tried using Mozilla's generated ones but no success.
Rails Server Log
1 - 422 Unprocessable Entity
2 - ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken)
nginx.conf
upstream puma {
server unix:///home/rails/apps/calwinkle/shared/tmp/sockets/calwinkle-puma.sock;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name calwinkle.com www.calwinkle.com;
# Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
return 301 https://$host$request_uri;
}
server {
# listen 80;
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name calwinkle.com www.calwinkle.com;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
# intermediate configuration. tweak to your needs.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;
root /home/rails/apps/calwinkle/current/public;
access_log /home/rails/apps/calwinkle/current/log/nginx.access.log;
error_log /home/rails/apps/calwinkle/current/log/nginx.error.log info;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #puma;
location #puma {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://puma;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 10M;
keepalive_timeout 10;
}
What I think is, somehow my devise controller is still trying to access using http and I've redirected all http requests to https with 301 and this is causing authenticity token to expire.
I've tried to remove redirection and accept both http and https but that caused an error in nginx configuration.
Given your situation, it looks like you are setting wrong headers. So cookies/sessions are being saved on http.
Can you try and add following two lines in your /etc/nginx/sites-available/* and /etc/nginx/sites-enabled/*
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
After doing that run:
sudo service nginx restart
Additionally, clear your sessions and cookies in browser.
If your site is live with users(which I don't think should be without https), you may need to destroy existing sessions of users.
Hope it helps.

nginx CORS Issues with MAXCDN and Easydns with Digital Ocean

I am having issues with CORS, specifically with max cdn. CORS was working properly with maxcdn until a few days ago. I have posted my host config and the cors header is included.
I am stumped at this point, as I have done the following to troubleshoot:
Disabled a rocket-cache specific configuration for nginx included in
the server block.
I have changed caching methods - rather than redis-hhvm I have tried
switching over to fcgi-hhvm with rocket cache.
I have disabled rocket cache after clearing it's cache - then purging
the entire cache, and used a third party plugin for wordpress
specifically for linking the cdn.
I am using SNI with SPDY on maxcdn - I have a cert just for the subdomain (cdn.jurisdesk.com). And I am using Digitalocean for hosting.
Below is my current nginx config (everything was working properly until a few days ago which prompted me to speak with maxcdn support - who are great by the way, and extremely knowledgeable when it comes to advanced configurations specifically using nginx).
server {
server_name www.jurisdesk.com;
ssl_certificate_key /path/to/key/foobar.key;
ssl_certificate /path/to/cert/foobar.crt;
listen *:80;
listen *:443 ssl spdy;
listen [::]:80 ipv6only=on;
listen [::]:443 ssl spdy ipv6only=on;
return 301 https://jurisdesk.com$request_uri;
}
server {
server_name jurisdesk.com;
listen *:80;
listen [::]:80;
return 301 https://jurisdesk.com$request_uri;
}
server {
server_name jurisdesk.com;
listen *:443 ssl spdy;
listen [::]:443 ssl spdy;
ssl on;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
ssl_certificate_key /path/to/key/foobar.key;
ssl_certificate /path/to/cert/foobar.crt;
access_log /var/log/nginx/jurisdesk.com.access.log rt_cache_redis;
error_log /var/log/nginx/jurisdesk.com.error.log;
root /var/www/jurisdesk.com/htdocs;
index index.php index.html index.htm;
include common/redis-hhvm.conf;
include rocket-nginx/rocket-nginx.conf;
include common/wpcommon.conf;
include common/locations.conf;
location ~ \.(ttf|ttc|otf|eot|woff|woff2|font.css|css|js)$ {
add_header Access-Control-Allow-Origin "*";
}
}
I have also added CORS to rocket-nginx.conf - as this is something I've been tinkering with lately and reflects a change to my config - however I have also removed the directive to eliminate that as the cause of the problem.

Resources