Nginx

From IndieWeb

Nginx is an open source commonly used web server.

Why

Nginx is designed to be modular and you can alter it by building your own binaries. The version you find with most packaging systems come with a core set of features so you may not need to do a custom build.

The other great part about Nginx is that it is designed from the beginning to work with virtual hosts and that most of the common use cases are already listed in the Configuration of the Nginx Wiki.

IndieWeb Examples

Please list any sites you know of that also use Nginx for their IndieWeb server.

How to

SSL Setup

/etc/nginx/nginx.conf

The core configuration for Nginx resides in /etc/nginx/nginx.conf - here is where you will put entries that are designed to be global to all virtual hosts. It is also where you configure how Nginx will use the systems resources.

 user www-data;
 worker_processes 2;
 error_log /var/log/nginx/error.log info;
 pid /var/run/nginx.pid;
 events {
 worker_connections 1024;
 }
 http {
 include 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;
 client_max_body_size 100M;
 server_tokens off;
 keepalive_timeout 65;
 gzip on;
 
 # Read "Which Cipher List Should I Use?" below to select cipher list
 ssl_ciphers <insert cipher list>
 ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 ssl_prefer_server_ciphers on;
 ssl_session_timeout 5m;
 
 # Enable this if your want HSTS (recommended)
 # 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 /etc/ssl/certs;
 
 # use an internal DNS resolver if your behind a firewall, this is Google's DNS server
 resolver 8.8.8.8;
 
 include conf.d/*.conf;
 }


With this as your core config, the remaining items all go into each virtual hosts config, which will be included automatically when you put them in /etc/nginx/conf.d/ and name them as a *.conf file.

Which Cipher List should I use?

This list is from the excellent Mozilla Security page https://wiki.mozilla.org/Security/Server_Side_TLS

PFS (Perfect Forward Secrecy)

In case you're using *DH* ciphers, you'll need a dhparam file for the Diffie–Hellman key exchange step.

 openssl dhparam -rand - 2048 > /etc/nginx/mywebsite.com.dh.pem

and add it to the nginx config, next to the ssl certificate line:

 ssl_dhparam /etc/etc/nginx/mywebsite.com.dh.pem

For details, see the nginx documentation.

Modern compatibility

For servers that do not need to maintain backwards compatibility and what higher security

 ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK

Intermediate compatibility (default)

For servers that do not need legacy clients but need to support a wider range of clients

 ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA

Here is what the above config contains section by section (note: the best place to grok this is, of course, the primary docs for Nginx)

  • user www-data; which system user Nginx will switch to when dropping priviledges to run as a daemon - varies by OS, shown is Ubuntu.
  • worker_processes 2; how many worker threads to create. I consider two to be the minimum and you can increase this if you run on a multiple core server.
  • worker_connections 1024; the maximum number of sockets each worker process is allowed to handle. Make sure this value and the worker_processes value is less than the open files setting for your system.
  • client_max_body_size 100M; sets the maximum post body size, set to some large value if you are expecting to handle photo or video uploads.
  • server_tokens off; this prevents Nginx from leaking information about it's version.
  • ssl_ciphers The list of SSL Ciphers shown is selected to offer a balance of good client support but to also enable Perfect Forward Secrecy. For a lot more detail please read SSL Labs article on deploying forward secrecy.
  • ssl_protocols The order Nginx should look for when negotiating the secure link.
  • ssl_prefer_server_ciphers Do not allow the client to dictate which ciphers will be used - there lies madness as you will almost always end up in lowest-common-denominator land *shudder*.

static site example

For example, a basic static site with zero extra processing, would have as part of /etc/nginx/conf.d/example.com.conf:

 server {
 listen 80;
 server_name example.com;
 root /srv/example;
 access_log /var/log/nginx/example.log main;
 
 include conf.d/redirects.txt;
 
 location / {
 try_files $uri $uri.html $uri/ =404;
 }
 }
  • listen 80; tells Nginx to open port 80 for requests.
  • server_name example.com; if the client making the connection on port 80 is looking for http://example.com, then use this server config to answer the request.
  • root /srv/exampe; where Nginx should look for static files by default. This can (and is often) overridden in the various *location* entries.
  • "include conf.d/redirects.txt" when you have a static site you are removing one of the few benefits that a dynamic site has, the ability to adjust url routing. To solve this we need to include a file in our config and then tell the webserver to reload when new items are added.
  • location / if the path portion of the requested URI matches the given text string, then handle the request using the config inside of this location block.
  • try_files Nginx will try and handle the given request (as modified by any location rewrite rules, in the order given. Our example above says the following until it finds a file that matches:
    • try the URI as given by looking for the file after pre-pending the root path
    • try the URI as given but append .html to the filename
    • try the URI as given but append / so that it will search inside of any matching subdirectories
    • and as the last resort, return the 404 status code

static site with SSL termination

 server {
 listen 80;
 server_name example.com;
 rewrite ^ https://$server_name$request_uri? permanent;
 }
 server {
 listen 443 default_server ssl;
 server_name example.com;
 root /srv/example.com;
 
 access_log /var/log/nginx/example.com.log main;
 
 ssl_certificate /etc/nginx/ssl/example.com.crt;
 ssl_certificate_key /etc/nginx/ssl/example.com.key;
 add_header Strict-Transport-Security max-age=15768000;
 location / {
 try_files $uri $uri.html $uri/ =404;
 }
 }

nginx 1.9.5+

Nginx includes HTTP2 support from version 1.9.5. If it's enabled for the build, you can use the following:

 server {
 listen 80;
 server_name example.com;
 rewrite ^ https://$server_name$request_uri? permanent;
 }
 server {
 listen 443 default_server ssl http2;
 server_name example.com;
 root /srv/example.com;
 
 access_log /var/log/nginx/example.com.log main;
 
 ssl_certificate /etc/nginx/ssl/example.com.crt;
 ssl_certificate_key /etc/nginx/ssl/example.com.key;
 add_header Strict-Transport-Security max-age=15768000;
 location / {
 try_files $uri $uri.html $uri/ =404;
 }
 }

Using PHP with nginx

When using PHP with apache, PHP gets compiled as the apache module mod_php. nginx doesn't do things the same way. So we must take advantage of the fastcgi module built into PHP, namely php5-fpm. Once that's installed we can use it in nginx like so:

server {
 listen 80;
 server_name example.org;
 root /path/to/website/files;
 index index.php;
 location / {
 try_files $uri $uri/ index.php?$query_string;
 }
 location ~ ^(?<script_name>.+?\.php)(?<path_info>.*)$ {
 try_files $uri $script_name =404;
 //or fastcgi_pass unix:/path/to/php5-fpm.sock
 fastcgi_index index.php;
 include /path/to/nginx/fastcgi_params;
 fastcgi_param SCRIPT_FILENAME $document_root$script_name;
 fastcgi_param SCRIPT_NAME $script_name;
 fastcgi_param PATH_INFO $path_info;
 fastcgi_param PATH_TRANSLATED $document_root$path_info;
 fastcgi_param HTTPS $https if_not_empty;
 fastcgi_split_path_info ^(?<script_name>.+?\.php)(?<path_info>.*)$;
 fastcgi_pass 127.0.0.1:9000;
 }
}

Notice the try_files line in the first location block, we try to serve a static file, then a directory. And then instead of returning a 404, we now try the request as index.php. The next location block matches for .php and so then the php application accessed via /path/to/website/files/index.php can determine the appropriate response.

Using uWSGI with Ngnix

This is how I ( Kara Mahan ) configured nginx to proxy to uWSGI (which in turn runs my Python/Flask application). nginx serves resources under /static directly.

server {
 listen 80;
 server_name kylewm.com;
 access_log /srv/www/redwind/logs/access.log;
 error_log /srv/www/redwind/logs/error.log;
 client_max_body_size 10M;
 location / { try_files $uri @redwind; }
 location @redwind {
 include uwsgi_params;
 uwsgi_pass unix:/tmp/uwsgi.sock;
 }
 location /static {
 root /home/kmahan/redwind/redwind;
 expires 30d;
 }
}

And here is the uWSGI configuration to launch the application (communicates with nginx via /tmp/uwsgi.sock)

[uwsgi]
master=true
processes=4
threads=2
socket=/tmp/uwsgi.sock
chmod-socket=666
spooler=spool
module=redwind:app
pidfile=/tmp/redwind.pid
daemonize=uwsgi.log


Using Rails with a Nginx cache

Just a brain dump, perhaps it will be usefull for others too. About a year ago I (Jeena) got on the HackerNews frontpage which put my server down for a couple of hours (load of 24 and more) and I couldn't do anything to get it up again until it was gone from the front page. After That I tried to fix it and configured a cache layer which is build in into Nginx. Today I hit the HN again and now after one hour on it the load is in between 0.3 and 1.0. It looks like the cache helps a lot. But I have to add that we also got a more powerful server which could also account for some of the success.

This is my cleaned up configuration (I have other stuff in it like ssl/php/owncloud, etc.) I marked the three lines which I think are the important ones with a #* at the end of the line.

proxy_cache_path /var/lib/nginx/cache/example.net keys_zone=examplenet:30m; #*
upstream example_net {
 server unix:///home/example/example.net/tmp/example_net.sock;
}
server {
 server_name example.net www.example.net;
 root /home/example/example.net/public/;
 access_log /var/log/nginx/example.net-access.log;
 error_log /var/log/nginx/example.net-error.log;
 merge_slashes off;
 
 location ~* ^/assets/ {
 # Per RFC2616 - 1 year maximum expiry
 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
 expires 1y;
 add_header Cache-Control public;
 # Some browsers still send conditional-GET requests if there's a
 # Last-Modified header or an ETag header even if they haven't
 # reached the expiry date sent in the Expires header.
 add_header Last-Modified "";
 add_header ETag "";
 break;
 }
 
 location / {
 try_files $uri $uri @rails; #*
 expires max;
 access_log off;
 }
 
 location @rails {
 proxy_pass http://example_net;
 proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Forwarded-Proto https;
 proxy_redirect off;
 proxy_cache examplenet; #*
 }
}

Serve files without extensions

AKA: How do I get HTML files served without the .html extension in the URL?

The try_files example in the static site example shows how nginx can be told how to look for the requested resource - specifically the $uri.html part.

Proxy WebSockets

Proxying websockets through nginx makes it easy to support https connections (if the server is already configured for https, secure websockets will just work). It is also a convenient way to collect multiple server processes so they all serve over the same public port.

This snippet will proxy a service running on localhost:8077 to the path /_updates. By default nginx will close the connection after a short idle period; the very long read timeout (1 day) prevents this from happening.

 location /_updates { 
 proxy_pass http://localhost:8077;
 proxy_http_version 1.1;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection "upgrade";
 proxy_read_timeout 86400s;
 }

Serve HTTPS more securely

See Jonny’s blog-post for more information: https://jonnybarnes.uk/blog/2015/04/hardening-https-with-nginx

Silo Examples

Instagram

Instagram uses nginx to serve the frames that constitute the majority of their "native iOS app" content and interface, as demonstrated by this nginx default error message:

FAQ

If you have a question about nginx or are having an issue with it, feel free to add it here with a === heading === and hopefully one of the nginx experts in the community will answer it and we can start an FAQ!

What is POODLE

The POODLE attack (which stands for "Padding Oracle On Downgraded Legacy Encryption") is a man-in-the-middle exploit which takes advantage of Internet and security software clients' fallback to SSL 3.0 - see https://en.wikipedia.org/wiki/POODLE

What is HeartBleed

Heartbleed is a security bug disclosed in April 2014 in the OpenSSL cryptography library, which is a widely used implementation of the Transport Layer Security (TLS) protocol. Heartbleed may be exploited regardless of whether the party using a vulnerable OpenSSL instance for TLS is a server or a client. - see https://en.wikipedia.org/wiki/Heartbleed

... add an FAQ here ...

See Also

Open Source IndieWeb related projects and tools
Topics Why open source? β€’ How to open source a project β€’ Open for contributions β€’ IndieWeb Principles β€’ Package managers for various languages and systems
PHP (See LAMP) b2evolution β€’ CASSIS β€’ Known β€’ Falcon (portions) β€’ HamsterCMS β€’ p3k (portions) β€’ stapibas β€’ dobrado β€’ Grav β€’ Miniflux-legacy β€’ ProcessWire β€’ Textpattern β€’ tt-rss β€’ Twyne β€’ TYPO3
WordPress (PHP) Indieweb Plugin β€’ Webmention β€’ Semantic Linkbacks β€’ Micropub β€’ IndieAuth β€’ Post Kinds β€’ Syndication Links β€’ WebSub plugins β€’ See Template:WordPress for others.
Python BeautifulSoup β€’ blag β€’ Bleach β€’ Bridgy Fed β€’ Bundle β€’ django CMS β€’ IndieWeb Search β€’ IndieWeb Utils β€’ kaku β€’ mf2util β€’ Nefelibata β€’ ninka β€’ Publ β€’ Pushl β€’ python-indieweb β€’ Red Wind β€’ Requests β€’ ronkyuu β€’ Woodwind β€’ gunicorn
Ruby Dark Matter β€’ Feedbin β€’ FrancisCMS β€’ GitLab β€’ Micropublish β€’ Publify β€’ Ruby on Rails β€’ Sinatra β€’ Singulus β€’ Transformative β€’ webmention.io β€’ Jekyll β€’ Authorio β€’ ruby parser β€’ microformats-ruby
JavaScript AerosolCMS β€’ CASSIS β€’ Camel β€’ Eleventy β€’ Ghost β€’ IndieKit β€’ D3
Others Apache (C) β€’ Forgejo (Go) β€’ Hugo (Go) β€’ Firefox (C++) β€’ Nginx (C) β€’ NetNewsWire (Swift) β€’ Snac (C)