2
votes

Trying to figure out how to host a WordPress site with NGINX and PHP-FPM, but also add Varnish for caching; and, to make it worse, have this offered over SSL.

I've worked with NGINX, Varnish, and Gunicorn for an SSL Django site, and I thought this might be similar. I've also setup Varnish, Apache and WordPress before. But so far my logs have been fruitless.


Here's what is working right now (NGINX and FPM):

NGINX port 80 forwarding to 443 (also forwarding www to non-www).

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com
    return 301 https://example.com$request_uri;
}

NGINX port 443 passing to PHP-FPM listening on port 9000

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    access_log /var/log/nginx-access.log;
    error_log /var/log/nginx-error.log;

    server_name example.com;

    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

So I add Varnish

I've set up Varnish on port 6081 (default) and point it to 8080 as a backend.

Then I add another port for NGINX to proxy to FPM (like before)

I've then added this config for NGINX to handle port 8080, posted here in its entirety since it's so small (NOTE! THIS SECTION SEEMS TO BE THE PROBLEM):

server {
    listen 127.0.0.1:8080;

    access_log /var/log/nginx-access.log;
    error_log /var/log/php-fpm-error.log;

    root /var/www/example.com;

    index index.php;
    port_in_redirect off;

    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
    }
}

And then I've altered the SSL section of NGINX to proxy_pass to Varnish (like I've done in my other projects):

upstream varnish_server {
    server 127.0.0.1:6081 fail_timeout=0;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    # etc. 

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header 'Access-Control-Allow-Origin' '*';

        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header HTTPS "on";
        proxy_pass http://varnish_server;
        break;
    }
}

Errors I've received:

varnishlog says this:

   11 ReqStart     c 127.0.0.1 39742 1007025263
   11 RxRequest    c GET
   11 RxURL        c /
   11 RxProtocol   c HTTP/1.0
   11 RxHeader     c X-Forwarded-For: 12.34.567.890
   11 RxHeader     c Host: example.com
   11 RxHeader     c Access-Control-Allow-Origin: *
   11 RxHeader     c X-Forwarded-Host: example.com
   11 RxHeader     c X-Real-IP: 12.34.567.890
   11 RxHeader     c X-Forwarded-Proto: https
   11 RxHeader     c HTTPS: on
   11 VCL_call     c pass pass
   11 FetchError   c no backend connection
   11 VCL_call     c error deliver
   11 VCL_call     c deliver deliver
   11 TxProtocol   c HTTP/1.1
   11 TxStatus     c 503
   11 TxResponse   c Service Unavailable
   11 TxHeader     c Server: Varnish

nginx-error.log says this:

[error] 7532#0: *1 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 12.34.567.890, server: example.com, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:6081/", host: "example.com"

Visually the site hangs for about a minute or two, times out; can't see any other errors.

Also, note that I added this to wp-config.php, as recommended in WordPress Codex:

if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false)
       $_SERVER['HTTPS']='on';

Any help on this would be much appreciated! :D

Update

Varnish default.vcl as requested; though, it is virtually identical to tutslpus tutorial on it (here):

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .connect_timeout = 60s;
    .max_connections = 500;
}

acl purge {
    "127.0.0.1";
    "localhost";
}

sub vcl_recv {
    set req.grace = 2m;

    # Set X-Forwarded-For header for logging in nginx
    remove req.http.X-Forwarded-For;
    set    req.http.X-Forwarded-For = client.ip;

    # Remove has_js and CloudFlare/Google Analytics __* cookies and statcounter is_unique
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js|is_unique)=[^;]*", "");
    # Remove a ";" prefix, if present.
    set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");

    # Either the admin pages or the login
    if (req.url ~ "/wp-(login|admin|cron)") {
        # Don't cache, pass to backend
        return (pass);
    }

    # Remove the wp-settings-1 cookie
    set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-1=[^;]+(; )?", "");

    # Remove the wp-settings-time-1 cookie
    set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-time-1=[^;]+(; )?", "");

    # Remove the wp test cookie
    set req.http.Cookie = regsuball(req.http.Cookie, "wordpress_test_cookie=[^;]+(; )?", "");

    # Static content unique to the theme can be cached (so no user uploaded images)
    # The reason I don't take the wp-content/uploads is because of cache size on bigger blogs
    # that would fill up with all those files getting pushed into cache
    if (req.url ~ "wp-content/themes/" && req.url ~ "\.(css|js|png|gif|jp(e)?g)") {
        unset req.http.cookie;
    }

    # Even if no cookies are present, I don't want my "uploads" to be cached due to their potential size
    if (req.url ~ "/wp-content/uploads/") {
        return (pass);
    }

    # any pages with captchas need to be excluded
    if (req.url ~ "^/contact/" || req.url ~ "^/links/domains-for-sale/") {
        return(pass);
    }

    # Check the cookies for wordpress-specific items
    if (req.http.Cookie ~ "wordpress_" || req.http.Cookie ~ "comment_") {
        # A wordpress specific cookie has been set
        return (pass);
    }

    # allow PURGE from localhost
    if (req.request == "PURGE") {
        if (!client.ip ~ purge) {
                error 405 "Not allowed.";
        }
        return (lookup);
    }

    # Force lookup if the request is a no-cache request from the client
    if (req.http.Cache-Control ~ "no-cache") {
        return (pass);
    }

    # Try a cache-lookup
    return (lookup);

}

sub vcl_deliver {
    if (obj.hits > 0) { # Add debug header to see if it's a HIT/MISS and the number of hits, disable when not needed
        set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
    # Please note that obj.hits behaviour changed in 4.0, now it counts per objecthead, not per object
    # and obj.hits may not be reset in some cases where bans are in use. See bug 1492 for details.
    # So take hits with a grain of salt
    set resp.http.X-Cache-Hits = obj.hits;
}

sub vcl_fetch {
    # set obj.grace = 5m;
    set beresp.grace = 2m;

    if (req.url !~ "wp-admin|wp-login|product|cart|checkout|my-account|/?remove_item=") {
        unset beresp.http.set-cookie;
    }
}

And netstat, as requested:

root@server:/# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:11211         0.0.0.0:*               LISTEN      13454/memcached 
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      21420/nginx     
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      21420/nginx     
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      3583/sshd       
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      3778/master     
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      21420/nginx     
tcp        0      0 0.0.0.0:6081            0.0.0.0:*               LISTEN      29149/varnishd  
tcp        0      0 127.0.0.1:6082          0.0.0.0:*               LISTEN      29148/varnishd  
tcp6       0      0 :::3306                 :::*                    LISTEN      3643/mysqld     
tcp6       0      0 :::80                   :::*                    LISTEN      21420/nginx     
tcp6       0      0 :::22                   :::*                    LISTEN      3583/sshd       
tcp6       0      0 ::1:25                  :::*                    LISTEN      3778/master     
tcp6       0      0 :::443                  :::*                    LISTEN      21420/nginx     
tcp6       0      0 :::6081                 :::*                    LISTEN      29149/varnishd  
tcp6       0      0 ::1:6082                :::*                    LISTEN      29148/varnishd
1
Please post your Varnish config, and this guide may help you. See the section on HTTPS near the end.user2493235
The error message you're seeing indicates that nginx is failing to connect to varnish at all. Can you check varnish is online, and running? If so, please paste config, and output of "netstat -tnlp"Kirrus
Updated. Sorry for the late response. I should say though that the error message I'm seeing is a Varnish error, meaning NGINX is, in fact, connecting. It appears to me that the problem is proxying back to NGINX, and then to PHP-FPM. Also please note that I have changed to using the sock file instead of port 9000 for PHP-FPM (why netstat isn't showing 9000)bozdoz

1 Answers

1
votes

The config bellow work with me

  1. Set up Varnish on port 80 and point it to 8080 as a backend.
  2. setup Nginx listen on 2 port: 8080 and 443

port 8080 : as you config to process web request

    server {
    listen 127.0.0.1:8080;

    access_log /var/log/nginx-access.log;
    error_log /var/log/php-fpm-error.log;

    root /var/www/example.com;

    index index.php;
    port_in_redirect off;

    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
    }}

port 443: proxy_pass to varnish

    server {
        listen 443 ssl;
        server_name  example.com;
        ssl_certificate /path/to/fullchain.pem;
        ssl_certificate_key /path/to/privkey.pem;

        location / {
            proxy_pass  http://127.0.0.1:80;
    
            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_set_header X-Forwarded-Port 443;
            proxy_set_header Host $host;
        }
    }

That config follow:

this image archive