When you move sites behind a load balancer, reverse proxy, or CDN, you’ll often terminate SSL before the request reaches PHP. The browser connects over HTTPS, but by the time the request hits Apache/PHP-FPM, it might look like plain HTTP.
That’s when things get weird:
- Apps think they’re on
http://and generate insecure URLs - “Force HTTPS” logic stops working
- Secure cookies don’t get set correctly
- Frameworks start complaining about “Insecure request” or “unexpected scheme”
Under the hood, most PHP applications decide “am I on HTTPS?” by inspecting a couple of $_SERVER variables. If we understand those, we can safely “fool” PHP into behaving as if the request is HTTPS—as long as the original client really did connect over HTTPS.
Let’s walk through how this works and the right way to configure it.
How PHP Detects HTTPS
This is the key: PHP itself doesn’t run TLS. The web server or proxy does that. PHP just looks at environment variables the web server passes in.
Typical detection logic in PHP looks like this:
|
1 2 3 4 |
$is_https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443); |
So the main signals are:
$_SERVER['HTTPS']– usuallyonfor HTTPS, empty orofffor HTTP$_SERVER['SERVER_PORT']– often443for HTTPS,80for HTTP
Frameworks and CMSs also sometimes check:
$_SERVER['HTTP_X_FORWARDED_PROTO'](when behind a proxy)$_SERVER['REQUEST_SCHEME'](set by some servers)
If you can control these values, you can control what PHP thinks is going on.
The Wrong Approach: Faking It in PHP
You can directly override the variables at the top of your script:
|
1 2 3 4 |
// Don't do this in production unless you REALLY know what you're doing $_SERVER['HTTPS'] = 'on'; $_SERVER['SERVER_PORT'] = 443; |
This will usually convince any “am I HTTPS?” checks that they are secure.
The problem: PHP has no idea whether the original client came in over HTTPS or not. If you turn this on unconditionally, your app will assume every request is encrypted. That can:
- Mark cookies as
Securewhen they’re actually being sent over HTTP - Skip redirects intended to force HTTPS
- Confuse logging / debugging tools
This might be acceptable on a dev box or in a very controlled environment, but it’s not what you want as a general solution.
The Right Approach: Let the Web Server Tell the Truth
The correct pattern is:
Only tell PHP “this is HTTPS” if the front-end connection is HTTPS.
When you’re behind a proxy or load balancer, that front-end usually passes its own signal (like X-Forwarded-Proto) to the backend. We then translate that into HTTPS=on before PHP runs.
Apache / .htaccess Example
If Apache is sitting behind a load balancer that sets X-Forwarded-Proto: https, you can do this in your vhost or .htaccess:
|
1 2 |
SetEnvIfNoCase X-Forwarded-Proto "https" HTTPS=on |
What this does:
- Looks at the incoming
X-Forwarded-Protoheader - If it’s “https”, it sets the environment variable
HTTPS=on - PHP then sees
$_SERVER['HTTPS'] = 'on'
Many apps will immediately start generating correct https:// URLs after this change.
You may also want to ensure that $SERVER_PORT looks right (some apps check it):
|
1 2 3 |
SetEnvIfNoCase X-Forwarded-Proto "https" HTTPS=on SetEnvIfNoCase X-Forwarded-Proto "https" X_FORWARDED_HTTPS=on |
Usually SERVER_PORT is set by Apache to whatever port it’s listening on, so you don’t always need to fake it. For most apps, HTTPS=on is enough.
nginx + PHP-FPM Example
In nginx, you control what goes into $_SERVER using fastcgi_param. Here’s a typical snippet in the PHP location block:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/run/php/php-fpm.sock; # If nginx itself is doing HTTPS: # fastcgi_param HTTPS $https; # If you're behind a proxy that sets X-Forwarded-Proto: if ($http_x_forwarded_proto = "https") { set $rp_https on; } fastcgi_param HTTPS $rp_https; } |
If nginx is directly terminating TLS, you can often just do:
|
1 2 |
fastcgi_param HTTPS $https; |
Where $https will be on for HTTPS requests and an empty string otherwise.
Special Case: Plesk, Proxies, and Panels
On panels like Plesk/Obsidian and similar hosting environments, you might have multiple layers:
Browser → CDN → Load Balancer → Apache/nginx (Plesk) → PHP-FPM
Common gotchas in this setup:
- CDN terminates HTTPS and forwards as HTTP
- Plesk’s Apache/nginx doesn’t see the real scheme
- PHP thinks everything is plain HTTP
The fix is conceptually the same:
- Confirm what header your front-end is sending (
X-Forwarded-Proto,X-Forwarded-Scheme, etc.). - Configure Apache/nginx at the panel level to set
HTTPS=onwhen that header ishttps. - Test from a real HTTPS request, not just
curlto the backend.
Once that’s done correctly, apps like WordPress, Laravel, Symfony, etc. usually “just work” without hacks.
Detecting HTTPS Robustly in Your Own PHP Code
If you’re writing your own app or library, use a slightly more complete detection function that understands proxies:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
function is_https_request(): bool { if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') { return true; } if (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) { return true; } // Behind proxies / load balancers if ( isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https' ) { return true; } if ( isset($_SERVER['HTTP_X_FORWARDED_SSL']) && strtolower($_SERVER['HTTP_X_FORWARDED_SSL']) === 'on' ) { return true; } return false; } |
This won’t fix a broken server config by itself, but it will respect correctly-passed headers when your infrastructure is set up to send them.
When Is It Safe to “Fool” PHP?
It’s reasonable to spoof HTTPS only when you’re absolutely certain of one of these:
- You’re in a local/dev environment and don’t care about real TLS
- You’ve already validated (in the web server) that the original connection was HTTPS, and you’re just translating that fact to PHP
If you blindly set $_SERVER['HTTPS'] = 'on' in production, you’re asking for security and debugging headaches.
Summary
- PHP doesn’t do TLS; it trusts
$_SERVERto know if a request is HTTPS. - Most code checks
$_SERVER['HTTPS']and sometimes$_SERVER['SERVER_PORT']. - You can “fool” PHP by setting those values manually, but that’s usually the wrong tool.
- The right way is to configure your web server (Apache/nginx/Plesk/etc.) to set
HTTPS=ononly when the client actually connected over HTTPS (often usingX-Forwarded-Proto). - For your own code, prefer a helper that understands both
HTTPSand proxy headers.




