Skip to content

Add support for page cache #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# vim: set ft=nginx:

{{- $chosenBackend := default "" .Env.STACK_PAGE_CACHE_BACKEND }}
{{- $redisHost := default "localhost" .Env.STACK_PAGE_CACHE_REDIS_HOST }}
{{- $redisPort := default "6379" .Env.STACK_PAGE_CACHE_REDIS_PORT }}
{{- $memcachedHost := default "127.0.0.1" .Env.STACK_PAGE_CACHE_MEMCACHED_HOST }}
{{- $memcachedPort := default "11211" .Env.STACK_PAGE_CACHE_MEMCACHED_PORT }}

{{- if eq $chosenBackend "redis" }}

location = /.stack-cache-fetch {
internal;

set $redis_key $args;
redis_pass {{ $redisHost }}:{{ $redisPort }};
}

location = /.stack-cache-store {
internal;

set_unescape_uri $exptime $arg_exptime;
set_unescape_uri $key $arg_key;

redis2_query set $key $echo_request_body;
redis2_query expire $key $exptime;
redis2_pass {{ $redisHost }}:{{ $redisPort }};
}

{{- else if eq $chosenBackend "memcached" }}
location = /.stack-cache-fetch {
internal;

set $memc_key $args;

memc_connect_timeout 500ms;
memc_read_timeout 500ms;
memc_ignore_client_abort on;

memc_pass {{ $memcachedHost }}:{{ $memcachedPort }};
}

location = /.stack-cache-store {
internal;

set_unescape_uri $exptime $arg_exptime;
set_unescape_uri $key $arg_key;

set $memc_key $key;
set $memc_exptime $exptime;

memc_connect_timeout 500ms;
memc_send_timeout 500ms;
memc_ignore_client_abort on;

memc_pass {{ $memcachedHost }}:{{ $memcachedPort }};
}
{{- end }}
7 changes: 4 additions & 3 deletions php/docker/templates/nginx-vhost-conf.d/80-index.conf
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ location / {
}

location ~ \.php$ {
fastcgi_pass $upstream;
include /usr/local/docker/etc/nginx-vhost-conf.d/page-cache.d/*.conf;
fastcgi_pass $upstream;
fastcgi_read_timeout {{ max 60 (add 10 (default "30" .Env.PHP_REQUEST_TIMEOUT | atoi)) }};
fastcgi_index index.php;
include /usr/local/openresty/nginx/conf/fastcgi.conf;
fastcgi_index index.php;
include /usr/local/openresty/nginx/conf/fastcgi.conf;
}
156 changes: 156 additions & 0 deletions php/docker/templates/nginx-vhost-conf.d/page-cache.d/10-index.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# vim: set ft=nginx:

{{- $chosenBackend := default "" .Env.STACK_PAGE_CACHE_BACKEND }}
{{- $allowedBackends := list "redis" "memcached" "custom" }}
{{- $memcachedHost := default "127.0.0.1" .Env.STACK_PAGE_CACHE_MEMCACHED_HOST }}
{{- $memcachedPort := default "11211" .Env.STACK_PAGE_CACHE_MEMCACHED_PORT }}
{{- $truthStrings := list "true" "on" "yes" -}}
{{- $keyPrefix := default "nginx-cache:" .Env.STACK_PAGE_CACHE_KEY_PREFIX }}
{{- $keyUID := default "https$request_method$host$request_uri" .Env.STACK_PAGE_CACHE_KEY_UID }}

{{- if has $chosenBackend $allowedBackends }}
set $skip_cache 0;

set_by_lua_block $skip_cache {
local scheme = ngx.var.scheme
local uri = ngx.var.request_uri
local request = ngx.ctx.request or {}
ngx.ctx.request = request
local user_agent = ngx.var.http_user_agent or ''
local request_method = ngx.req.get_method()

re = {}
re.wp_loggedin = [[wordpress_logged_in|wordpress_no_cache|comment_author_|wp-postpass_|wp_gdsr_|fbs_|wp-query_monitor_]]
re.woocommerce_cookies = [[woocommerce_cart_hash|woocommerce_items_in_cart|wp_woocommerce_session_]]
re.woocommerce_args = [[remove_item|removed_item|remove_coupon|undo_item|order|add-to-cart|added-to-cart|revoke-key]]
re.bbpress_cookies = [[EmailID]]

function find_plain(s, sub)
if string.find(s, sub, 1, true) == nil then
return false
end

return true
end

request.is_wp_admin = (
not ngx.re.match(uri, "^.*/wp-admin/admin-ajax.php")
and ngx.re.match(uri, "^.*/(wp-admin/|wp-login.php|wp-signup.php|wp-cron.php|xmlrpc.php|git-webhook.php|feed/|sitemap.xml|sitemap_index.xml)", "o")
)

request.wp_loggedin = (ngx.re.match(ngx.var.http_cookie or '', re.wp_loggedin, 'io') and true or false)

request.query_string = (ngx.var.is_args or '') .. (ngx.var.args or '')

request.is_woocommerce = (
ngx.re.match(request.query_string or '', re.woocommerce_args, 'io') or
(ngx.re.match(ngx.var.http_cookie or '', re.woocommerce_cookies, 'io') and true) or
(find_plain(uri, '/wc-api/') or ngx.re.match(request.query_string, 'wc-api=', 'o'))
)

request.is_dynamic = (
request.is_wp_admin or
request.wp_loggedin or
request.is_woocommerce or
(request_method ~= 'GET' and
request_method ~= 'HEAD') or
-- skip cache if there are query strings (TODO: improve this logic for better cache hit)
request.query_string ~= ''
)

local cache_bypass = (ngx.var.skip_cache or '0') ~= '0'
or (ngx.re.match(ngx.var.http_cookie or '', re.bbpress_cookies, 'io') and true)
or false

local skip_cache = 0
if request.is_dynamic or cache_bypass then
skip_cache = 1
end

return skip_cache
}

set $key "";
set $key_prefix {{ $keyPrefix }};
set $key_uid {{ $keyUID }};
set $escaped_key "";
set_escape_uri $escaped_key_prefix $key_prefix;
set_escape_uri $escaped_key_uid $key_uid;

# Use versioned keys with memcached by default, for implementing cache flush
{{- if and (eq $chosenBackend "memcached") ( has (default "on" .Env.STACK_PAGE_CACHE_MEMCACHED_USE_VERSION) $truthStrings) }}
rewrite_by_lua_block {
if ngx.var.skip_cache == "1" then
return
end

local memcached = require "resty.memcached"

local function identity(key) return key end

local memc, err = memcached:new{
-- don't escape/unescape keys
key_transform = { identity, identity }
}

if not memc then
ngx.log(ngx.ERR, "failed to instantiate memcached: ", err)
return
end

memc:set_timeout(1000)

local ok, err = memc:connect("{{ $memcachedHost }}:{{ $memcachedPort }}")
if not ok then
ngx.log(ngx.ERR, "failed to connect to memcached: ", err)
return
end

local versionKey = ngx.var.key_prefix .. "version"

local version, flags, err = memc:get(versionKey)
if err then
ngx.log(ngx.ERR, "failed to get memcached version: ", err)
return
end

if not version then
ngx.log(ngx.INFO, "memcached version not found, attempting to set one...")

version = tostring(os.time()) .. ":"
local ok, err = memc:set(versionKey, version)
if not ok then
ngx.log(ngx.ERR, "failed to set memcached version: ", err)
return
end
end

ngx.var.key = ngx.var.key_prefix .. version .. ngx.var.key_uid
ngx.var.escaped_key = ngx.var.escaped_key_prefix .. ngx.escape_uri(version) .. ngx.var.escaped_key_uid

local ok, err = memc:close()
if not ok then
ngx.say("failed to close memcached: ", err)
return
end
}

{{- else }}
# default key is nginx-cache:https$request_method$host$request_uri
set $key $key_prefix$key_uid;
set $escaped_key $escaped_key_prefix$escaped_key_uid;
{{- end }}

srcache_fetch_skip $skip_cache;
srcache_store_skip $skip_cache;
srcache_store_statuses {{ default "200 301 302" .Env.STACK_PAGE_CACHE_STORE_STATUSES }};

# https://github.com/openresty/srcache-nginx-module#srcache_response_cache_control
srcache_response_cache_control {{ default "on" .Env.STACK_PAGE_CACHE_RESPONSE_CACHE_CONTROL }};

srcache_fetch GET /.stack-cache-fetch $key;
srcache_store PUT /.stack-cache-store key=$escaped_key&exptime={{ default "360" .Env.STACK_PAGE_CACHE_EXPIRE_SECONDS | atoi }};

more_set_headers "x-cache-fetch $srcache_fetch_status";

{{- end }}