WordPress Hid the Application Passwords Box. Three Checks Found Why.
The Application Passwords box was not on my profile page. It had been there the week before.
Application Passwords missing from Users → Profile is not a bug. WordPress hides that panel on purpose, and there are only three reasons it does. The site is not being seen as HTTPS. Something called a filter and turned the feature off. Or you are on a WordPress older than 5.6, which does not have the feature at all. You can tell which one in about two minutes without editing a single file, and the first check takes five seconds.
KEY FACTS
Application Passwords shipped in WordPress 5.6, in December 2020. Before that version there is no panel to find.
Core decides availability in one line: is_ssl() || 'local' === wp_get_environment_type().
is_ssl() reads the PHP request, not the address bar. Behind a CDN or a reverse proxy it can return false on a site that is plainly served over HTTPS.
Two filters can switch the feature off: wp_is_application_passwords_available for the whole site, and wp_is_application_passwords_available_for_user for one person.
The fastest test is unauthenticated and takes one request: fetch /wp-json/ and look at the authentication key.
Check one: ask the REST API whether the feature exists
Do this before you look at anything else. The REST API root advertises which authentication methods the site offers, and it will answer an anonymous request. No login, no password, no plugin.
curl -s 'https://example.com/wp-json/' \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["authentication"])'
On a site where the feature is available you get the authorization endpoint back:
{"application-passwords": {"endpoints": {"authorization":
"https://example.com/wp-admin/authorize-application.php"}}}
On a site where it is not, you get an empty object:
{}
That one line tells you which half of the problem you have. An empty object means WordPress has the feature off, and checks two and three apply. A populated object means the feature is on and your problem is elsewhere. Wrong profile screen, maybe. Or a role or plugin hiding the panel for your user in particular while leaving it on for everyone else.
I run this against my own site whenever automation starts failing, because it separates “WordPress turned it off” from “my credentials are bad” without touching credentials at all.
Check two: is WordPress seeing HTTPS at all
Here is the whole rule, from core:
function wp_is_application_passwords_supported() {
return is_ssl() || 'local' === wp_get_environment_type();
}
function wp_is_application_passwords_available() {
return apply_filters(
'wp_is_application_passwords_available',
wp_is_application_passwords_supported()
);
}
So the feature is on when the request is HTTPS, or when the environment is local. Nothing else. And is_ssl() is narrower than people expect:
function is_ssl() {
if ( isset( $_SERVER['HTTPS'] ) ) {
if ( 'on' === strtolower( $_SERVER['HTTPS'] ) ) {
return true;
}
if ( '1' === (string) $_SERVER['HTTPS'] ) {
return true;
}
} elseif ( isset( $_SERVER['SERVER_PORT'] ) && ( '443' === (string) $_SERVER['SERVER_PORT'] ) ) {
return true;
}
return false;
}
Read what that function is allowed to look at. Two server variables, both set by whatever handed the request to PHP. It never sees the URL in your browser.
That matters the moment anything sits in front of your web server. A CDN, a load balancer, a reverse proxy on your own hardware. The browser speaks HTTPS to that thing, that thing terminates the certificate, and then it speaks plain HTTP to PHP on port 80. PHP is telling the truth when it says the request was not encrypted. It just is not the truth you care about.
The proxy usually passes the original scheme along in a header. Teach WordPress to read it, near the top of wp-config.php, above the line that loads wp-settings.php:
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
&& 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
One caveat worth taking seriously. That header is client supplied. Only trust it when you know a proxy you control is the only thing that can reach your origin, because otherwise anyone can claim their plain HTTP request was secure. If your origin is reachable directly on the open internet, lock that down first.
If you want to watch the values rather than guess at them, drop this in temporarily and load any admin page:
add_action( 'admin_notices', function () {
printf(
'<div class="notice notice-info"><p>is_ssl: %s | env: %s | available: %s</p></div>',
is_ssl() ? 'true' : 'false',
esc_html( wp_get_environment_type() ),
wp_is_application_passwords_available() ? 'true' : 'false'
);
} );
Three booleans, and they tell you everything. If is_ssl is false, it is the proxy. If is_ssl is true and available is false, go to check three.
Check three: something turned it off deliberately
That gap between supported and available is the filter. Somebody hooked wp_is_application_passwords_available and returned false.
Usually it is a security plugin. Several ship with Application Passwords disabled by default, on the reasonable view that most sites never use the REST API and an extra credential is an extra thing to steal. Look for a setting named after the REST API or XML-RPC rather than one named after application passwords, because that is where the switch tends to live. Deactivating your security plugin for sixty seconds and re-running check one will settle it faster than reading its settings pages.
It can also be a single line in a theme’s functions file or a must use plugin that a previous developer left behind. If you have filesystem access, search for the filter name across wp-content:
grep -rn 'wp_is_application_passwords_available' wp-content/
And if it is off for you but on for a colleague, it is the per user filter instead. This logs a line for every check, which is enough to find the culprit in the stack trace or the plugin name:
add_filter( 'wp_is_application_passwords_available_for_user', function ( $available, $user ) {
error_log( sprintf( 'app passwords for %s: %s',
$user->user_login, $available ? 'yes' : 'no' ) );
return $available;
}, 999, 2 );
Turning it back on
Once you know which check failed, the fix follows from it. A false is_ssl gets the wp-config change above, because that fixes the real problem and a dozen other things that quietly depend on scheme detection. A plugin gets a setting change.
The blunt instrument is one line:
add_filter( 'wp_is_application_passwords_available', '__return_true' );
That is what I keep on my own site, in a code snippet rather than a theme file so it survives theme changes. I am comfortable with it because I checked first that the site is actually served over HTTPS end to end. Forcing that filter true on a site that really is answering over plain HTTP does not make anything secure. It hands your admin credentials to the network in base64, which is what the SSL requirement exists to prevent. Fix the scheme, then force the filter, in that order.
What breaks next
Getting the box back is step one. The password it generates still has to survive the trip to PHP, and on shared hosting it often does not, because Apache strips the Authorization header before WordPress ever sees it. Symptom: a perfectly good application password returns 401 on every request, including from curl on your own machine. That one now has its own writeup: the Authorization header your server threw away.
Then there is the class of plugin that reads the current user too early to notice you authenticated at all. That one cost me an afternoon and is written up in why Site Kit said I was missing scopes I had granted.
Both of those are downstream. Neither is worth debugging until check one comes back with an authorization endpoint in it.
FAQ
Why does WordPress require HTTPS for application passwords?
Application passwords authenticate with HTTP Basic Auth, which sends your username and password base64 encoded on every single request. Base64 is not encryption. Over plain HTTP, anything on the path can read the credential and replay it. The HTTPS check is core refusing to hand you a footgun.
My site is HTTPS in the browser but the panel is still gone. Why?
Almost always a CDN or reverse proxy terminating TLS in front of your origin, so PHP sees a plain HTTP request. Check the is_ssl value with the admin notice snippet above rather than trusting the address bar.
Can I use application passwords on a local development site?
Yes, without HTTPS. Core treats a local environment as supported. Set WP_ENVIRONMENT_TYPE to local in wp-config and the panel appears.
Which WordPress version added application passwords?
Version 5.6. The availability functions arrived alongside it, and wp_is_application_passwords_supported() was split out in 5.9 so core could tell you why the feature was unavailable instead of just hiding it.
Is forcing the filter true a security risk?
On a genuinely HTTPS site behind a proxy, no. You are correcting a false negative. On a site served over plain HTTP, yes, and a serious one. The filter does not encrypt anything, it only stops core from complaining.
Do I need to disable my security plugin permanently?
No. Turn it off just long enough to re-run the REST root check, confirm it was the cause, then turn it back on and find the specific setting. If the plugin offers no way to allow application passwords while keeping the rest of its protection, that is worth knowing before you build automation on top of it.
The short version
Fetch /wp-json/ and read the authentication key. Empty means core has the feature off, and it is either scheme detection or a filter. Populated means the feature is fine and your problem is somewhere else entirely.
I hit this while wiring up the automation that now writes and publishes on this site, which I documented in how I let Claude run my website. The same proxy behaviour turns up on self-hosted setups too, which is why I mention scheme detection in the notes on building the home server. Two minutes of checking beats an afternoon of regenerating credentials that were never the problem.
Stay in the loop.
No spam, just signal.
I send one email when I publish something worth reading. Stock deep dives, new projects, tech breakdowns, and what I'm learning from building in public.
Free forever. Unsubscribe anytime. I respect your inbox.