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