andybeak
2/17/2015 - 7:51 AM

An nginx config for Wordpress

An nginx config for Wordpress

# Read  http://codex.wordpress.org/Nginx
#       http://wiki.nginx.org/Pitfalls
#       http://wiki.nginx.org/QuickStart
#       http://www.queryadmin.com/854/secure-wordpress-nginx/
#       http://tautt.com/best-nginx-configuration-for-security/
#       https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
#
#       Generate your key with: openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
#       Generate certificate: sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt


server {

    server_name www.example.com;
    listen [::]:8080;
    root /home/web/sites/default/html/;
    index index.php;

    access_log /home/web/sites/default/logs/access.log combined;
    error_log /home/web/sites/default/logs/error.log warn;

    include  /home/web/sites/default/html/nginx.conf;
    
    add_header X-Content-Type-Options "nosniff";
    add_header X-XSS-Protection "1; mode=block";
    
    if ($server_protocol ~* "HTTP/1.0") {
      return 444;
    }

    location / {
        # refer to https://www.proteansec.com/linux/naxsi/
        # include /etc/nginx/naxsi.rules;
  
        # include the "?$args" part so non-default permalinks doesn't break when using query string
        try_files /wp-content/w3tc/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args ;
        # if you're not using w3tc then comment the line above and uncomment the line below
        # try_files $uri $uri/ /index.php?$args;
    }

    location /wp-admin/ {
        return 301 https://$server_name$request_uri;
    }

    location /mystery-login {
        return 301 https://$server_name$request_uri;
    }

    # Prevent any potentially-executable files in the uploads directory from being executed
    location ~* /uploads/ {
        location ~ \.php {return 403;}
    }

    # Do not log favicon.ico requests
    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    # Do not log robots.txt requests
    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires max;
        log_not_found off;
    }

    include global/w3tc.conf;

    # Common deny or internal locations, to help prevent access to not-public areas
    location ~* wp-admin/includes { deny all; }
    location ~* wp-includes/theme-compat/ { deny all; }
    location ~* wp-includes/js/tinymce/langs/.*\.php { deny all; }
    location /wp-content/ { internal; }
    location /wp-includes/ { internal; }
    location ~* wp-config.php { deny all; }

    # Rewrite rules for Wordpress SEO by Yoast
    rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last;
    rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2;

    # Add trailing slash to */wp-admin requests
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;

    # Redirect 403 errors to 404 error to fool attackers
    error_page 403 = 404;

    # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
    location ~ /\. {
        deny all;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        if (!-f $document_root$fastcgi_script_name) {
            return 404;
        }
        fastcgi_keep_conn on;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;

        include fastcgi_params;
    }

}

server {
    server_name example.com;
    listen 8080;
    return 301 $scheme://www.example.com$request_uri;
}


server {

    server_name www.example.com;
    listen 443 ssl;

    server_tokens off;
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com https://assets.zendesk.com https://connect.facebook.net; img-src 'self' https://ssl.google-analytics.com https://s-static.ak.facebook.com https://assets.zendesk.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://assets.zendesk.com; font-src 'self' https://themes.googleusercontent.com; frame-src https://assets.zendesk.com https://www.facebook.com https://s-static.ak.facebook.com https://tautt.zendesk.com; object-src 'none'";

    root /home/web/sites/default/html/;
    index index.php;

    access_log /home/web/sites/default/logs/access_ssl.log combined;
    error_log /home/web/sites/default/logs/error_ssl.log warn;

    # enable session resumption to improve https performance
    # http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html
    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout 5m;

    # Diffie-Hellman parameter for DHE ciphersuites, recommended at least 2048 bits
    # generate with openssl dhparam -out dhparam.pem 4096
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;

    # see https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    ssl_protocols TLSv1.2;
    ssl_prefer_server_ciphers on;
    keepalive_timeout    60;
    # config to enable HSTS(HTTP Strict Transport Security) https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
    # to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";

    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;

    location / {
        # include the "?$args" part so non-default permalinks doesn't break when using query string
        try_files /wp-content/w3tc/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args ;
    }

    # Add trailing slash to */wp-admin requests
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;

    include  /home/web/sites/default/html/nginx.conf;

    rewrite ^(/)?mystery-login/?$ /wp-login.php?$query_string break;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        if (!-f $document_root$fastcgi_script_name) {
            return 404;
        }
        fastcgi_keep_conn on;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;

        include fastcgi_params;
    }
}