Created
March 30, 2026 18:08
-
-
Save jamct/1b54a82f2f3f1a3754cf8b6ac9056d4c to your computer and use it in GitHub Desktop.
WordPress with Vinyl Cache
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| vcl 4.1; | |
| import std; | |
| backend default { | |
| .host = "wordpress"; | |
| .port = "80"; | |
| } | |
| # Hostname, that is allowed to purge: | |
| acl purge { | |
| "wordpress"; | |
| } | |
| sub vcl_recv { | |
| # Remove empty query string parameters | |
| if (req.url ~ "\?$") { | |
| set req.url = regsub(req.url, "\?$", ""); | |
| } | |
| set req.url = std.querysort(req.url); | |
| unset req.http.proxy; | |
| if (!req.http.X-Forwarded-Proto) { | |
| if(std.port(server.ip) == 443) { | |
| set req.http.X-Forwarded-Proto = "https"; | |
| } else { | |
| set req.http.X-Forwarded-Proto = "http"; | |
| } | |
| } | |
| if(req.method == "PURGE") { | |
| if(!client.ip ~ purge) { | |
| return(synth(405,"PURGE not allowed for this IP")); | |
| } | |
| if (req.http.X-Purge-Method == "regex") { | |
| ban("obj.http.x-url ~ " + req.url + " && obj.http.x-host == " + req.http.host); | |
| return(synth(200, "Purged")); | |
| } | |
| ban("obj.http.x-url == " + req.url + " && obj.http.x-host == " + req.http.host); | |
| return(synth(200, "Purged")); | |
| } | |
| # Only handle relevant HTTP request methods | |
| if ( | |
| req.method != "GET" && | |
| req.method != "HEAD" && | |
| req.method != "PUT" && | |
| req.method != "POST" && | |
| req.method != "PATCH" && | |
| req.method != "TRACE" && | |
| req.method != "OPTIONS" && | |
| req.method != "DELETE" | |
| ) { | |
| return (pipe); | |
| } | |
| # Remove tracking query string parameters used by analytics tools | |
| if (req.url ~ "(\?|&)(_branch_match_id|_bta_[a-z]+|_bta_c|_bta_tid|_ga|_gl|_ke|_kx|campid|cof|customid|cx|dclid|dm_i|ef_id|epik|fbclid|gad_source|gbraid|gclid|gclsrc|gdffi|gdfms|gdftrk|hsa_acc|hsa_ad|hsa_cam|hsa_grp|hsa_kw|hsa_mt|hsa_net|hsa_src|hsa_tgt|hsa_ver|ie|igshid|irclickid|matomo_campaign|matomo_cid|matomo_content|matomo_group|matomo_keyword|matomo_medium|matomo_placement|matomo_source|mc_[a-z]+|mc_cid|mc_eid|mkcid|mkevt|mkrid|mkwid|msclkid|mtm_campaign|mtm_cid|mtm_content|mtm_group|mtm_keyword|mtm_medium|mtm_placement|mtm_source|nb_klid|ndclid|origin|pcrid|piwik_campaign|piwik_keyword|piwik_kwd|pk_campaign|pk_keyword|pk_kwd|redirect_log_mongo_id|redirect_mongo_id|rtid|s_kwcid|sb_referer_host|sccid|si|siteurl|sms_click|sms_source|sms_uph|srsltid|toolid|trk_contact|trk_module|trk_msg|trk_sid|ttclid|twclid|utm_[a-z]+|utm_campaign|utm_content|utm_creative_format|utm_id|utm_marketing_tactic|utm_medium|utm_source|utm_source_platform|utm_term|vmcid|wbraid|yclid|zanpid)=") { | |
| set req.url = regsuball(req.url, "(_branch_match_id|_bta_[a-z]+|_bta_c|_bta_tid|_ga|_gl|_ke|_kx|campid|cof|customid|cx|dclid|dm_i|ef_id|epik|fbclid|gad_source|gbraid|gclid|gclsrc|gdffi|gdfms|gdftrk|hsa_acc|hsa_ad|hsa_cam|hsa_grp|hsa_kw|hsa_mt|hsa_net|hsa_src|hsa_tgt|hsa_ver|ie|igshid|irclickid|matomo_campaign|matomo_cid|matomo_content|matomo_group|matomo_keyword|matomo_medium|matomo_placement|matomo_source|mc_[a-z]+|mc_cid|mc_eid|mkcid|mkevt|mkrid|mkwid|msclkid|mtm_campaign|mtm_cid|mtm_content|mtm_group|mtm_keyword|mtm_medium|mtm_placement|mtm_source|nb_klid|ndclid|origin|pcrid|piwik_campaign|piwik_keyword|piwik_kwd|pk_campaign|pk_keyword|pk_kwd|redirect_log_mongo_id|redirect_mongo_id|rtid|s_kwcid|sb_referer_host|sccid|si|siteurl|sms_click|sms_source|sms_uph|srsltid|toolid|trk_contact|trk_module|trk_msg|trk_sid|ttclid|twclid|utm_[a-z]+|utm_campaign|utm_content|utm_creative_format|utm_id|utm_marketing_tactic|utm_medium|utm_source|utm_source_platform|utm_term|vmcid|wbraid|yclid|zanpid)=[-_A-z0-9+(){}%.*]+&?", ""); | |
| set req.url = regsub(req.url, "[?|&]+$", ""); | |
| } | |
| # Only cache GET and HEAD requests | |
| if (req.method != "GET" && req.method != "HEAD") { | |
| set req.http.X-Cacheable = "NO:REQUEST-METHOD"; | |
| return(pass); | |
| } | |
| # Mark static files with the X-Static-File header, and remove any cookies | |
| # X-Static-File is also used in vcl_backend_response to identify static files | |
| if (req.url ~ "^[^?]*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|ogg|ogm|opus|otf|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?.*)?$") { | |
| set req.http.X-Static-File = "true"; | |
| unset req.http.Cookie; | |
| return(hash); | |
| } | |
| if ( | |
| req.http.Cookie ~ "wordpress_(?!test_)[a-zA-Z0-9_]+|wp-postpass|comment_author_[a-zA-Z0-9_]+|woocommerce_cart_hash|woocommerce_items_in_cart|wp_woocommerce_session_[a-zA-Z0-9]+|wordpress_logged_in_|comment_author|PHPSESSID" || | |
| req.http.Authorization || | |
| req.url ~ "add_to_cart" || | |
| req.url ~ "edd_action" || | |
| req.url ~ "nocache" || | |
| req.url ~ "^/addons" || | |
| req.url ~ "^/bb-admin" || | |
| req.url ~ "^/bb-login.php" || | |
| req.url ~ "^/bb-reset-password.php" || | |
| req.url ~ "^/cart" || | |
| req.url ~ "^/checkout" || | |
| req.url ~ "^/control.php" || | |
| req.url ~ "^/login" || | |
| req.url ~ "^/logout" || | |
| req.url ~ "^/lost-password" || | |
| req.url ~ "^/my-account" || | |
| req.url ~ "^/product" || | |
| req.url ~ "^/register" || | |
| req.url ~ "^/register.php" || | |
| req.url ~ "^/server-status" || | |
| req.url ~ "^/signin" || | |
| req.url ~ "^/signup" || | |
| req.url ~ "^/stats" || | |
| req.url ~ "^/wc-api" || | |
| req.url ~ "^/wp-admin" || | |
| req.url ~ "^/wp-comments-post.php" || | |
| req.url ~ "^/wp-cron.php" || | |
| req.url ~ "^/wp-login.php" || | |
| req.url ~ "^/wp-activate.php" || | |
| req.url ~ "^/wp-mail.php" || | |
| req.url ~ "^/wp-login.php" || | |
| req.url ~ "^\?add-to-cart=" || | |
| req.url ~ "^\?wc-api=" || | |
| req.url ~ "^/preview=" || | |
| req.url ~ "^/\.well-known/acme-challenge/" | |
| ) { | |
| set req.http.X-Cacheable = "NO:Logged in/Got Sessions"; | |
| if(req.http.X-Requested-With == "XMLHttpRequest") { | |
| set req.http.X-Cacheable = "NO:Ajax"; | |
| } | |
| return(pass); | |
| } | |
| # Remove any cookies left | |
| unset req.http.Cookie; | |
| return(hash); | |
| } | |
| sub vcl_hash { | |
| if(req.http.X-Forwarded-Proto) { | |
| # Create cache variations depending on the request protocol | |
| hash_data(req.http.X-Forwarded-Proto); | |
| } | |
| } | |
| sub vcl_backend_response { | |
| # Inject URL & Host header into the object for asynchronous banning purposes | |
| set beresp.http.x-url = bereq.url; | |
| set beresp.http.x-host = bereq.http.host; | |
| # If we dont get a Cache-Control header from the backend | |
| # we default to 1h cache for all objects | |
| if (!beresp.http.Cache-Control) { | |
| set beresp.ttl = 1h; | |
| set beresp.http.X-Cacheable = "YES:Forced"; | |
| } | |
| # If the file is marked as static we cache it for 1 day | |
| if (bereq.http.X-Static-File == "true") { | |
| unset beresp.http.Set-Cookie; | |
| set beresp.http.X-Cacheable = "YES:Forced"; | |
| set beresp.ttl = 1d; | |
| } | |
| # Remove the Set-Cookie header when a specific Wordfence cookie is set | |
| if (beresp.http.Set-Cookie ~ "wfvt_|wordfence_verifiedHuman") { | |
| unset beresp.http.Set-Cookie; | |
| } | |
| if (beresp.http.Set-Cookie) { | |
| set beresp.http.X-Cacheable = "NO:Got Cookies"; | |
| } elseif(beresp.http.Cache-Control ~ "private") { | |
| set beresp.http.X-Cacheable = "NO:Cache-Control=private"; | |
| } | |
| } | |
| sub vcl_deliver { | |
| # Debug header | |
| if(req.http.X-Cacheable) { | |
| set resp.http.X-Cacheable = req.http.X-Cacheable; | |
| } elseif(obj.uncacheable) { | |
| if(!resp.http.X-Cacheable) { | |
| set resp.http.X-Cacheable = "NO:UNCACHEABLE"; | |
| } | |
| } elseif(!resp.http.X-Cacheable) { | |
| set resp.http.X-Cacheable = "YES"; | |
| } | |
| # Cleanup of headers | |
| unset resp.http.x-url; | |
| unset resp.http.x-host; | |
| unset resp.http.x-varnish; | |
| unset resp.http.x-powered-by; | |
| unset resp.http.via; | |
| #unset resp.http.x-cacheable; | |
| unset resp.http.server; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| services: | |
| db: | |
| image: mysql | |
| volumes: | |
| - ./data/sql/database/:/var/lib/mysql | |
| restart: always | |
| environment: | |
| MYSQL_ROOT_PASSWORD: secretwordpress | |
| MYSQL_DATABASE: wordpress | |
| MYSQL_USER: wordpress | |
| MYSQL_PASSWORD: wordpress | |
| wordpress: | |
| depends_on: | |
| - db | |
| #ports: | |
| # - 8080:80 | |
| image: wordpress:latest | |
| restart: always | |
| environment: | |
| WORDPRESS_DB_HOST: db:3306 | |
| WORDPRESS_DB_USER: wordpress | |
| WORDPRESS_DB_PASSWORD: wordpress | |
| working_dir: /var/www/html | |
| volumes: | |
| - ./data/wp/wp-content:/var/www/html/wp-content | |
| vinyl: | |
| image: varnish:stable | |
| depends_on: | |
| - wordpress | |
| ports: | |
| - 8080:80 | |
| environment: | |
| - VARNISH_SIZE=2G | |
| volumes: | |
| - ./default.vcl:/etc/varnish/default.vcl:ro | |
| tmpfs: /var/lib/varnish:exec |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import http from "k6/http"; | |
| import { check, sleep } from "k6"; | |
| export const options = { | |
| stages: [ | |
| { duration: "30s", target: 100 }, | |
| { duration: "1m", target: 500 }, | |
| { duration: "0", target: 1000 }, | |
| { duration: "20s", target: 1000 }, | |
| { duration: "20s", target: 0 }, | |
| ], | |
| }; | |
| export default function () { | |
| let res = http.get("http://localhost:8080/lasttest"); | |
| check(res, { "status was 200": (r) => r.status == 200 }); | |
| sleep(1); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment