HTTP → HTTPS redirect +Additional Recipients issue
-
im having issues with
“HTTP → HTTPS redirect0/1The site is reachable over plain HTTP without a redirect to HTTPS”
and Im getting the “Administrator login detected” email to my “Additional Recipients” email but not the email code for some reason.
-
Hi again @akgt 🙂
Two separate things here, let me take them one at a time.
HTTP -> HTTPS redirect: That check asks your own server for the plain http:// version of your home URL and passes only if the very first answer is a 3xx redirect whose Location starts with https://. It fails in a few cases that are not really a broken site: a two hop redirect (http://domain -> http://www.domain -> https://www.domain), a redirect done by a CDN at the edge (Cloudflare “Always Use HTTPS”), or a cached page served without running PHP.
Could you run these two from any machine and paste the output?
curl -I http://yourdomain.com/
curl -I http://www.yourdomain.com/Also, do you use Cloudflare or a page cache plugin, and is “Force HTTPS” enabled in Vigilant under Headers? If your first Location line already points to an https:// URL, the site is fine and the probe is what needs fixing on my side.
The 2FA code: This one is working as designed, the verification code is always sent to the email address of the account that is logging in, the one in your user profile, never to the Additional Recipients list. That list is only for administrative notices (admin login, scan results, alerts), and codes are deliberately kept out of it so a login secret never lands in a shared mailbox.
One thing makes me think something else is going on, though. The “Administrator login detected” notice is sent on wp_login, which only fires once the login is fully completed. So if you get that email and you were never asked for a code, 2FA by email is not running for your account at all. Could you check: does the login screen ask you for a code? In Vigilant, under Login Security, is the 2FA method set to Email or to Authenticator app, and is your role included in the enforced roles? And what address does your own user profile have?
Fernando
I’m not using Cloudflare at the moment, my site is loading as HTTPS, and redirects to https but its not detecting. im not using a cache plugin, and on most sites its working but not some new sites I’m working on, i’ve kept the setting the same. any setting to select or reinstall Really Simple Security again ?
Hi Fernando,
Regarding HTTP -> HTTPS redirect: I’m not using Cloudflare or a page cache plugin on these new sites. my settings https://ibb.co/0yms8P0v . The site loads fine via HTTPS in a browser, but when running
curlon plain HTTP, the server returns a403 Forbiddeninstead of a redirect.Here is the
curl -Ioutput:Plaintext
HTTP/1.1 403 Forbidden Connection: Keep-Alive Keep-Alive: timeout=5, max=100 Content-Type: text/html; charset=UTF-8 Expires: Wed, 11 Jan 1984 05:00:00 GMT Cache-Control: no-cache, must-revalidate, max-age=0, no-store, private Date: Tue, 22 Sep 2026 21:41:22 GMT Vary: User-Agent X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Strict-Transport-Security: max-age=31536000; includeSubDomains Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(self), usb=() Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https: blob:; font-src 'self' data: https:; connect-src 'self' https: wss: blob:; media-src 'self' https: blob:; frame-src 'self' https: blob:; frame-ancestors 'self'; base-uri 'self'; form-action 'self' https:; object-src 'none'; worker-src 'self' blob:; upgrade-insecure-requests Cross-Origin-Opener-Policy: same-origin-allow-popups Cross-Origin-Resource-Policy: cross-originRegarding the 2FA Code: You were spot on! The user profile email address was set differently on these new sites. Once I updated my user profile email to match, the 2FA verification code emails started coming through right away. That part is now completely resolved.
Thanks!
Good news on the 2FA side, and thanks for confirming it 🙂
About the redirect check, that 403 is Vigilant itself, not your server. The firewall blocks the curl user agent as a bad bot, which is why the response carries all of Vigilant’s security headers: WordPress ran, the firewall stopped the request, and it never reached the redirect. So that output tells us nothing about whether your site redirects.
Could you run these two instead, from any machine?
curl -I -A 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36' http://yourdomain.com/curl -I -A 'Vigilant-Security-Analyzer/1.0' http://yourdomain.com/The second one uses the exact user agent the Security Check uses for its own probe, so the two together tell us which of these is happening:
- Browser gets a 301 to https and the analyzer one gets 403 or 429: the firewall is blocking my own probe, the redirect works fine, and the fix belongs in the plugin.
- Both get a 301: your site is fine and the probe fails only when it runs from the server itself.
- Both get a 200: the redirect really is not happening on plain HTTP.
Two more questions: is Really Simple Security still active alongside Vigilant, and do you also have “Force HTTPS” enabled in Vigilant under Headers? You only need one of the two doing the redirect. There is nothing to reinstall, and no setting to change while we find out which case this is.
Either way, if your site loads over HTTPS in a browser, you are not exposed. That line is one point in the score and I would rather fix the check than have you change a working setup.
Thanks!
Fernando
Hi Fernando
I’ve got the following results.
HTTP/1.1 200 OK
Connection: Keep-Alive
Keep-Alive: timeout=5, max=100
Content-Type: text/html; charset=UTF-8
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache; private
Date: Tue, 22 Sep 2026 22:16:38 GMT
Vary: User-Agent
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Strict-Transport-Security: max-age=31536000; includeSubDomains
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(self), usb=()
Content-Security-Policy: default-src ‘self’; script-src ‘self’ ‘unsafe-inline’ ‘unsafe-eval’ https:; style-src ‘self’ ‘unsafe-inline’ https:; img-src ‘self’ data: https: blob:; font-src ‘self’ data: https:; connect-src ‘self’ https: wss: blob:; media-src ‘self’ https: blob:; frame-src ‘self’ https: blob:; frame-ancestors ‘self’; base-uri ‘self’; form-action ‘self’ https:; object-src ‘none’; worker-src ‘self’ blob:; upgrade-insecure-requests
Cross-Origin-Opener-Policy: same-origin-allow-popups
Cross-Origin-Resource-Policy: cross-originHTTP/1.1 200 OK
Connection: Keep-Alive
Keep-Alive: timeout=5, max=100
Content-Type: text/html; charset=UTF-8
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache; private
Date: Tue, 22 Sep 2026 22:17:46 GMT
Vary: User-Agent
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Strict-Transport-Security: max-age=31536000; includeSubDomains
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(self), usb=()
Content-Security-Policy: default-src ‘self’; script-src ‘self’ ‘unsafe-inline’ ‘unsafe-eval’ https:; style-src ‘self’ ‘unsafe-inline’ https:; img-src ‘self’ data: https: blob:; font-src ‘self’ data: https:; connect-src ‘self’ https: wss: blob:; media-src ‘self’ https: blob:; frame-src ‘self’ https: blob:; frame-ancestors ‘self’; base-uri ‘self’; form-action ‘self’ https:; object-src ‘none’; worker-src ‘self’ blob:; upgrade-insecure-requests
Cross-Origin-Opener-Policy: same-origin-allow-popups
Cross-Origin-Resource-Policy: cross-origin.Im getting 301: on my other sites
No, I dont use Really Simple Security
Thanks
Thanks, that settles it: the check is right and this is not a false positive. Both requests, the browser one and the one with my analyzer’s user agent, came back 200 over plain HTTP. Your site really is answering on http:// without redirecting.
Here is the interesting part. Your screenshot shows HSTS enabled with no red warning next to it, which means your WordPress Address is already an https:// one. On a site like that, WordPress redirects http to https all by itself through redirect_canonical(), with no plugin involved. So if neither WordPress nor Vigilant redirects, it is because PHP never sees an HTTP request: your host is terminating TLS in front of PHP and telling WordPress that every request is already HTTPS. Vigilant’s redirect checks is_ssl() first and steps aside when it says yes, which is exactly what is happening.
That also explains why your other sites answer 301: different hosting setup, not different settings.
To confirm it, I made you a small diagnostic file. Drop it in wp-content/mu-plugins/ via FTP and edit the
VIGDBG_TOKENline near the top first, putting any random word there. Then request, over plain HTTP:http://yourdomain.com/?vigdbg=YOUR-WORD<?php
/**
* Plugin Name: Vigilant Debug — HTTPS redirect
* Description: Reports what PHP sees about the scheme of a plain HTTP request, so a site that answers 200 over http:// can be told apart from one whose host makes WordPress believe every request is already HTTPS. Temporary tool — delete the file via FTP when finished.
* Version: 1.0.0
* Author: Fernando Tellado
* Requires at least: 6.2
* Requires PHP: 7.4
*
* Drop this file in wp-content/mu-plugins/ via FTP. No activation needed.
* Then request, over plain HTTP:
*
* http://yourdomain.com/?vigdbg=TOKEN
*
* replacing TOKEN with the value of VIGDBG_TOKEN below. Copy the whole answer.
* Delete the file from FTP when you're done.
*
* @package Vigilante
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Change this before sending the file to anyone. The report is plain text and
// says nothing secret, but a token keeps it out of crawlers and scanners.
define( 'VIGDBG_TOKEN', 'YOUR-WORD' );
/**
* True when this request asked for the report.
*
* @return bool
*/
function vigdbg_requested() {
if ( empty( $_GET['vigdbg'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only diagnostic, gated by the token below.
return false;
}
$given = sanitize_text_field( wp_unslash( $_GET['vigdbg'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Same read-only diagnostic.
return hash_equals( VIGDBG_TOKEN, $given );
}
/**
* Print the report and stop.
*
* Hooked at template_redirect priority 0, one step before Vigilant's own
* redirect (priority 1). Reaching this point proves the request got as far as
* template_redirect, so a missing redirect is a decision and not a page served
* by something earlier.
*/
function vigdbg_report() {
if ( ! vigdbg_requested() ) {
return;
}
$server = array();
foreach ( array( 'HTTPS', 'SERVER_PORT', 'REQUEST_SCHEME', 'HTTP_HOST', 'SERVER_NAME', 'HTTP_X_FORWARDED_PROTO', 'HTTP_X_FORWARDED_SSL', 'HTTP_X_FORWARDED_PORT', 'HTTP_CF_VISITOR', 'HTTP_FRONT_END_HTTPS', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR', 'SERVER_SOFTWARE' ) as $key ) {
$server[ $key ] = isset( $_SERVER[ $key ] ) ? sanitize_text_field( wp_unslash( $_SERVER[ $key ] ) ) : '(not set)';
}
$options = get_option( 'vigilante_options', array() );
$headers = isset( $options['security_headers'] ) ? $options['security_headers'] : array();
$modules = isset( $options['modules'] ) ? $options['modules'] : array();
$lines = array();
$lines[] = '=== Vigilant HTTPS redirect diagnostic ===';
$lines[] = '';
$lines[] = '-- What PHP thinks of this request --';
$lines[] = 'is_ssl() : ' . ( is_ssl() ? 'TRUE' : 'false' );
foreach ( $server as $key => $value ) {
$lines[] = str_pad( $key, 26 ) . ': ' . $value;
}
$lines[] = '';
$lines[] = '-- What the site declares --';
$lines[] = 'home option : ' . get_option( 'home' );
$lines[] = 'siteurl option : ' . get_option( 'siteurl' );
$lines[] = 'WP_HOME constant : ' . ( defined( 'WP_HOME' ) ? WP_HOME : '(not defined)' );
$lines[] = 'WP_SITEURL constant : ' . ( defined( 'WP_SITEURL' ) ? WP_SITEURL : '(not defined)' );
$lines[] = 'FORCE_SSL_ADMIN : ' . ( defined( 'FORCE_SSL_ADMIN' ) ? ( FORCE_SSL_ADMIN ? 'true' : 'false' ) : '(not defined)' );
$lines[] = '';
$lines[] = '-- Vigilant settings that govern the redirect --';
$lines[] = 'security_headers.enabled : ' . ( ! empty( $headers['enabled'] ) ? 'on' : 'OFF' );
$lines[] = 'redirect_http_to_https : ' . ( ! empty( $headers['redirect_http_to_https'] ) ? 'on' : 'OFF' );
$lines[] = 'modules.security_headers : ' . ( isset( $modules['security_headers'] ) ? ( $modules['security_headers'] ? 'on' : 'OFF' ) : '(key absent)' );
$lines[] = 'enforcer hook registered : ' . ( has_action( 'template_redirect' ) ? 'template_redirect has ' . count( $GLOBALS['wp_filter']['template_redirect']->callbacks ) . ' priority levels' : 'no callbacks' );
$lines[] = '';
$lines[] = '-- Anything serving pages before WordPress --';
$lines[] = 'WP_CACHE : ' . ( defined( 'WP_CACHE' ) ? ( WP_CACHE ? 'true' : 'false' ) : '(not defined)' );
$lines[] = 'advanced-cache.php : ' . ( file_exists( WP_CONTENT_DIR . '/advanced-cache.php' ) ? 'present' : 'absent' );
$lines[] = 'object-cache.php : ' . ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ? 'present' : 'absent' );
$lines[] = '';
$lines[] = '-- Reading --';
$lines[] = 'If is_ssl() says TRUE on this plain HTTP request, that is the answer: your host';
$lines[] = 'terminates TLS in front of PHP and tells WordPress every request is already';
$lines[] = 'HTTPS, so neither WordPress nor Vigilant ever sees an HTTP request to redirect.';
$lines[] = '';
$lines[] = 'Delete this file from wp-content/mu-plugins/ when you are done.';
nocache_headers();
header( 'Content-Type: text/plain; charset=utf-8' );
echo esc_html( implode( "\n", $lines ) );
exit;
}
add_action( 'template_redirect', 'vigdbg_report', 0 );Paste the answer here. If the first line says is_ssl() TRUE, that is the whole story. Delete the file from FTP afterwards.
If it does confirm it, the redirect has to be done by the server, because PHP cannot see the difference. The safest route is your hosting panel: most have a “Force HTTPS” switch that does it at the server level. Failing that, this at the very top of your .htaccess, above the WordPress block:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>Test it in a private window and keep your FTP client open while you do. If your proxy does not send X-Forwarded-Proto at all, that rule can loop, and you want to be able to undo it.
And thanks for the report, because there is something for me to fix here too: when that checkbox is ticked and the server never lets PHP see an HTTP request, Vigilant should say so on screen instead of leaving you with a checkbox that looks enabled and a score line that says 0/1.
Fernando
Hi @akgt
Any news?
Since there hasn’t been a response for several days, I have to assume the issue has been addressed, so I’m marking the thread as resolved.
You must be logged in to reply to this topic.