So, I have a wordpress website running behind nginx -> varnish -> httpd
The htaccess rule for wp-login.php is set as:
<Files wp-login.php> allow from client ip deny from all </Files> This used to work fine without using varnish, but when I put varnish in the middle between nginx and httpd, this caching issue or IP forwarding issue started happening.
Since I can easily change the webserver config, I can disable/enable varnish for a site easily. So, as a test, I changed the htaccess rule to:
<Files wp-login.php> allow from Server Public IP deny from all </Files> This made wp-login accessible to everyone. Then I disabled varnish and kept the Server public IP in htaccess, and now nobody can access the page (which is what is supposed to happen).
So, the culprit is varnish.
I have mod_cloudflare setup on apache. I've also tested by switching it to mod_remoteip to no avail.
Here's my nginx:443, varnish:82 and apache:8181 vhost templates (This IP 108.148.54.124 is an example for the server public IP):
server { listen 108.148.54.124:443 ssl http2; server_name %domain_idn% %alias_idn%; access_log /usr/local/apache/domlogs/%domain%.bytes bytes; access_log /usr/local/apache/domlogs/%domain%.log full; error_log /usr/local/apache/domlogs/%domain%.error.log error; ssl_certificate %ssl_cert_path%/%domain%.bundle; ssl_certificate_key %ssl_key_path%/%domain%.key; ssl_protocols TLSv1.3; ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA!RC4:EECDH:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 60m; location / { location ~.*\.(3gp|gif|jpg|jpeg|png|ico|wmv|avi|asf|asx|mpg|mpeg|mp4|pls|mp3|mid|wav|swf|flv|html|htm|txt|js|css|exe|zip|tar|rar|gz|tgz|bz2|uha|7z|doc|docx|xls|xlsx|pdf|iso|woff|ttf|svg|eot|sh|webp)$ { root %docroot%; expires max; try_files $uri $uri/ @backend; } error_page 405 = @backend; error_page 500 = @custom; add_header X-Cache "HIT from Backend"; add_header Strict-Transport-Security "max-age=31536000"; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; proxy_pass %proxy_protocol%://108.148.54.124:82; include proxy.inc; } location @backend { internal; proxy_pass %proxy_protocol%://108.148.54.124:82; include proxy.inc; } location @custom { internal; proxy_pass %proxy_protocol%://108.148.54.124:82; include proxy.inc; } location ~ .*\.(php|jsp|cgi|pl|py)?$ { proxy_pass %proxy_protocol%://108.148.54.124:82; include proxy.inc; } location ~ /\.ht {deny all;} location ~ /\.svn/ {deny all;} location ~ /\.git/ {deny all;} location ~ /\.hg/ {deny all;} location ~ /\.bzr/ {deny all;} location ~\.(ini|log|conf)$ {deny all;error_page 403 =404 / ;} disable_symlinks if_not_owner from=%docroot%; location /.well-known/acme-challenge { default_type "text/plain"; alias /usr/local/apache/autossl_tmp/.well-known/acme-challenge; } location /.well-known/pki-validation { default_type "text/plain"; alias /usr/local/apache/autossl_tmp/.well-known/acme-challenge; } } ..... backend %backend_domain% { .host = "108.148.54.124"; .port = "8181"; } sub vcl_recv { if (req.http.host ~ "%domain%") { set req.backend_hint = %backend_domain%; # Always cache the following file types for all users. if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(\?[a-z0-9]+)?$") { unset req.http.Cookie; } # Remove any Google Analytics based cookies set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", ""); # Do not cache AJAX requests. if (req.http.X-Requested-With == "XMLHttpRequest") { return(pass); } # Post requests will not be cached if (req.http.Authorization || req.method == "POST") { return (pass); } if (req.method != "GET" && req.method != "HEAD") { return (pass); } # Do not cache Authorized requests. if (req.http.Authorization) { return(pass); } # LetsEncrypt Certbot passthrough if (req.url ~ "^/\.well-known/acme-challenge/") { return (pass); } if (req.url ~ "^/\.well-known/pki-validation/") { return (pass); } # Forward client's IP to the backend if (req.restarts == 0) { if (req.http.X-Real-IP) { set req.http.X-Forwarded-For = req.http.X-Real-IP; } else if (req.http.X-Forwarded-For) { set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip; } else { set req.http.X-Forwarded-For = client.ip; } } ### Wordpress ### if (req.url ~ "(wp-admin|post\.php|edit\.php|wp-login)") { return(pass); } if (req.url ~ "/wp-cron.php" || req.url ~ "preview=true") { return (pass); } # WP-Affiliate if ( req.url ~ "\?ref=" ) { return (pass); } set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-1=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-time-1=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "wordpress_test_cookie=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "PHPSESSID=[^;]+(; )?", ""); return (hash); } } <VirtualHost 108.148.54.124:8443> ServerName %domain_idn% %domain_aliases% ServerAdmin webmaster@%domain% DocumentRoot %docroot% UseCanonicalName Off ScriptAlias /cgi-bin/ %docroot%/cgi-bin/ CustomLog /usr/local/apache/domlogs/%domain%.bytes bytes CustomLog /usr/local/apache/domlogs/%domain%.log combined ErrorLog /usr/local/apache/domlogs/%domain%.error.log ## Custom settings are loaded below this line (if any exist) # IncludeOptional "/usr/local/apache/conf/userdata/%user%/%domain%/*.conf" SSLEngine on SSLCertificateFile %ssl_cert_path%/%domain%.cert SSLCertificateKeyFile %ssl_key_path%/%domain%.key SSLCertificateChainFile %ssl_cert_path%/%domain%.bundle SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown <IfModule mod_userdir.c> UserDir disabled UserDir enabled %user% </IfModule> <IfModule mod_suexec.c> SuexecUserGroup %user% %group% </IfModule> <IfModule mod_suphp.c> suPHP_UserGroup %user% %group% suPHP_ConfigPath %home%/%user% </IfModule> <IfModule mod_ruid2.c> RMode config RUidGid %user% %group% </IfModule> <IfModule itk.c> AssignUserID %user% %group% </IfModule> <Directory "%docroot%"> AllowOverride All SSLRequireSSL Require all granted </Directory> <IfModule proxy_fcgi_module> <FilesMatch \.php$> SetHandler "proxy:%backend_fcgi%|fcgi://localhost" </FilesMatch> </IfModule> </VirtualHost> and this is the main nginx conf file:
user nobody; worker_processes auto; #worker_rlimit_nofile 65535; error_log /var/log/nginx/error.log crit; pid /var/run/nginx.pid; events { worker_connections 1024; use epoll; multi_accept on; } http { sendfile on; tcp_nopush on; tcp_nodelay on; client_header_timeout 3m; client_body_timeout 3m; client_max_body_size 256m; client_header_buffer_size 4k; client_body_buffer_size 256k; large_client_header_buffers 4 32k; send_timeout 3m; keepalive_timeout 60 60; reset_timedout_connection on; server_names_hash_max_size 1024; server_names_hash_bucket_size 1024; ignore_invalid_headers on; connection_pool_size 256; request_pool_size 4k; output_buffers 4 32k; postpone_output 1460; include mime.types; default_type application/octet-stream; # Compression gzip gzip on; gzip_vary on; gzip_disable "MSIE [1-6]\."; gzip_proxied any; gzip_min_length 512; gzip_comp_level 6; gzip_buffers 8 64k; gzip_types text/plain text/xml text/css text/js application/x-javascript application/xml image/png image/x-icon image/gif image/jpeg image/svg+xml application/xml+rss text/javascript application/atom+xml application/javascript application/json application/x-font-ttf font/opentype; # Proxy settings proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass_header Set-Cookie; proxy_connect_timeout 300; proxy_send_timeout 300; proxy_read_timeout 300; proxy_buffers 32 4k; proxy_cache_path /var/cache/nginx levels=2 keys_zone=cache:10m inactive=60m max_size=512m; proxy_cache_key "$host$request_uri $cookie_user"; proxy_temp_path /var/cache/nginx/temp; proxy_ignore_headers Expires Cache-Control; proxy_cache_use_stale error timeout invalid_header http_502; proxy_cache_valid any 1d; open_file_cache_valid 120s; open_file_cache_min_uses 2; open_file_cache_errors off; open_file_cache max=5000 inactive=30s; open_log_file_cache max=1024 inactive=30s min_uses=2; # SSL Settings ssl_session_cache shared:SSL:10m; ssl_protocols TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA!RC4:EECDH:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS"; # Logs log_format main '$remote_addr - $remote_user [$time_local] $request ' '"$status" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; log_format full '[$time_local] $remote_addr $remote_user - "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"'; log_format bytes '$body_bytes_sent'; #access_log /var/log/nginx/access.log main; access_log off; # Cache bypass map $http_cookie $no_cache { default 0; ~SESS 1; ~wordpress_logged_in 1; } # Include additional configuration include /etc/nginx/cloudflare.inc; include /etc/nginx/conf.d/*.conf; }