autopilotpattern / wordpress

A robust and highly-scalable implementation of WordPress in Docker using the Autopilot Pattern
GNU General Public License v2.0
158 stars 41 forks source link

Sample Nginx config #19

Open misterbisson opened 8 years ago

misterbisson commented 8 years ago

This config file is sets up some strong caching, including overriding cache headers emitted from the upstream and forcing some requests to GET. Very specific directory paths have exceptions that allow uncached requests.

Rather than burying details in location, as is common for shared Nginx configs, I sprinkled details throughout the hierarchy as needed and convenient for the scope of each rule.

# Many details of this from http://www.djm.org.uk/wordpress-nginx-reverse-proxy-caching-setup/
# Though also referenced http://wiki.nginx.org/Pitfalls to validate some of it

user   www  www;
worker_processes  11;

events {
    # After increasing this value You probably should increase limit
    # of file descriptors (for example in start_precmd in startup script)
    worker_connections  8192;
}
worker_rlimit_nofile 16384; # see http://stackoverflow.com/questions/7325211/

http {
    include       /opt/local/etc/nginx/mime.types;
    default_type  application/octet-stream;

    # Defines the cache log format, cache log location
    # and the main access log location.
    log_format cache '***$time_local '
        '$upstream_cache_status '
        'Cache-Control: $upstream_http_cache_control '
        'Expires: $upstream_http_expires '
        '$host '
        '"$request" ($status) '
        '"$http_user_agent" '
        'Args: $args '
        'Wordpress Auth Cookie: $wordpress_auth '
        ;
    access_log /var/log/nginx/cache.log cache;
    access_log /var/log/nginx/access.log;

    # Hide server information for security
    server_tokens off;

    # caching path and config
    proxy_cache_path  /var/nginx/cache  levels=1:2   keys_zone=main:192m  max_size=2g;
    proxy_temp_path /var/nginx/temp;

    #Gzip config
    gzip on;
    gzip_comp_level 4;
    gzip_proxied any;
    # gzip_static on; # module not installed? http://wiki.nginx.org/HttpGzipStaticModule
    gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    # Some version of IE 6 don't handle compression well on some mime-types, so just disable for them
    gzip_disable "MSIE [1-6]\.";
    # Set a vary header so downstream proxies don't send cached gzipped content to IE6
    gzip_vary on;

    # activate sendfile for performance http://wiki.nginx.org/HttpCoreModule#sendfile
    # WP3.5 eliminated the ms-files.php proxying of user-uploaded files, so this is less useful now
    sendfile on;

    # a pool of servers to handle public requests
    upstream backendpublic {
        server 192.168.114.154 weight=3 max_fails=3 fail_timeout=30s; # app3
        server 192.168.114.148 weight=3 max_fails=3 fail_timeout=30s; # app2
        server 192.168.114.147 weight=2; #app1
        }

    # a pool of servers to handle admin requests
    upstream backendadmin {
        server 192.168.114.154 weight=2 max_fails=3 fail_timeout=30s; # app3
        server 192.168.114.148 weight=2 max_fails=3 fail_timeout=30s; # app2
        server 192.168.114.147 weight=3; #app1
    }

    # cache ssl sessions as suggested in http://nginx.org/en/docs/http/configuring_https_servers.html#optimization
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;

    # map oauth connection get vars
    # not currently used
    map $args $oauth {
        default 0;
        "~(oauth|code|denied|error)" 1;
    }

    # increase the proxy buffer from the default 4k
    proxy_buffer_size 8k;

    server {
        listen *:80 default_server;
        listen *:443 ssl;
        keepalive_timeout 70;
        proxy_http_version 1.1;

        # ssl keys
        ssl_certificate /path/to/cert-bundle.crt;
        ssl_certificate_key /path/to/site.com.key;

        # Set proxy headers for the passthrough
        proxy_set_header Host $host:$server_port;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # redirect www. prefix, as in www.domain.org -> domin.org (301 - Permanent)
        if ($host ~* www\.(.*)) {
            set $host_without_www $1;
            return 301 https://$host_without_www$request_uri ;
        }

        # Max upload size: make sure this matches the php.ini in .htaccess
        client_max_body_size 50m;

        # Catch the wordpress cookies.
        # Must be set to blank first for when they don't exist.
        set $wordpress_auth "";
        set $proxy_cache_bypass 0;
        set $proxy_no_cache 0;
        set $requestmethod GET;
        if ($http_cookie ~* "wordpress_logged_in_[^=]*=([^%]+)%7C") {
            set $wordpress_auth wordpress_logged_in_$1;
            set $proxy_cache_bypass 1;
            set $proxy_no_cache 1;
            set $requestmethod $request_method;
        }

        # Set the proxy cache key
        set $cache_key $scheme$host$uri$is_args$args;

        proxy_hide_header X-Powered-By;

        # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
        location ~ /\. {
            deny all;
        }

        # wp-admin and cron pages
        # always handle on the admin host
        location ~* ^/(wp-admin|wp-cron.php) {
            proxy_pass http://backendadmin;

            proxy_read_timeout 300s;
            proxy_connect_timeout 75s;
            proxy_send_timeout 120s;
            proxy_pass_header 'Set-Cookie';
        }

        # login/signup pages and comment submission
        # handle on public hosts, but do no caching
        # @TODO: maybe add throttling here
        location ~* ^/(wp-comments-post.php|wp-login.php|newsletters/|subscription/|members/|b/|connect/|do/|enterprise-onboarding/) {
            proxy_pass http://backendpublic;

            proxy_pass_header 'Set-Cookie';
        }

        # Deny access to user uploaded files with a .php extension
        # found in http://codex.wordpress.org/Nginx#Global_restrictions_file
        location ~* /(?:uploads|files)/.*\.php$ {
            deny all;
        }

        # user uploaded content
        # forces caching and GET for all requests
        location ~* ^/(uploads|files|wp-content) {
            proxy_pass http://backendpublic;

            # override the request method, force everything to GET
            proxy_method GET;

            # hide cache-related headers
            proxy_hide_header X-Powered-By;
            proxy_hide_header Vary;
            proxy_hide_header Pragma;
            proxy_hide_header Expires;
            proxy_hide_header Last-Modified;
            proxy_hide_header Cache-Control;
            proxy_hide_header Set-Cookie;

            # set headers to encourage caching
            expires 30d;
            add_header Vary "Accept-Encoding, Cookie";
            add_header Pragma "public";
            add_header Cache-Control "public, must-revalidate, proxy-revalidate";

            # don't honor cache headers from the app server
            proxy_ignore_headers Set-Cookie Expires Cache-Control;

            # internal cache settings
            proxy_cache main;
            proxy_cache_key $cache_key;
            proxy_cache_valid 30d; # 200, 301 and 302 will be cached.
            # Fallback to stale cache on certain errors.
            #503 is deliberately missing
            proxy_cache_use_stale error timeout invalid_header http_404 http_500 http_502 http_504;
        }

        # front page is uncached because of oauth requests
        # also accepts POST requests because of enterprise onboarding
        # handle on public hosts, but maybe add throttling here
        location = / {
            proxy_pass http://backendpublic;

            proxy_pass_header Set-Cookie;
        }

        # default location
        # forces caching and GET, except for logged in users
        location / {
            proxy_pass http://backendpublic;

            # maybe override the request method, defaults to GET, see if conditionals above
            # proxy_method $requestmethod;
            # actually, this doesn't work, see http://trac.nginx.org/nginx/ticket/283

            # hide cache-related headers
            proxy_hide_header X-Powered-By;
            proxy_hide_header Vary;
            proxy_hide_header Pragma;
            proxy_hide_header Expires;
            proxy_hide_header Last-Modified;
            proxy_hide_header Cache-Control;
            proxy_hide_header Set-Cookie;

            # set headers to encourage caching
            expires 30m;
            add_header Vary "Accept-Encoding, Cookie";
            add_header Pragma "public";
            add_header Cache-Control "public, must-revalidate, proxy-revalidate";

            # don't honor cache headers from the app server
            proxy_ignore_headers Set-Cookie Expires Cache-Control;

            # internal cache settings
            proxy_cache main;
            proxy_cache_key $cache_key;
            proxy_cache_valid 30m; # 200, 301 and 302 will be cached.
            # Fallback to stale cache on certain errors.
            #503 is deliberately missing
            proxy_cache_use_stale error timeout invalid_header http_404 http_500 http_502 http_504;
            #2 rules to disabled the cache for logged in users.
            proxy_cache_bypass $proxy_cache_bypass; # Do not cache the response.
            proxy_no_cache $proxy_no_cache; # Do not serve response from cache.
        }

    } # End server

} # End http

In the context of Containerbuddy, I'd expect to change the backends and fetch the SSL keys, with everything else being static.

(Originally at https://github.com/autopilotpattern/nginx/issues/2)