Nginx Snippets & Advanced Techniques

Nginx Snippets & Advanced Techniques

Memcached config

Nginx configuration that reads from memcached (populated by WP-FFPC plugin), and if not found, then from HHVM, and lastly from PHP-FPM. The only thing not working is if you stop HHVM, it does not failover to PHP-FPM as it should be. Also, responces from memcached does not contain original headers.

I decided not to use such advanced configuration as I detected that speed directly reading memcached from PHP is not that slower than from nginx - and there are far less complications involved.

# Force www
#
if ($host !~* ^(www)) {
    return 301 $scheme://www.$host$request_uri;
}

# Force SSL
#
if ($scheme = http) {
    return 301 https://$host$request_uri;
}

# Fix for Wordpress permalinks 404 problems
#
location / {
    try_files $uri $uri/ /index.php$is_args$args;
}

location ~ \.php$ {
    try_files /dummy_nonexistant_file @memcached;
}

# memcached with failover to hhvm
#
location @memcached {
    error_page 404 405 502 504 = @hhvm;

    # avoid cache serve of any URL with query strings
    if ($args) {
        return 405;
    }

    # avoid cache serve of POST requests
    if ($request_method = POST) {
        return 405;
    }

    # avoid cache serve of wp-admin-like pages, starting with "wp-"
    if ($uri ~ "/wp-" ) {
        return 405;
    }

    if ($http_cookie ~ (wp-postpass|wordpress_logged_in|comment_author|wordpressuser)_ ) {
        return 405;
    }

    default_type text/html;

    add_header X-Powered-By "Memcached";

    set $memcached_raw_key $scheme://$host$request_uri;
    set $memcached_key data-$memcached_raw_key;

    memcached_pass 127.0.0.1:11211;
}

# Also needed to increase upload max size
#
client_max_body_size 32M;

# Web Fonts - CORS & expires
#
location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ {
    add_header "Access-Control-Allow-Origin" "*";

    # Only one location match is ever executed - therefore the relevant
    # location match must contain all relevant rules. The first regular
    # expression to match the query will stop the search.
    #
    expires 1M;
    access_log off;
    add_header Cache-Control "public";
}

# OCSP stapling
#
ssl_stapling on;
ssl_stapling_verify on;
#
# Trusted cert must be made up of your intermediate certificate followed by root certificate
# ssl_trusted_certificate /path/to/ca.crt;
#
# Using Google DNS & OpenDNS
resolver 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
resolver_timeout 2s;

# Speed up SSL handshake
#
# SSL Session Cache
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 10m;
#
# Use only fast ciphers
# ssl_ciphers ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM
ssl_ciphers 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;

# Cache static content
# Note: webpagetest.org needs a expire period of one week for a A grade
#
location ~* \.(?:css|js|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
  expires 1w;
  access_log off;
  add_header Cache-Control "public";

  # add missing vary header in SPDY to please GTmetrix and such
  add_header Vary "Accept-Encoding";
}

This is a compiled idea from the following sources:


About some nginx variables:

  • $request_uri is full original request URI, with arguments, and it does not contain a host or domain name (always starts with a “/” character).

I want to protect a site from public eyes, but also give access only to specific users - cookies seem perfect for this.

Just add this:

if ($http_cookie !~* 'etk-private') {
    return 401;
}

Simplest way to set a cookie is to go to any domain page and in JS console type:

document.cookie = 'cookie=etk-private;max-age=3153600000;path=/';

Or you can do this automatically by visiting specific URL. To set this up, enter:

if ($uri = "/etk-set-private-cookie/") {
    rewrite ^ /etk-set-private-cookie/ last;
}

location = /etk-set-private-cookie/ {
    add_header Set-Cookie "etk-private=1;Path=/;Max-Age=31536000";
    empty_gif;
}

Done.

something like:

if ($uri = "/private/") { rewrite ^ /private/ last; }
location = /private/ { add_header Set-Cookie "private=1;Path=/;Max-Age=31536000"; empty_gif; }
if ($http_cookie !~* 'private') { return 401; }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Cookie based authentication
#
if ($uri = "/svko/") { rewrite ^ /svko/ last; }

location = /svko/ {
    default_type text/html;
    add_header Set-Cookie "svko=1;Path=/;Max-Age=31536000";
    return 200 '<!DOCTYPE html>\n<h2>Hooray ;)</h2>\n<p>... now you can <a href="/">access the site</a></p>';
}

# Pass through certificate checks
if ($uri !~* "^/.well-known/acme-challenge/(.*)") { set $condition BO; }
if ($http_cookie !~* 'svko') { set $condition "${condition}TH"; }
if ($condition = "BOTH") { rewrite ^ /401-error/ last; }

location = /401-error/ { default_type text/html; return 401 '<!DOCTYPE html>\n<pre>  401 | Restricted Access</pre>'; }

We can test if it’s working.

1
2
3
4
5
6
7
8
9
# test if it's returning a cookie
curl -I https://www.gzoo.de/gzoo/

# test with cookie sent
curl -I -b 'gzoo=1' https://www.gzoo.de/

# working with Let's Encrypt? even without cookie
curl -I https://www.gzoo.de/.well-known/acme-challenge/dummy.html

Do note that if statements in nginx are evaluated on every request, so that could have some performance implications.

date 01. Jan 0001 | modified 28. May 2021
filename: Server - ( Nginx ) Snippets