我网站的 https 版本出现混合内容错误。
我有以下设置
端口 443 上的流量正在运行 Hitch TLS 代理,该代理被转发到 Varnish,并最终在 Apache Web Server 中结束。
我的想法是我网站上的第一个请求通过 HTTPS 加载,但所有后续请求都由 Varnish 在 HTTP 中直接处理。
我需要帮助弄清楚如何在客户端通过 TLS 加载所有网址。
这是我的 Hitch TLS 代理配置文件
# Upstream server address.
backend = "[127.0.0.1]:8443"
pem-file = {
cert = "/etc/letsencrypt/live/staging1.sainikbiswas.com/fullchain.pem"
private-key = "/etc/letsencrypt/live/staging1.sainikbiswas.com/privkey.pem"
}
ocsp-dir = "/var/lib/hitch-ocsp"
ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
# List of allowed TLS ciphers.
ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHAA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RRSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
# Enforce server cipher list order
#
# type: boolean
prefer-server-ciphers = off
tls-protos = TLSv1.2 TLSv1.3
alpn-protos = "h2, http/1.1"
workers = 4
backlog = 100
keepalive = 3600
chroot = ""
user = "hitch"
group = "hitch"
syslog = on
syslog-facility = "daemon"
daemon = on
write-ip = off
write-proxy-v1 = off
write-proxy-v2 = on
proxy-proxy = off
sni-nomatch-abort = off
Apache 2 配置文件
<VirtualHost *:8080>
ServerName staging1.sainikbiswas.com
ServerAdmin [email protected]
DocumentRoot "/home/sainikbiswas/domains/sainikbiswas-com/public"
<Directory /home/sainikbiswas/domains/sainikbiswas-com/public>
#Disable .htaccess file
AllowOverride None
#WordPress Rewrite Rules
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
#Disable File Index
Options -Indexes -FollowSymLinks +SymLinksIfOwnerMatch
#Enable Access to the Document Root
Require all granted
</Directory>
</VirtualHost>
清漆配置文件
vcl 4.1;
import proxy;
import std;
# Default backend definition. Set this to point to your content server.
backend default {
.host = "127.0.0.1";
.port = "8080";
}
# Add hostnames, IP addresses and subnets that are allowed to purge content
acl purge {
"localhost";
"127.0.0.1";
"::1";
}
sub vcl_recv {
if ((req.http.X-Forwarded-Proto && req.http.X-Forwarded-Proto != "https") ||
(req.http.Scheme && req.http.Scheme != "https")) {
return (synth(750));
} elseif (!req.http.X-Forwarded-Proto &&
!req.http.Scheme && !proxy.is_ssl()) {
return (synth(750));
}
# WordPress Varnish Configuration
# Remove empty query string parameters
# e.g.: www.example.com/index.html?
if (req.url ~ "\?$") {
set req.url = regsub(req.url, "\?$", "");
}
# Remove port number from host header
set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");
# Sorts query string parameters alphabetically for cache normalization purposes
set req.url = std.querysort(req.url);
# Remove the proxy header to mitigate the httpoxy vulnerability
# See https://httpoxy.org/
unset req.http.proxy;
# Add X-Forwarded-Proto header when using https
if(!req.http.X-Forwarded-Proto) {
if (proxy.is_ssl()) {
set req.http.X-Forwarded-Proto = "https";
} else {
set req.http.X-Forwarded-Proto = "http";
}
}
# Purge logic to remove objects from the cache.
# Tailored to the Proxy Cache Purge WordPress plugin
# See https://wordpress.org/plugins/varnish-http-purge/
if(req.method == "PURGE") {
if(!client.ip ~ purge) {
return(synth(405,"PURGE not allowed for this IP address"));
}
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 ~ "(\?|&)(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteur
l)=") {
set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|utm_content|gcli
d|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "");
set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|utm_content|gcl
id|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?");
set req.url = regsub(req.url, "\?&", "?");
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|t
ar|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);
}
# No caching of special URLs, logged in users and some plugins
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]+|wordp
ress_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_synth {
if (resp.status == 750) {
set resp.status = 301;
set resp.http.location = "https://" + req.http.Host + req.url;
set resp.reason = "Moved";
return (deliver);
}
}
sub vcl_hash {
#WordPress Varnish Configuration
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 {
if(beresp.http.Vary) {
set beresp.http.Vary = beresp.http.Vary + ", X-Forwarded-Proto";
} else {
set beresp.http.Vary = "X-Forwarded-Proto";
}
# 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;
}
我尝试从 Varnish 教程中实现这一点。我认为这会将所有 HTTP 请求转发到 HTTPS。但很可能只有第一个请求被重定向到 HTTPS,页面上的后续链接通过 HTTP 加载,该链接被浏览器阻止为混合内容错误。
vcl 4.1;
import proxy;
backend default {
.host = "127.0.0.1";
.port = 8080;
}
sub vcl_recv {
if ((req.http.X-Forwarded-Proto && req.http.X-Forwarded-Proto != "https") ||
(req.http.Scheme && req.http.Scheme != "https")) {
return (synth(750));
} elseif (!req.http.X-Forwarded-Proto && !req.http.Scheme && !proxy.is_ssl()) {
return (synth(750));
}
}
sub vcl_synth {
if (resp.status == 750) {
set resp.status = 301;
set resp.http.location = "https://" + req.http.Host + req.url;
set resp.reason = "Moved";
return (deliver);
}
}
我尝试在 wp-config.php 文件中设置以下常量,看看强制应用程序中的 URL 是否会进行更改,但这不起作用。
define( 'WP_HOME', 'https://staging1.sainikbiswas.com' );
define( 'WP_SITEURL', 'https://staging1.sainikbiswas.com');
从表面上看,你做的一切都是正确的。使用 TLS 代理时,您需要执行以下几项操作来避免混合内容:
-a :8443,PROXY
)X-Forwarded-Proto
标题proxy
标头,请使用 VCL 中的 proxy.is_ssl()
VMOD 调用 X-Forwarded-Proto
,并适当设置标头X-Forwarded-Proto
标头值添加到哈希中以创建协议感知(例如 hash_data("X-Forwarded-Proto")
)再一次,看起来你已经做了所有这些事情。
澄清一下:Varnish Cache(开源)本身并不处理 HTTPS,并且当 TLS 在其他地方卸载时,通过 PROXY 协议具有 TLS 感知。
这就是为什么
X-Forwarded-Proto
标头如此有用:它宣布初始连接的协议。您可以将以下 PHP 代码添加到您的 WordPress 设置中以强制支持 X-Forwarded-Proto
:
if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false)
$_SERVER['HTTPS']='on';
}
如果这没有帮助,请通过
varnishlog
提供一些相关的日志输出供我检查。
假设我们正在监控主页,您将提供以下命令的输出:
sudo varnishlog -g request -q "ReqUrl eq '/'"
运行此命令时请确保您的缓存为空,否则我们不知道后端如何响应您的请求。