From 89717905fa5fbbf534702afff9fef57b4d8e9bc2 Mon Sep 17 00:00:00 2001 From: apedik Date: Wed, 13 Sep 2023 11:04:24 +0100 Subject: [PATCH] Varnish support Ssl proxy Magento production mode --- bin/docker-compose | 1 - docker/nginx/default.conf | 2 +- docker/nginx/m-servers.conf | 146 ++++++++++- docker/nginx/nginx.conf | 1 - docker/services.yml | 498 ++++++++++++++++++------------------ docker/varnish/default.vcl | 254 ++++++++++++++++++ 6 files changed, 651 insertions(+), 251 deletions(-) create mode 100644 docker/varnish/default.vcl diff --git a/bin/docker-compose b/bin/docker-compose index 0ef1811..a3f260c 100755 --- a/bin/docker-compose +++ b/bin/docker-compose @@ -61,4 +61,3 @@ if [[ "$PWD" == *"docker-stack"* || "$PWD" == *"htdocs"* ]]; then else ${REAL_DOCKER_COMPOSE_CMD} ${BASH_ARGVS[*]}; fi - diff --git a/docker/nginx/default.conf b/docker/nginx/default.conf index 06e56bb..65afbb2 100644 --- a/docker/nginx/default.conf +++ b/docker/nginx/default.conf @@ -1,5 +1,5 @@ server { - listen 80; + listen 8080; server_name _; access_log /var/log/nginx/default.access.log main; diff --git a/docker/nginx/m-servers.conf b/docker/nginx/m-servers.conf index b545e22..7a84f44 100644 --- a/docker/nginx/m-servers.conf +++ b/docker/nginx/m-servers.conf @@ -5,6 +5,141 @@ # Magento 2 focused PHP hosts server { listen 80; + # Docker DNS resolver is .11, NOT A MISTAKE + resolver 127.0.0.11 ipv6=off; + + server_name ~^.*\.(loc|local|localhost)$; + + error_log /dev/stdout; + access_log /dev/stdout; + + # Magento Vars + set $MAGE_MODE production; # or production or developer + + root /var/www/htdocs/${http_host}${root_subfolder}; + + index index.php; + autoindex off; + charset off; + client_max_body_size 20M; + + add_header 'X-Content-Type-Options' 'nosniff'; + add_header 'X-XSS-Protection' '1; mode=block'; + + location / { + try_files $uri $uri/ /index.php?$args; + } + + location /pub { + location ~ ^/pub/media/(downloadable|customer|import|theme_customization/.*\.xml) { + deny all; + } + set $php_host ${PHP_HOSTM2}; + alias /var/www/htdocs/eg-m2-opensource.loc/pub; + add_header X-Frame-Options "SAMEORIGIN"; + } + + location /static/ { + if ($MAGE_MODE = "production") { + expires max; + } + location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { + add_header Cache-Control "public"; + add_header X-Frame-Options "SAMEORIGIN"; + expires +1y; + + if (!-f $request_filename) { + rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; + } + } + location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { + add_header Cache-Control "no-store"; + add_header X-Frame-Options "SAMEORIGIN"; + expires off; + + if (!-f $request_filename) { + rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; + } + } + if (!-f $request_filename) { + rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; + } + add_header X-Frame-Options "SAMEORIGIN"; + } + + location /media/ { + try_files $uri $uri/ =404; + + location ~ ^/media/theme_customization/.*\.xml { + deny all; + } + + location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { + add_header Cache-Control "public"; + add_header X-Frame-Options "SAMEORIGIN"; + expires +1y; + try_files $uri $uri/ =404; + } + location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { + add_header Cache-Control "no-store"; + add_header X-Frame-Options "SAMEORIGIN"; + expires off; + try_files $uri $uri/ =404; + } + add_header X-Frame-Options "SAMEORIGIN"; + } + + location /media/customer/ { + deny all; + } + + location /media/downloadable/ { + deny all; + } + + location /media/import/ { + deny all; + } + + location ~ cron\.php { + deny all; + } + + location ~ (index|url|messages|test|get|static|report|404|503)\.php$ { + try_files $uri =404; + set $php_host ${PHP_HOSTM2}; + fastcgi_pass unix:/tmp/${php_host}-fpm.sock; + + fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off"; + fastcgi_param PHP_VALUE "memory_limit=256M \n max_execution_time=600"; + fastcgi_read_timeout 600s; + fastcgi_connect_timeout 600s; + fastcgi_buffers 16 256k; + fastcgi_buffer_size 256k; + + fastcgi_param MAGE_MODE $MAGE_MODE; + fastcgi_param MAGE_RUN_TYPE store; + fastcgi_param MAGE_RUN_CODE $MAGE_RUN_CODE; + + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PHP_VALUE auto_prepend_file=$PHP_PREPENDM2; + include fastcgi_params; + } + + # mftf magento cli support + location ~* ^/dev/tests/acceptance/utils($|/) { + root /var/www/htdocs/${http_host}/; + location ~ ^/dev/tests/acceptance/utils/command.php { + set $php_host ${PHP_HOSTM2}; + fastcgi_pass unix:/tmp/${php_host}-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + } +} +server { listen 443 ssl; http2 on; # Docker DNS reolver is .11, NOT A MISTAKE @@ -18,7 +153,7 @@ server { access_log /dev/stdout; # Magento Vars - set $MAGE_MODE developer; # or production or developer + set $MAGE_MODE production; # or production or developer root /var/www/htdocs/${http_host}${root_subfolder}; @@ -34,7 +169,6 @@ server { root /var/www/htdocs/${http_host}/; location ~ ^/setup/index.php { set $php_host ${PHP_HOSTM2}; -# fastcgi_pass ${PHP_HOSTM2}:9000; fastcgi_pass unix:/tmp/${php_host}-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; @@ -56,7 +190,6 @@ server { location ~ ^/update/index.php { fastcgi_split_path_info ^(/update/index.php)(/.+)$; set $php_host ${PHP_HOSTM2}; -# fastcgi_pass ${PHP_HOSTM2}:9000; fastcgi_pass unix:/tmp/${php_host}-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; @@ -76,6 +209,13 @@ server { location / { try_files $uri $uri/ /index.php?$args; + set $varnish_upstream varnish; + proxy_pass http://$varnish_upstream: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; } location /pub { diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index 04e06bb..7af27ce 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -1,4 +1,3 @@ - user nginx; worker_processes 1; diff --git a/docker/services.yml b/docker/services.yml index 560ace4..096b22e 100644 --- a/docker/services.yml +++ b/docker/services.yml @@ -1,199 +1,207 @@ version: '3' services: - mailhog: - image: mailhog/mailhog - ports: - - "1025:1025" - - "8025:8025" + mailhog: + image: mailhog/mailhog + ports: + - "1025:1025" + - "8025:8025" - php74: - user: "${USERID}:${GROUPID}" - build: - context: ./php/7.4 - volumes: - - composer_cache:${HOME}/.composer/cache - - ssh-key:/var/www/htdocs/.ssh - - web_files:/var/www/htdocs - - shared-tmp:/tmp - environment: - MYSQL_USER: ${MYSQL_USER} - MYSQL_PASSWORD: ${MYSQL_PASSWORD} - XDEBUG_MODE: ${XDEBUG_MODE} + php74: + user: "${USERID}:${GROUPID}" + build: + context: ./php/7.4 + volumes: + - composer_cache:${HOME}/.composer/cache + - ssh-key:/var/www/htdocs/.ssh + - web_files:/var/www/htdocs + - shared-tmp:/tmp + environment: + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + XDEBUG_MODE: ${XDEBUG_MODE} - php81: - user: "${USERID}:${GROUPID}" - build: - context: ./php/8.1 - volumes: - - composer_cache:${HOME}/.composer/cache - - ssh-key:/var/www/htdocs/.ssh - - web_files:/var/www/htdocs - - shared-tmp:/tmp - environment: - MYSQL_USER: ${MYSQL_USER} - MYSQL_PASSWORD: ${MYSQL_PASSWORD} - XDEBUG_MODE: ${XDEBUG_MODE} + php81: + user: "${USERID}:${GROUPID}" + build: + context: ./php/8.1 + volumes: + - composer_cache:${HOME}/.composer/cache + - ssh-key:/var/www/htdocs/.ssh + - web_files:/var/www/htdocs + - shared-tmp:/tmp + environment: + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + XDEBUG_MODE: ${XDEBUG_MODE} - php82: - user: "${USERID}:${GROUPID}" - build: - context: ./php/8.2 - volumes: - - composer_cache:${HOME}/.composer/cache - - ssh-key:/var/www/htdocs/.ssh - - web_files:/var/www/htdocs - - shared-tmp:/tmp - - /var/run/docker.sock:/var/run/docker.sock - - docker-files:/var/www/htdocs/docker - - project-files:/var/www/htdocs/project - - bin-files:/var/www/htdocs/bin - environment: - MYSQL_USER: ${MYSQL_USER} - MYSQL_PASSWORD: ${MYSQL_PASSWORD} - XDEBUG_MODE: ${XDEBUG_MODE} + php82: + user: "${USERID}:${GROUPID}" + build: + context: ./php/8.2 + volumes: + - composer_cache:${HOME}/.composer/cache + - ssh-key:/var/www/htdocs/.ssh + - web_files:/var/www/htdocs + - shared-tmp:/tmp + - /var/run/docker.sock:/var/run/docker.sock + - docker-files:/var/www/htdocs/docker + - project-files:/var/www/htdocs/project + - bin-files:/var/www/htdocs/bin + environment: + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + XDEBUG_MODE: ${XDEBUG_MODE} + PHP_IDE_CONFIG: "serverName=docker" + extra_hosts: + - "host.docker.internal:host-gateway" - nginx: - build: - context: ./nginx - ports: - - 80:80 - - 443:443 - volumes: - - web_files:/var/www/htdocs - - shared-tmp:/tmp - networks: - default: - aliases: - - eg-m2-opensource.loc + nginx: + build: + context: ./nginx + volumes: + - web_files:/var/www/htdocs + - shared-tmp:/tmp + ports: + - "443:443" + - "80:80" - selenium: - image: selenium/standalone-chrome:latest - shm_size: '2gb' - ports: - - "4444:4444" - - "7900:7900" - depends_on: - - nginx + varnish: + image: varnish:7.3-alpine + volumes: + - ./varnish/default.vcl:/etc/varnish/default.vcl + depends_on: + - nginx + links: + - nginx - elasticsearch7: - build: - context: ./elasticsearch/7 - environment: - - xpack.security.enabled=false - - discovery.type=single-node - ulimits: - memlock: - soft: -1 - hard: -1 - nofile: - soft: 65536 - hard: 65536 - cap_add: - - IPC_LOCK - volumes: - - elasticsearch7-data:/usr/share/elasticsearch7/data - ports: - - 9207:9200 - - 9307:9300 + selenium: + image: selenium/standalone-chrome:latest + shm_size: '2gb' + ports: + - "4444:4444" + - "7900:7900" + depends_on: + - nginx - elasticsearch716: - build: - context: ./elasticsearch/7.16 - environment: - - xpack.security.enabled=false - - xpack.watcher.enabled=false - - xpack.monitoring.enabled=true - - discovery.type=single-node - - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - ulimits: - memlock: - soft: -1 - hard: -1 - nofile: - soft: 65536 - hard: 65536 - cap_add: - - IPC_LOCK - volumes: - - elasticsearch716-data:/usr/share/elasticsearch7/data - ports: - - 9716:9200 - - 9816:9300 + elasticsearch7: + build: + context: ./elasticsearch/7 + environment: + - xpack.security.enabled=false + - discovery.type=single-node + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + cap_add: + - IPC_LOCK + volumes: + - elasticsearch7-data:/usr/share/elasticsearch7/data + ports: + - 9207:9200 + - 9307:9300 - opensearch12: - build: - context: ./opensearch/1.2 - environment: - - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" - - node.name=opensearch123 - - discovery.type=single-node - - bootstrap.memory_lock=true - - plugins.security.disabled=true - - network.host=0.0.0.0 - ulimits: - memlock: - soft: -1 - hard: -1 + elasticsearch716: + build: + context: ./elasticsearch/7.16 + environment: + - xpack.security.enabled=false + - xpack.watcher.enabled=false + - xpack.monitoring.enabled=true + - discovery.type=single-node + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + cap_add: + - IPC_LOCK + volumes: + - elasticsearch716-data:/usr/share/elasticsearch7/data + ports: + - 9716:9200 + - 9816:9300 - opensearch25: - build: - context: ./opensearch/2.5 - environment: - - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" - - node.name=opensearch25 - - discovery.type=single-node - - bootstrap.memory_lock=true - - plugins.security.disabled=true - - network.host=0.0.0.0 - ulimits: - memlock: - soft: -1 - hard: -1 + opensearch12: + build: + context: ./opensearch/1.2 + environment: + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" + - node.name=opensearch123 + - discovery.type=single-node + - bootstrap.memory_lock=true + - plugins.security.disabled=true + - network.host=0.0.0.0 + ulimits: + memlock: + soft: -1 + hard: -1 - kibana: - container_name: kibana - image: docker.elastic.co/kibana/kibana:7.13.0 - depends_on: - - elasticsearch716 - - apm-server - environment: - - ELASTICSEARCH_URL=http://elasticsearch716:9200 - - ELASTICSEARCH_HOSTS=http://elasticsearch716:9200 - - xpack.apm.enabled=false - - xpack.encryptedSavedObjects.encryptionKey= + opensearch25: + build: + context: ./opensearch/2.5 + environment: + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" + - node.name=opensearch25 + - discovery.type=single-node + - bootstrap.memory_lock=true + - plugins.security.disabled=true + - network.host=0.0.0.0 + ulimits: + memlock: + soft: -1 + hard: -1 - apm-server: - image: docker.elastic.co/apm/apm-server:7.13.0 - cap_add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"] - cap_drop: ["ALL"] - depends_on: - - elasticsearch716 - command: > - apm-server -e - -E apm-server.rum.enabled=true - -E setup.kibana.host=kibana:5601 - -E setup.template.settings.index.number_of_replicas=0 - -E apm-server.kibana.enabled=true - -E apm-server.kibana.host=kibana:5601 - -E output.elasticsearch.hosts=["elasticsearch716:9200"] - healthcheck: - interval: 10s - retries: 12 - test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:8200/ + kibana: + container_name: kibana + image: docker.elastic.co/kibana/kibana:7.13.0 + depends_on: + - elasticsearch716 + - apm-server + environment: + - ELASTICSEARCH_URL=http://elasticsearch716:9200 + - ELASTICSEARCH_HOSTS=http://elasticsearch716:9200 + - xpack.apm.enabled=false + - xpack.encryptedSavedObjects.encryptionKey= - mysql57: - build: - context: ./mysql/5.7 - ports: - - 3307:3306 - volumes: - - mysql_data57:/var/lib/mysql - environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} - MYSQL_DATABASE: ${MYSQL_DATABASE} - MYSQL_USER: ${MYSQL_USER} - MYSQL_PASSWORD: ${MYSQL_PASSWORD} + apm-server: + image: docker.elastic.co/apm/apm-server:7.13.0 + cap_add: [ "CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID" ] + cap_drop: [ "ALL" ] + depends_on: + - elasticsearch716 + command: > + apm-server -e + -E apm-server.rum.enabled=true + -E setup.kibana.host=kibana:5601 + -E setup.template.settings.index.number_of_replicas=0 + -E apm-server.kibana.enabled=true + -E apm-server.kibana.host=kibana:5601 + -E output.elasticsearch.hosts=["elasticsearch716:9200"] + healthcheck: + interval: 10s + retries: 12 + test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:8200/ + + mysql57: + build: + context: ./mysql/5.7 + ports: + - 3307:3306 + volumes: + - mysql_data57:/var/lib/mysql + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} mysql80: build: @@ -211,74 +219,74 @@ services: MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} - redissession: - image: redis:5-alpine - command: redis-server /usr/local/etc/redis/redis.conf - volumes: - - ./redissession/redis.conf:/usr/local/etc/redis/redis.conf - - shared-tmp:/tmp + redissession: + image: redis:5-alpine + command: redis-server /usr/local/etc/redis/redis.conf + volumes: + - ./redissession/redis.conf:/usr/local/etc/redis/redis.conf + - shared-tmp:/tmp - rediscache: - image: redis:5-alpine - command: redis-server /usr/local/etc/redis/redis.conf - volumes: - - ./rediscache/redis.conf:/usr/local/etc/redis/redis.conf - - shared-tmp:/tmp + rediscache: + image: redis:5-alpine + command: redis-server /usr/local/etc/redis/redis.conf + volumes: + - ./rediscache/redis.conf:/usr/local/etc/redis/redis.conf + - shared-tmp:/tmp - redisfullpage: - image: redis:5-alpine - command: redis-server /usr/local/etc/redis/redis.conf - volumes: - - ./redisfullpage/redis.conf:/usr/local/etc/redis/redis.conf - - shared-tmp:/tmp + redisfullpage: + image: redis:5-alpine + command: redis-server /usr/local/etc/redis/redis.conf + volumes: + - ./redisfullpage/redis.conf:/usr/local/etc/redis/redis.conf + - shared-tmp:/tmp - rabbitmq: - image: rabbitmq:3.8-management - container_name: 'rabbitmq' - command: "/bin/bash -c \"rabbitmq-plugins enable rabbitmq_management; rabbitmq-plugins enable rabbitmq_mqtt; rabbitmq-plugins enable rabbitmq_web_mqtt; rabbitmq-plugins enable rabbitmq_amqp1_0; rabbitmq-server\"" - ports: - - 1883:1883 - volumes: - - ./rabbitmq/rabbitmq-isolated.conf:/etc/rabbitmq/rabbitmq.config + rabbitmq: + image: rabbitmq:3.8-management + container_name: 'rabbitmq' + command: "/bin/bash -c \"rabbitmq-plugins enable rabbitmq_management; rabbitmq-plugins enable rabbitmq_mqtt; rabbitmq-plugins enable rabbitmq_web_mqtt; rabbitmq-plugins enable rabbitmq_amqp1_0; rabbitmq-server\"" + ports: + - 1883:1883 + volumes: + - ./rabbitmq/rabbitmq-isolated.conf:/etc/rabbitmq/rabbitmq.config - dozzle: - container_name: dozzle - image: amir20/dozzle:latest - volumes: - - /var/run/docker.sock:/var/run/docker.sock + dozzle: + container_name: dozzle + image: amir20/dozzle:latest + volumes: + - /var/run/docker.sock:/var/run/docker.sock - lighthouse-server: - build: - context: ./lighthouse-server - ports: - - '9001:9001' - volumes: - - lhci-data:/data - cap_add: - - SYS_ADMIN + lighthouse-server: + build: + context: ./lighthouse-server + ports: + - '9001:9001' + volumes: + - lhci-data:/data + cap_add: + - SYS_ADMIN - phpstorm-server: - user: "${USERID}:${GROUPID}" - build: - context: ./phpstorm-server - ports: - - '5993:5993' - environment: - - RUNNING_PROJECT=${RUNNING_PROJECT} - volumes: - - ssh-key:/var/www/htdocs/.ssh - - web_files:/var/www/htdocs + phpstorm-server: + user: "${USERID}:${GROUPID}" + build: + context: ./phpstorm-server + ports: + - '5993:5993' + environment: + - RUNNING_PROJECT=${RUNNING_PROJECT} + volumes: + - ssh-key:/var/www/htdocs/.ssh + - web_files:/var/www/htdocs - vscode-server: - user: "${USERID}:${GROUPID}" - build: - context: ./vscode-server - ports: - - "8443:8443" - - "9000:9000" - environment: - - RUNNING_PROJECT=${RUNNING_PROJECT} - - VSCODE_SECURE=${VSCODE_SECURE} - volumes: - - ssh-key:/var/www/htdocs/.ssh - - web_files:/var/www/htdocs + vscode-server: + user: "${USERID}:${GROUPID}" + build: + context: ./vscode-server + ports: + - "8443:8443" + - "9000:9000" + environment: + - RUNNING_PROJECT=${RUNNING_PROJECT} + - VSCODE_SECURE=${VSCODE_SECURE} + volumes: + - ssh-key:/var/www/htdocs/.ssh + - web_files:/var/www/htdocs diff --git a/docker/varnish/default.vcl b/docker/varnish/default.vcl new file mode 100644 index 0000000..1dff1b0 --- /dev/null +++ b/docker/varnish/default.vcl @@ -0,0 +1,254 @@ +# VCL version 5.0 is not supported so it should be 4.0 even though actually used Varnish version is 6 +vcl 4.0; + +import std; +# The minimal Varnish version is 6.0 +# For SSL offloading, pass the following header in your proxy server or load balancer: 'X-Forwarded-Proto: https' + +backend default { + .host = "nginx"; + .port = "80"; + .first_byte_timeout = 600s; + .probe = { + .url = "/pub/health_check.php"; + .timeout = 2s; + .interval = 5s; + .window = 10; + .threshold = 5; + } +} + +acl purge { +} + +sub vcl_recv { + if (req.restarts > 0) { + set req.hash_always_miss = true; + } + + if (req.method == "PURGE") { + if (client.ip !~ purge) { + return (synth(405, "Method not allowed")); + } + # To use the X-Pool header for purging varnish during automated deployments, make sure the X-Pool header + # has been added to the response in your backend server config. This is used, for example, by the + # capistrano-magento2 gem for purging old content from varnish during it's deploy routine. + if (!req.http.X-Magento-Tags-Pattern && !req.http.X-Pool) { + return (synth(400, "X-Magento-Tags-Pattern or X-Pool header required")); + } + if (req.http.X-Magento-Tags-Pattern) { + ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern); + } + if (req.http.X-Pool) { + ban("obj.http.X-Pool ~ " + req.http.X-Pool); + } + return (synth(200, "Purged")); + } + + if (req.method != "GET" && + req.method != "HEAD" && + req.method != "PUT" && + req.method != "POST" && + req.method != "TRACE" && + req.method != "OPTIONS" && + req.method != "DELETE") { + /* Non-RFC2616 or CONNECT which is weird. */ + return (pipe); + } + + # We only deal with GET and HEAD by default + if (req.method != "GET" && req.method != "HEAD") { + return (pass); + } + + # Bypass customer, shopping cart, checkout + if (req.url ~ "/customer" || req.url ~ "/checkout") { + return (pass); + } + + # Bypass health check requests + if (req.url ~ "^/(pub/)?(health_check.php)$") { + return (pass); + } + + # Set initial grace period usage status + set req.http.grace = "none"; + + # normalize url in case of leading HTTP scheme and domain + set req.url = regsub(req.url, "^http[s]?://", ""); + + # collect all cookies + std.collect(req.http.Cookie); + + # Compression filter. See https://www.varnish-cache.org/trac/wiki/FAQ/Compression + if (req.http.Accept-Encoding) { + if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") { + # No point in compressing these + unset req.http.Accept-Encoding; + } elsif (req.http.Accept-Encoding ~ "gzip") { + set req.http.Accept-Encoding = "gzip"; + } elsif (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") { + set req.http.Accept-Encoding = "deflate"; + } else { + # unknown algorithm + unset req.http.Accept-Encoding; + } + } + + # Remove all marketing get parameters to minimize the cache objects + if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") { + set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); + set req.url = regsub(req.url, "[?|&]+$", ""); + } + + # Static files caching + if (req.url ~ "^/(pub/)?(media|static)/") { + # Static files should not be cached by default + #eturn (pass); + + # But if you use a few locales and don't use CDN you can enable caching static files by commenting previous line (#return (pass);) and uncommenting next 3 lines + unset req.http.Https; + unset req.http.X-Forwarded-Proto; + unset req.http.Cookie; + } + + # Bypass authenticated GraphQL requests without a X-Magento-Cache-Id + if (req.url ~ "/graphql" && !req.http.X-Magento-Cache-Id && req.http.Authorization ~ "^Bearer") { + return (pass); + } + + return (hash); +} + +sub vcl_hash { + if ((req.url !~ "/graphql" || !req.http.X-Magento-Cache-Id) && req.http.cookie ~ "X-Magento-Vary=") { + hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1")); + } + + # To make sure http users don't see ssl warning + if (req.http.X-Forwarded-Proto) { + hash_data(req.http.X-Forwarded-Proto); + } + + + if (req.url ~ "/graphql") { + call process_graphql_headers; + } +} + +sub process_graphql_headers { + if (req.http.X-Magento-Cache-Id) { + hash_data(req.http.X-Magento-Cache-Id); + + # When the frontend stops sending the auth token, make sure users stop getting results cached for logged-in users + if (req.http.Authorization ~ "^Bearer") { + hash_data("Authorized"); + } + } + + if (req.http.Store) { + hash_data(req.http.Store); + } + + if (req.http.Content-Currency) { + hash_data(req.http.Content-Currency); + } +} + +sub vcl_backend_response { + + set beresp.grace = 3d; + + if (beresp.http.content-type ~ "text") { + set beresp.do_esi = true; + } + + if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") { + set beresp.do_gzip = true; + } + + if (beresp.http.X-Magento-Debug) { + set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control; + } + + # cache only successfully responses and 404s that are not marked as private + if (beresp.status != 200 && + beresp.status != 404 && + beresp.http.Cache-Control ~ "private") { + set beresp.uncacheable = true; + set beresp.ttl = 86400s; + return (deliver); + } + + # validate if we need to cache it and prevent from setting cookie + if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) { + unset beresp.http.set-cookie; + } + + # If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass + if (beresp.ttl <= 0s || + beresp.http.Surrogate-control ~ "no-store" || + (!beresp.http.Surrogate-Control && + beresp.http.Cache-Control ~ "no-cache|no-store") || + beresp.http.Vary == "*") { + # Mark as Hit-For-Pass for the next 2 minutes + set beresp.ttl = 120s; + set beresp.uncacheable = true; + } + + # If the cache key in the Magento response doesn't match the one that was sent in the request, don't cache under the request's key + if (bereq.url ~ "/graphql" && bereq.http.X-Magento-Cache-Id && bereq.http.X-Magento-Cache-Id != beresp.http.X-Magento-Cache-Id) { + set beresp.ttl = 0s; + set beresp.uncacheable = true; + } + + return (deliver); +} + +sub vcl_deliver { + if (resp.http.x-varnish ~ " ") { + set resp.http.X-Magento-Cache-Debug = "HIT"; + set resp.http.Grace = req.http.grace; + } else { + set resp.http.X-Magento-Cache-Debug = "MISS"; + } + + # Not letting browser to cache non-static files. + if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(pub/)?(media|static)/") { + set resp.http.Pragma = "no-cache"; + set resp.http.Expires = "-1"; + set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0"; + } + + if (!resp.http.X-Magento-Debug) { + unset resp.http.Age; + } + unset resp.http.X-Magento-Debug; + unset resp.http.X-Magento-Tags; + unset resp.http.X-Powered-By; + unset resp.http.Server; + unset resp.http.X-Varnish; + unset resp.http.Via; + unset resp.http.Link; +} + +sub vcl_hit { + if (obj.ttl >= 0s) { + # Hit within TTL period + return (deliver); + } + if (std.healthy(req.backend_hint)) { + if (obj.ttl + 300s > 0s) { + # Hit after TTL expiration, but within grace period + set req.http.grace = "normal (healthy server)"; + return (deliver); + } else { + # Hit after TTL and grace expiration + return (restart); + } + } else { + # server is not healthy, retrieve from cache + set req.http.grace = "unlimited (unhealthy server)"; + return (deliver); + } +}